tldraw
Version:
A tiny little drawing editor.
177 lines (176 loc) • 6.6 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var A11y_exports = {};
__export(A11y_exports, {
DefaultA11yAnnouncer: () => DefaultA11yAnnouncer,
SkipToMainContent: () => SkipToMainContent,
generateShapeAnnouncementMessage: () => generateShapeAnnouncementMessage,
useSelectedShapesAnnouncer: () => useSelectedShapesAnnouncer
});
module.exports = __toCommonJS(A11y_exports);
var import_jsx_runtime = require("react/jsx-runtime");
var import_editor = require("@tldraw/editor");
var import_react = require("react");
var import_a11y = require("../context/a11y");
var import_useTranslation = require("../hooks/useTranslation/useTranslation");
var import_TldrawUiButton = require("./primitives/Button/TldrawUiButton");
function SkipToMainContent() {
const editor = (0, import_editor.useEditor)();
const msg = (0, import_useTranslation.useTranslation)();
const button = (0, import_react.useRef)(null);
const handleNavigateToFirstShape = (0, import_react.useCallback)(
(e) => {
editor.markEventAsHandled(e);
button.current?.blur();
const shapes = editor.getCurrentPageShapesInReadingOrder();
if (!shapes.length) return;
editor.setSelectedShapes([shapes[0].id]);
editor.zoomToSelectionIfOffscreen(256, {
animation: {
duration: editor.options.animationMediumMs
},
inset: 0
});
editor.timers.setTimeout(() => editor.getContainer().focus(), 100);
},
[editor]
);
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_TldrawUiButton.TldrawUiButton,
{
ref: button,
type: "low",
tabIndex: 0,
className: "tl-skip-to-main-content",
onClick: handleNavigateToFirstShape,
children: msg("a11y.skip-to-main-content")
}
);
}
const DefaultA11yAnnouncer = (0, import_react.memo)(function TldrawUiA11yAnnouncer() {
const a11y = (0, import_a11y.useA11y)();
const translation = (0, import_useTranslation.useTranslation)();
const msg = (0, import_editor.useValue)("a11y-msg", () => a11y.currentMsg.get(), []);
useA11yDebug(msg.msg);
useSelectedShapesAnnouncer();
return msg.msg && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"div",
{
"aria-label": translation("a11y.status"),
"aria-live": msg.priority || "assertive",
role: "status",
"aria-hidden": "false",
style: {
position: "absolute",
top: "-10000px",
left: "-10000px"
},
children: msg.msg
}
);
});
function generateShapeAnnouncementMessage(args) {
const { editor, selectedShapeIds, msg } = args;
let a11yLive = "";
const numShapes = selectedShapeIds.length;
if (numShapes > 1) {
a11yLive = msg("a11y.multiple-shapes").replace("{num}", numShapes.toString());
} else if (numShapes === 1) {
const shapeId = selectedShapeIds[0];
const shape = editor.getShape(shapeId);
if (!shape) return "";
const shapeUtil = editor.getShapeUtil(shape.type);
const isMedia = ["image", "video"].includes(shape.type);
let shapeType = "";
if (shape.type === "geo") {
shapeType = msg(`geo-style.${shape.props.geo}`);
} else if (isMedia) {
shapeType = msg(`a11y.shape-${shape.type}`);
} else {
shapeType = msg(`tool.${shape.type}`);
}
const readingOrderShapes = editor.getCurrentPageShapesInReadingOrder();
const currentShapeIndex = (readingOrderShapes.findIndex((s) => s.id === shapeId) + 1).toString();
const totalShapes = readingOrderShapes.length.toString();
const shapeIndex = msg("a11y.shape-index").replace("{num}", currentShapeIndex).replace("{total}", totalShapes);
const describingText = shapeUtil.getAriaDescriptor(shape) || shapeUtil.getText(shape) || "";
a11yLive = (describingText ? `${describingText}, ` : "") + `${shapeType}. ${shapeIndex}`;
}
return a11yLive;
}
const useSelectedShapesAnnouncer = () => {
const editor = (0, import_editor.useMaybeEditor)();
const a11y = (0, import_a11y.useA11y)();
const msg = (0, import_useTranslation.useTranslation)();
const rPrevSelectedShapeIds = (0, import_react.useRef)([]);
(0, import_editor.useReactor)(
"announce selection",
() => {
if (!editor) return;
const isInSelecting = editor.isIn("select.idle");
if (isInSelecting) {
const selectedShapeIds = editor.getSelectedShapeIds();
if (selectedShapeIds !== rPrevSelectedShapeIds.current) {
rPrevSelectedShapeIds.current = selectedShapeIds;
(0, import_editor.unsafe__withoutCapture)(() => {
const a11yLive = generateShapeAnnouncementMessage({
editor,
selectedShapeIds,
msg
});
if (a11yLive) {
a11y.announce({ msg: a11yLive });
}
});
}
}
},
[editor, a11y, msg]
);
};
const useA11yDebug = (msg) => {
const container = (0, import_editor.useContainer)();
(0, import_react.useEffect)(() => {
if (import_editor.debugFlags.a11y.get()) {
const log = (msg2) => {
console.debug(
`%ca11y%c: ${msg2}`,
`color: white; background: #40C057; padding: 2px;border-radius: 3px;`,
"font-weight: normal"
);
};
const handleKeyUp = (e) => {
const el = document.activeElement;
if (e.key === "Tab" && el && el !== document.body && !el.classList.contains("tl-container")) {
const label = el.getAttribute("aria-label") || el.getAttribute("title") || el.textContent;
if (label) {
log(label);
}
}
};
if (msg) {
log(msg);
}
document.addEventListener("keyup", handleKeyUp);
return () => document.removeEventListener("keyup", handleKeyUp);
}
return void 0;
}, [container, msg]);
};
//# sourceMappingURL=A11y.js.map