efficient-shell 4.131.0 → 4.134.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -12
- package/dist/{Browser-PSEMJFZE.js → Browser-O3QZGUF2.js} +11 -11
- package/dist/{Browser-PSEMJFZE.js.map → Browser-O3QZGUF2.js.map} +1 -1
- package/dist/{Calculator-ZXJPXG7F.js → Calculator-L6N5X3KU.js} +9 -9
- package/dist/{Calculator-ZXJPXG7F.js.map → Calculator-L6N5X3KU.js.map} +1 -1
- package/dist/ConfirmDialog-LYSSZSUD.js +4 -0
- package/dist/{ConfirmDialog-MTC7QMUJ.js.map → ConfirmDialog-LYSSZSUD.js.map} +1 -1
- package/dist/{CurrencyConverter-WSJ2BBSX.js → CurrencyConverter-64MKXB3D.js} +9 -9
- package/dist/{CurrencyConverter-WSJ2BBSX.js.map → CurrencyConverter-64MKXB3D.js.map} +1 -1
- package/dist/{Documents-PGMZR2MA.js → Documents-5WXXYCLI.js} +11 -11
- package/dist/{Documents-PGMZR2MA.js.map → Documents-5WXXYCLI.js.map} +1 -1
- package/dist/{Files-2LKCJGYW.js → Files-AUURUUDN.js} +17 -17
- package/dist/{Files-2LKCJGYW.js.map → Files-AUURUUDN.js.map} +1 -1
- package/dist/{Notepad-KW6WVABH.js → Notepad-GPXJVPB7.js} +12 -12
- package/dist/{Notepad-KW6WVABH.js.map → Notepad-GPXJVPB7.js.map} +1 -1
- package/dist/PdfViewer-HKO6P4SR.js +5 -0
- package/dist/{PdfViewer-M2YGZIYX.js.map → PdfViewer-HKO6P4SR.js.map} +1 -1
- package/dist/{PomodoroTimer-DG3LI5CX.js → PomodoroTimer-4TQBWLP2.js} +11 -11
- package/dist/{PomodoroTimer-DG3LI5CX.js.map → PomodoroTimer-4TQBWLP2.js.map} +1 -1
- package/dist/{Preview-E7EJJNXN.js → Preview-FOZYPKM2.js} +14 -14
- package/dist/{Preview-E7EJJNXN.js.map → Preview-FOZYPKM2.js.map} +1 -1
- package/dist/{Spreadsheet-IDUODRZU.js → Spreadsheet-3C66BBBF.js} +12 -12
- package/dist/{Spreadsheet-IDUODRZU.js.map → Spreadsheet-3C66BBBF.js.map} +1 -1
- package/dist/{Stock-YMPOMUAW.js → Stock-IF4IHYLU.js} +9 -9
- package/dist/{Stock-YMPOMUAW.js.map → Stock-IF4IHYLU.js.map} +1 -1
- package/dist/{Weather-NZO3CWLH.js → Weather-725PFR7T.js} +9 -9
- package/dist/{Weather-NZO3CWLH.js.map → Weather-725PFR7T.js.map} +1 -1
- package/dist/{WorldClock-HUC6D3AD.js → WorldClock-UU4NZIQ7.js} +9 -9
- package/dist/{WorldClock-HUC6D3AD.js.map → WorldClock-UU4NZIQ7.js.map} +1 -1
- package/dist/apps/index.d.ts +2 -2
- package/dist/apps/index.js +28 -28
- package/dist/apps/index.js.map +1 -1
- package/dist/brand.css +1 -1
- package/dist/{chunk-TQURTLK2.js → chunk-2TPTHAKI.js} +5 -5
- package/dist/{chunk-TQURTLK2.js.map → chunk-2TPTHAKI.js.map} +1 -1
- package/dist/{chunk-EENVXIVU.js → chunk-4P44JXDS.js} +3 -3
- package/dist/{chunk-EENVXIVU.js.map → chunk-4P44JXDS.js.map} +1 -1
- package/dist/{chunk-CT3HVZRZ.js → chunk-5O42JXQE.js} +2 -2
- package/dist/chunk-5O42JXQE.js.map +1 -0
- package/dist/{chunk-7IN7YZAL.js → chunk-72CQWN7O.js} +3 -3
- package/dist/chunk-72CQWN7O.js.map +1 -0
- package/dist/{chunk-I464GYJJ.js → chunk-BE7FZOFU.js} +2 -2
- package/dist/chunk-BE7FZOFU.js.map +1 -0
- package/dist/{chunk-3L463T5H.js → chunk-DVZWYWSD.js} +2 -2
- package/dist/chunk-DVZWYWSD.js.map +1 -0
- package/dist/{chunk-FONEQRBH.js → chunk-ET6ID4LP.js} +3 -3
- package/dist/{chunk-FONEQRBH.js.map → chunk-ET6ID4LP.js.map} +1 -1
- package/dist/{chunk-CGXMWHQW.js → chunk-GIGV2ZRW.js} +3 -3
- package/dist/{chunk-CGXMWHQW.js.map → chunk-GIGV2ZRW.js.map} +1 -1
- package/dist/{chunk-2GZQ352J.js → chunk-IULP5BCD.js} +5 -5
- package/dist/chunk-IULP5BCD.js.map +1 -0
- package/dist/{chunk-BJ34QOR3.js → chunk-OLJDJYSU.js} +287 -146
- package/dist/chunk-OLJDJYSU.js.map +1 -0
- package/dist/{chunk-7MA5XBWD.js → chunk-PIGDNN5F.js} +5 -5
- package/dist/{chunk-7MA5XBWD.js.map → chunk-PIGDNN5F.js.map} +1 -1
- package/dist/chunk-Q2MJVSJT.js +7 -0
- package/dist/{chunk-UHPWZ2F4.js.map → chunk-Q2MJVSJT.js.map} +1 -1
- package/dist/{chunk-ZKHD4J73.js → chunk-Q3LT745N.js} +10 -10
- package/dist/chunk-Q3LT745N.js.map +1 -0
- package/dist/{chunk-CLGNTGC5.js → chunk-QAGQRNKI.js} +3 -3
- package/dist/{chunk-CLGNTGC5.js.map → chunk-QAGQRNKI.js.map} +1 -1
- package/dist/{chunk-E3TWIN7Z.js → chunk-QRL64J5F.js} +3 -3
- package/dist/{chunk-E3TWIN7Z.js.map → chunk-QRL64J5F.js.map} +1 -1
- package/dist/{chunk-TFXJRHJV.js → chunk-QSKCQUZE.js} +3 -3
- package/dist/{chunk-TFXJRHJV.js.map → chunk-QSKCQUZE.js.map} +1 -1
- package/dist/{chunk-MQPX6ERJ.js → chunk-R4OQIQYX.js} +4 -4
- package/dist/{chunk-MQPX6ERJ.js.map → chunk-R4OQIQYX.js.map} +1 -1
- package/dist/{chunk-LQTVTOSY.js → chunk-RT6DSB5Y.js} +45 -19
- package/dist/chunk-RT6DSB5Y.js.map +1 -0
- package/dist/{chunk-VWZIGZFF.js → chunk-SHQM5CK3.js} +4 -4
- package/dist/{chunk-VWZIGZFF.js.map → chunk-SHQM5CK3.js.map} +1 -1
- package/dist/{chunk-KWEX4ZAF.js → chunk-T4VQHMG4.js} +3 -3
- package/dist/{chunk-KWEX4ZAF.js.map → chunk-T4VQHMG4.js.map} +1 -1
- package/dist/{chunk-BFBWLCO6.js → chunk-TW7LPMVR.js} +2 -2
- package/dist/chunk-TW7LPMVR.js.map +1 -0
- package/dist/{chunk-XH3ELPQZ.js → chunk-W622KQTD.js} +3 -3
- package/dist/chunk-W622KQTD.js.map +1 -0
- package/dist/{chunk-SEWF4O3H.js → chunk-X4Q6G253.js} +3 -3
- package/dist/chunk-X4Q6G253.js.map +1 -0
- package/dist/file-intake/index.js +2 -2
- package/dist/{index-COwksV6G.d.ts → index-DZ7E7bCl.d.ts} +196 -4
- package/dist/index.d.ts +3 -3
- package/dist/index.js +35 -35
- package/dist/index.js.map +1 -1
- package/dist/map/index.js +1 -1
- package/dist/markdown/index.d.ts +4 -4
- package/dist/markdown/index.js.map +1 -1
- package/dist/markup/index.d.ts +2 -2
- package/dist/markup/index.js.map +1 -1
- package/dist/page-tabs/index.js +3 -3
- package/dist/shell.css +1 -1
- package/dist/styles.css +4 -4
- package/dist/themes.css +3 -3
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +12 -12
- package/dist/ui.css +291 -48
- package/package.json +1 -1
- package/dist/ConfirmDialog-MTC7QMUJ.js +0 -4
- package/dist/PdfViewer-M2YGZIYX.js +0 -5
- package/dist/chunk-2GZQ352J.js.map +0 -1
- package/dist/chunk-3L463T5H.js.map +0 -1
- package/dist/chunk-7IN7YZAL.js.map +0 -1
- package/dist/chunk-BFBWLCO6.js.map +0 -1
- package/dist/chunk-BJ34QOR3.js.map +0 -1
- package/dist/chunk-CT3HVZRZ.js.map +0 -1
- package/dist/chunk-I464GYJJ.js.map +0 -1
- package/dist/chunk-LQTVTOSY.js.map +0 -1
- package/dist/chunk-SEWF4O3H.js.map +0 -1
- package/dist/chunk-UHPWZ2F4.js +0 -7
- package/dist/chunk-XH3ELPQZ.js.map +0 -1
- package/dist/chunk-ZKHD4J73.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_SHELL_STRINGS, useShellStrings } from './chunk-
|
|
1
|
+
import { DEFAULT_SHELL_STRINGS, useShellStrings } from './chunk-72CQWN7O.js';
|
|
2
2
|
import { useRef, useState, useCallback } from 'react';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -141,5 +141,5 @@ function FileIntakeAlert({ rejections, className = "mt-2" }) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
export { FileIntakeAlert, acceptsFile, humanSize, useFileIntake };
|
|
144
|
-
//# sourceMappingURL=chunk-
|
|
145
|
-
//# sourceMappingURL=chunk-
|
|
144
|
+
//# sourceMappingURL=chunk-QAGQRNKI.js.map
|
|
145
|
+
//# sourceMappingURL=chunk-QAGQRNKI.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/forms/useFileIntake.tsx"],"names":[],"mappings":"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","file":"chunk-CLGNTGC5.js","sourcesContent":["/**\n * useFileIntake — the one way a file gets INTO a kit primitive.\n *\n * Every upload surface in the kit (`FilePicker`, `MediaUploadField`,\n * `MediaUploadGrid`, `BrandAssetEditor`, `BulkImportGrid`) takes files through\n * this hook, so the three gestures — the native file dialog, a drop onto the\n * zone, a paste into a composer — share ONE intake path and ONE set of checks.\n *\n * Before this each primitive owned its own hidden `<input type=file>`, its own\n * drag-over state and its own idea of what to check: `accept` reached the\n * native dialog only, so a file dropped onto the zone bypassed it entirely;\n * a multi-file drop onto a gallery kept the first file and lost the rest\n * without a word; and a rejected file was listed in plain red text that no\n * screen reader announced. The portals then copied the hidden-input block\n * nineteen more times. This hook is the fix at the narrowest layer that can\n * hold it (harness UI-15 / PAT-10).\n *\n * The hook validates and hands back `File[]`. It never uploads — transport\n * belongs to the consumer, for the reasons `FilePicker`'s header gives.\n *\n * Its rejection sentences and size units are the catalog's `files` section, so\n * a translated app says why a file was refused in the reader's language. The\n * catalog module imports nothing but React, which keeps this entry React-only.\n */\nimport {\n useCallback, useRef, useState,\n type ChangeEvent, type ClipboardEvent, type DragEvent, type ReactNode,\n} from 'react';\nimport { DEFAULT_SHELL_STRINGS, useShellStrings, type ShellStrings } from '../shell/strings';\n\nexport type FileRejectionReason = 'type' | 'size' | 'count';\n\nexport interface FileRejection {\n file: File;\n reason: FileRejectionReason;\n /** Human sentence naming the file and the rule it broke. */\n message: string;\n}\n\nexport interface FileIntakeLimits {\n /**\n * Native `accept` string — `image/*`, `.pdf,application/pdf`, … Enforced on\n * EVERY gesture, not only the dialog: a dropped or pasted file that fails it\n * is rejected with a reason. Empty or `*` accepts anything.\n */\n accept?: string;\n /** Rejected above this many bytes, with the reason shown. */\n maxSizeBytes?: number;\n /** Rejected once this many files are held in total (see `currentCount`). */\n maxFiles?: number;\n}\n\nexport interface FileIntakeOptions extends FileIntakeLimits {\n /** Receives the files that passed every check. Never called with an empty list. */\n onAccept: (files: File[]) => void;\n /** `false` takes the first candidate only — a single slot. Default `true`. */\n multiple?: boolean;\n /** Files the surface already holds, so `maxFiles` counts the total. Default 0. */\n currentCount?: number;\n /** Short kind hint for the type-rejection sentence, e.g. \"PNG · JPG\". */\n acceptHint?: ReactNode;\n disabled?: boolean;\n}\n\n/**\n * Does `file` satisfy a native `accept` string? Extension rules match the\n * name, `type/*` rules match the MIME prefix, anything else is an exact MIME.\n * A browser reports several media types for one `.ico` file and sometimes\n * none at all, which is why an extension rule exists at all.\n */\nexport function acceptsFile(file: File, accept?: string): boolean {\n const rules = (accept ?? '').split(',').map(rule => rule.trim().toLocaleLowerCase()).filter(Boolean);\n if (rules.length === 0 || rules.includes('*') || rules.includes('*/*')) return true;\n const fileName = file.name.toLocaleLowerCase();\n const fileType = file.type.toLocaleLowerCase();\n return rules.some(rule => {\n if (rule.startsWith('.')) return fileName.endsWith(rule);\n if (rule.endsWith('/*')) return fileType.startsWith(rule.slice(0, -1));\n return fileType === rule;\n });\n}\n\n/** `812 B`, `14 KB`, `6.2 MB` — the units, and their digits, from the catalog. */\nexport const humanSize = (bytes: number, strings: ShellStrings['files'] = DEFAULT_SHELL_STRINGS.files): string => {\n if (bytes < 1024) return strings.bytes(bytes);\n if (bytes < 1024 * 1024) return strings.kilobytes(Math.round(bytes / 1024));\n return strings.megabytes(bytes / (1024 * 1024));\n};\n\n/** Is this drag carrying files (as opposed to text, a URL, or an in-page reorder)? */\nexport const isFileDrag = (e: DragEvent): boolean =>\n Array.from(e.dataTransfer?.types ?? []).includes('Files');\n\n/** Files on a clipboard event, if the paste carried any. */\nexport function pastedFiles(e: ClipboardEvent): File[] {\n const data = e.clipboardData;\n if (!data) return [];\n if (data.files && data.files.length > 0) return Array.from(data.files);\n const out: File[] = [];\n for (const item of Array.from(data.items ?? [])) {\n if (item.kind === 'file') {\n const file = item.getAsFile();\n if (file) out.push(file);\n }\n }\n return out;\n}\n\nexport function useFileIntake({\n onAccept, accept, maxSizeBytes, maxFiles, multiple = true, currentCount = 0,\n acceptHint, disabled = false,\n}: FileIntakeOptions) {\n const strings = useShellStrings().files;\n const inputRef = useRef<HTMLInputElement>(null);\n const [dragOver, setDragOver] = useState(false);\n const [rejections, setRejections] = useState<FileRejection[]>([]);\n // A drag passes over every child of the zone, firing leave/enter pairs; a\n // depth counter is what keeps the highlight steady across them.\n const depthRef = useRef(0);\n\n const hintText = typeof acceptHint === 'string' ? acceptHint : '';\n\n /** Run every candidate through the checks; deliver the survivors. */\n const take = useCallback((incoming: FileList | File[] | null | undefined) => {\n if (disabled || !incoming) return;\n const candidates = Array.from(incoming);\n if (candidates.length === 0) return;\n const accepted: File[] = [];\n const rejected: FileRejection[] = [];\n for (const file of multiple ? candidates : candidates.slice(0, 1)) {\n if (!acceptsFile(file, accept)) {\n rejected.push({\n file, reason: 'type',\n message: strings.notAccepted(file.name, hintText),\n });\n continue;\n }\n if (maxSizeBytes != null && file.size > maxSizeBytes) {\n rejected.push({\n file, reason: 'size',\n message: strings.tooLarge(file.name, humanSize(file.size, strings), humanSize(maxSizeBytes, strings)),\n });\n continue;\n }\n if (multiple && maxFiles != null && currentCount + accepted.length >= maxFiles) {\n rejected.push({\n file, reason: 'count',\n message: strings.tooMany(file.name, maxFiles),\n });\n continue;\n }\n accepted.push(file);\n }\n setRejections(rejected);\n if (accepted.length) onAccept(accepted);\n // Reset the native input so re-picking the SAME file fires change again —\n // without this, removing a file and re-adding it appears to do nothing.\n if (inputRef.current) inputRef.current.value = '';\n }, [disabled, multiple, accept, hintText, maxSizeBytes, maxFiles, currentCount, onAccept, strings]);\n\n const open = useCallback(() => {\n if (!disabled) inputRef.current?.click();\n }, [disabled]);\n\n const clearRejections = useCallback(() => setRejections([]), []);\n\n /** Spread onto the hidden native input. It is never a tab stop and never the visible control. */\n const inputProps = {\n ref: inputRef,\n type: 'file' as const,\n accept: accept || undefined,\n multiple,\n disabled,\n tabIndex: -1,\n 'aria-hidden': true as const,\n className: 'hidden',\n onChange: (e: ChangeEvent<HTMLInputElement>) => take(e.target.files),\n };\n\n /** Spread onto the drop target. Only a drag carrying files lights it up. */\n const zoneProps = disabled\n ? {}\n : {\n onDragEnter: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n e.preventDefault();\n depthRef.current += 1;\n setDragOver(true);\n },\n onDragOver: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n e.preventDefault();\n if (!dragOver) setDragOver(true);\n },\n onDragLeave: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n depthRef.current = Math.max(0, depthRef.current - 1);\n if (depthRef.current === 0) setDragOver(false);\n },\n onDrop: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n e.preventDefault();\n depthRef.current = 0;\n setDragOver(false);\n take(e.dataTransfer.files);\n },\n };\n\n /** Spread onto a composer's text area: a paste that carries files is taken, one of text is left alone. */\n const pasteProps = disabled\n ? {}\n : {\n onPaste: (e: ClipboardEvent) => {\n const files = pastedFiles(e);\n if (files.length === 0) return;\n e.preventDefault();\n take(files);\n },\n };\n\n return { inputRef, inputProps, zoneProps, pasteProps, open, take, dragOver, rejections, clearRejections };\n}\n\n/**\n * The rejection list, announced. `role=\"alert\"` is what tells a screen-reader\n * user why the drop did nothing; the sighted user sees the same sentences.\n * Renders nothing while there is nothing to say.\n */\nexport function FileIntakeAlert({ rejections, className = 'mt-2' }: { rejections: FileRejection[]; className?: string }) {\n if (rejections.length === 0) return null;\n return (\n <ul role=\"alert\" className={`${className} flex flex-col gap-0.5`}>\n {rejections.map(({ file, message }, i) => (\n <li key={`${file.name}-${i}`} className=\"text-xs text-red-600\">{message}</li>\n ))}\n </ul>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/forms/useFileIntake.tsx"],"names":[],"mappings":"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","file":"chunk-QAGQRNKI.js","sourcesContent":["/**\n * useFileIntake — the one way a file gets INTO a kit primitive.\n *\n * Every upload surface in the kit (`FilePicker`, `MediaUploadField`,\n * `MediaUploadGrid`, `BrandAssetEditor`, `BulkImportGrid`) takes files through\n * this hook, so the three gestures — the native file dialog, a drop onto the\n * zone, a paste into a composer — share ONE intake path and ONE set of checks.\n *\n * Before this each primitive owned its own hidden `<input type=file>`, its own\n * drag-over state and its own idea of what to check: `accept` reached the\n * native dialog only, so a file dropped onto the zone bypassed it entirely;\n * a multi-file drop onto a gallery kept the first file and lost the rest\n * without a word; and a rejected file was listed in plain red text that no\n * screen reader announced. The portals then copied the hidden-input block\n * nineteen more times. This hook is the fix at the narrowest layer that can\n * hold it (harness UI-15 / PAT-10).\n *\n * The hook validates and hands back `File[]`. It never uploads — transport\n * belongs to the consumer, for the reasons `FilePicker`'s header gives.\n *\n * Its rejection sentences and size units are the catalog's `files` section, so\n * a translated app says why a file was refused in the reader's language. The\n * catalog module imports nothing but React, which keeps this entry React-only.\n */\nimport {\n useCallback, useRef, useState,\n type ChangeEvent, type ClipboardEvent, type DragEvent, type ReactNode,\n} from 'react';\nimport { DEFAULT_SHELL_STRINGS, useShellStrings, type ShellStrings } from '../shell/strings';\n\nexport type FileRejectionReason = 'type' | 'size' | 'count';\n\nexport interface FileRejection {\n file: File;\n reason: FileRejectionReason;\n /** Human sentence naming the file and the rule it broke. */\n message: string;\n}\n\nexport interface FileIntakeLimits {\n /**\n * Native `accept` string — `image/*`, `.pdf,application/pdf`, … Enforced on\n * EVERY gesture, not only the dialog: a dropped or pasted file that fails it\n * is rejected with a reason. Empty or `*` accepts anything.\n */\n accept?: string;\n /** Rejected above this many bytes, with the reason shown. */\n maxSizeBytes?: number;\n /** Rejected once this many files are held in total (see `currentCount`). */\n maxFiles?: number;\n}\n\nexport interface FileIntakeOptions extends FileIntakeLimits {\n /** Receives the files that passed every check. Never called with an empty list. */\n onAccept: (files: File[]) => void;\n /** `false` takes the first candidate only — a single slot. Default `true`. */\n multiple?: boolean;\n /** Files the surface already holds, so `maxFiles` counts the total. Default 0. */\n currentCount?: number;\n /** Short kind hint for the type-rejection sentence, e.g. \"PNG · JPG\". */\n acceptHint?: ReactNode;\n disabled?: boolean;\n}\n\n/**\n * Does `file` satisfy a native `accept` string? Extension rules match the\n * name, `type/*` rules match the MIME prefix, anything else is an exact MIME.\n * A browser reports several media types for one `.ico` file and sometimes\n * none at all, which is why an extension rule exists at all.\n */\nexport function acceptsFile(file: File, accept?: string): boolean {\n const rules = (accept ?? '').split(',').map(rule => rule.trim().toLocaleLowerCase()).filter(Boolean);\n if (rules.length === 0 || rules.includes('*') || rules.includes('*/*')) return true;\n const fileName = file.name.toLocaleLowerCase();\n const fileType = file.type.toLocaleLowerCase();\n return rules.some(rule => {\n if (rule.startsWith('.')) return fileName.endsWith(rule);\n if (rule.endsWith('/*')) return fileType.startsWith(rule.slice(0, -1));\n return fileType === rule;\n });\n}\n\n/** `812 B`, `14 KB`, `6.2 MB` — the units, and their digits, from the catalog. */\nexport const humanSize = (bytes: number, strings: ShellStrings['files'] = DEFAULT_SHELL_STRINGS.files): string => {\n if (bytes < 1024) return strings.bytes(bytes);\n if (bytes < 1024 * 1024) return strings.kilobytes(Math.round(bytes / 1024));\n return strings.megabytes(bytes / (1024 * 1024));\n};\n\n/** Is this drag carrying files (as opposed to text, a URL, or an in-page reorder)? */\nexport const isFileDrag = (e: DragEvent): boolean =>\n Array.from(e.dataTransfer?.types ?? []).includes('Files');\n\n/** Files on a clipboard event, if the paste carried any. */\nexport function pastedFiles(e: ClipboardEvent): File[] {\n const data = e.clipboardData;\n if (!data) return [];\n if (data.files && data.files.length > 0) return Array.from(data.files);\n const out: File[] = [];\n for (const item of Array.from(data.items ?? [])) {\n if (item.kind === 'file') {\n const file = item.getAsFile();\n if (file) out.push(file);\n }\n }\n return out;\n}\n\nexport function useFileIntake({\n onAccept, accept, maxSizeBytes, maxFiles, multiple = true, currentCount = 0,\n acceptHint, disabled = false,\n}: FileIntakeOptions) {\n const strings = useShellStrings().files;\n const inputRef = useRef<HTMLInputElement>(null);\n const [dragOver, setDragOver] = useState(false);\n const [rejections, setRejections] = useState<FileRejection[]>([]);\n // A drag passes over every child of the zone, firing leave/enter pairs; a\n // depth counter is what keeps the highlight steady across them.\n const depthRef = useRef(0);\n\n const hintText = typeof acceptHint === 'string' ? acceptHint : '';\n\n /** Run every candidate through the checks; deliver the survivors. */\n const take = useCallback((incoming: FileList | File[] | null | undefined) => {\n if (disabled || !incoming) return;\n const candidates = Array.from(incoming);\n if (candidates.length === 0) return;\n const accepted: File[] = [];\n const rejected: FileRejection[] = [];\n for (const file of multiple ? candidates : candidates.slice(0, 1)) {\n if (!acceptsFile(file, accept)) {\n rejected.push({\n file, reason: 'type',\n message: strings.notAccepted(file.name, hintText),\n });\n continue;\n }\n if (maxSizeBytes != null && file.size > maxSizeBytes) {\n rejected.push({\n file, reason: 'size',\n message: strings.tooLarge(file.name, humanSize(file.size, strings), humanSize(maxSizeBytes, strings)),\n });\n continue;\n }\n if (multiple && maxFiles != null && currentCount + accepted.length >= maxFiles) {\n rejected.push({\n file, reason: 'count',\n message: strings.tooMany(file.name, maxFiles),\n });\n continue;\n }\n accepted.push(file);\n }\n setRejections(rejected);\n if (accepted.length) onAccept(accepted);\n // Reset the native input so re-picking the SAME file fires change again —\n // without this, removing a file and re-adding it appears to do nothing.\n if (inputRef.current) inputRef.current.value = '';\n }, [disabled, multiple, accept, hintText, maxSizeBytes, maxFiles, currentCount, onAccept, strings]);\n\n const open = useCallback(() => {\n if (!disabled) inputRef.current?.click();\n }, [disabled]);\n\n const clearRejections = useCallback(() => setRejections([]), []);\n\n /** Spread onto the hidden native input. It is never a tab stop and never the visible control. */\n const inputProps = {\n ref: inputRef,\n type: 'file' as const,\n accept: accept || undefined,\n multiple,\n disabled,\n tabIndex: -1,\n 'aria-hidden': true as const,\n className: 'hidden',\n onChange: (e: ChangeEvent<HTMLInputElement>) => take(e.target.files),\n };\n\n /** Spread onto the drop target. Only a drag carrying files lights it up. */\n const zoneProps = disabled\n ? {}\n : {\n onDragEnter: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n e.preventDefault();\n depthRef.current += 1;\n setDragOver(true);\n },\n onDragOver: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n e.preventDefault();\n if (!dragOver) setDragOver(true);\n },\n onDragLeave: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n depthRef.current = Math.max(0, depthRef.current - 1);\n if (depthRef.current === 0) setDragOver(false);\n },\n onDrop: (e: DragEvent) => {\n if (!isFileDrag(e)) return;\n e.preventDefault();\n depthRef.current = 0;\n setDragOver(false);\n take(e.dataTransfer.files);\n },\n };\n\n /** Spread onto a composer's text area: a paste that carries files is taken, one of text is left alone. */\n const pasteProps = disabled\n ? {}\n : {\n onPaste: (e: ClipboardEvent) => {\n const files = pastedFiles(e);\n if (files.length === 0) return;\n e.preventDefault();\n take(files);\n },\n };\n\n return { inputRef, inputProps, zoneProps, pasteProps, open, take, dragOver, rejections, clearRejections };\n}\n\n/**\n * The rejection list, announced. `role=\"alert\"` is what tells a screen-reader\n * user why the drop did nothing; the sighted user sees the same sentences.\n * Renders nothing while there is nothing to say.\n */\nexport function FileIntakeAlert({ rejections, className = 'mt-2' }: { rejections: FileRejection[]; className?: string }) {\n if (rejections.length === 0) return null;\n return (\n <ul role=\"alert\" className={`${className} flex flex-col gap-0.5`}>\n {rejections.map(({ file, message }, i) => (\n <li key={`${file.name}-${i}`} className=\"text-xs text-red-600\">{message}</li>\n ))}\n </ul>\n );\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { onOverlayOpen, Z_LAYERS, registerModalEscapeInterceptor } from './chunk-
|
|
1
|
+
import { onOverlayOpen, Z_LAYERS, registerModalEscapeInterceptor } from './chunk-W622KQTD.js';
|
|
2
2
|
import { glassStyle, GLASS_DIVIDER } from './chunk-D276EMXS.js';
|
|
3
3
|
import { useHydrated, useBrowserLayoutEffect } from './chunk-IRWSRJ4K.js';
|
|
4
4
|
import { createContext, forwardRef, useContext, useRef, useId, useState, useEffect, useMemo, useCallback } from 'react';
|
|
@@ -499,5 +499,5 @@ function MenuIcon({ d, className = "h-4 w-4 text-gray-400" }) {
|
|
|
499
499
|
}
|
|
500
500
|
|
|
501
501
|
export { MenuIcon, NATIVE_MENU_ATTR, PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, PopupSubmenu, clampMenuTop, closeMenuBelow, describeContextTarget, keepsNativeMenu, menuPanelLeft, openMenuLevel, resolveMenuLevels };
|
|
502
|
-
//# sourceMappingURL=chunk-
|
|
503
|
-
//# sourceMappingURL=chunk-
|
|
502
|
+
//# sourceMappingURL=chunk-QRL64J5F.js.map
|
|
503
|
+
//# sourceMappingURL=chunk-QRL64J5F.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shell/contextMenuTarget.ts","../src/shell/menuPath.ts","../src/shell/PopupMenu.tsx"],"names":["PopupMenuItem"],"mappings":"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","file":"chunk-E3TWIN7Z.js","sourcesContent":["/**\n * What the shell's global context menu should offer for a given right-click.\n *\n * Kept free of React (and of the DOM beyond the element it is handed) so the\n * two rules that matter can be tested directly: which targets keep the\n * browser's own menu, and which items a click produces.\n */\n\n/** Input types that put a text caret on screen. Everything else — a checkbox,\n * a range, a color swatch, a file button — has no spellcheck and no Paste,\n * so there is nothing in the browser's menu worth keeping for it. */\nconst TEXT_INPUT_TYPES = new Set([\n 'text', 'search', 'url', 'tel', 'email', 'password', 'number',\n 'date', 'datetime-local', 'month', 'week', 'time',\n]);\n\n/** Elements whose browser menu does what a page cannot: Save image as and Copy\n * image — the picture itself, not its address — on an image or a canvas, and\n * the playback controls on video and audio. */\nconst NATIVE_MEDIA = 'img, picture, canvas, video, audio';\n\n/** Opt-out attribute: anything inside `[data-native-context-menu]` keeps the\n * browser's menu. For surfaces the shell has no business re-skinning — an\n * embedded viewer, a third-party widget. */\nexport const NATIVE_MENU_ATTR = 'data-native-context-menu';\n\nfunction closest(el: Element | null, selector: string): Element | null {\n if (!el || typeof (el as Element).closest !== 'function') return null;\n return el.closest(selector);\n}\n\n/**\n * Does this target keep the browser's native menu?\n *\n * Text inputs, textareas and contenteditable do, deliberately: the browser's\n * spellcheck suggestions and \"Add to dictionary\" cannot be rebuilt by a web\n * page, and a Paste item of our own would need a clipboard permission prompt.\n * Images, canvases and media do for the same reason — Save / Copy image and\n * the playback controls are the browser's. The shell menu is for everywhere\n * else.\n */\nexport function keepsNativeMenu(target: EventTarget | null): boolean {\n const el = target as Element | null;\n if (!el || typeof (el as Element).closest !== 'function') return false;\n\n if (closest(el, `[${NATIVE_MENU_ATTR}]`)) return true;\n if (closest(el, 'textarea')) return true;\n if (closest(el, NATIVE_MEDIA)) return true;\n\n const input = closest(el, 'input') as HTMLInputElement | null;\n if (input) {\n // The property, not the attribute: the browser normalizes it, so a type it\n // does not recognise (`type=\"currency\"`, a typo) reads as the text box it\n // actually renders.\n const type = (input.type || 'text').toLowerCase();\n if (TEXT_INPUT_TYPES.has(type)) return true;\n }\n\n // `isContentEditable` is the property to ask, but jsdom does not implement\n // it, so walk the attribute — which is also what the nesting rules are\n // written in (`contenteditable=\"false\"` re-enables our menu inside an\n // editable region).\n const editable = closest(el, '[contenteditable]');\n if (editable) {\n const value = (editable.getAttribute('contenteditable') || '').toLowerCase();\n if (value === '' || value === 'true' || value === 'plaintext-only') return true;\n }\n\n return false;\n}\n\nexport type ShellContextKind = 'selection' | 'link' | 'default';\n\nexport interface ShellContextTarget {\n /** The most specific thing under the pointer — what the menu leads with. */\n kind: ShellContextKind;\n /** Trimmed selection text, when something is selected. */\n selectionText?: string;\n /** Absolute href of the nearest enclosing link. */\n linkUrl?: string;\n}\n\n/** Schemes worth an Open / Copy link address. A `javascript:` URL is a button\n * wearing a link's clothes, and `href=\"#\"` is the same trick. */\nconst LINK_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:', 'blob:']);\n\nfunction linkAddress(href: string | null | undefined, base?: string): string | undefined {\n if (!href || href === '#') return undefined;\n try {\n // `document.baseURI`, not `location.href`: a page with a `<base>` resolves\n // this the way its own links do.\n const url = new URL(href, base ?? (typeof document !== 'undefined' ? document.baseURI : undefined));\n return LINK_SCHEMES.has(url.protocol) ? url.href : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe a right-click: what was under the pointer, and what is selected.\n *\n * A selection inside a link reports both, so the menu can offer both sections\n * the way a browser's does; `kind` names whichever the menu should lead with.\n */\nexport function describeContextTarget(\n target: EventTarget | null,\n selectionText?: string | null,\n baseUrl?: string,\n): ShellContextTarget {\n const el = target as Element | null;\n const link = closest(el, 'a[href]');\n\n const text = (selectionText || '').trim();\n const linkUrl = linkAddress(link?.getAttribute('href'), baseUrl);\n\n const kind: ShellContextKind = text ? 'selection' : linkUrl ? 'link' : 'default';\n\n return {\n kind,\n ...(text ? { selectionText: text } : {}),\n ...(linkUrl ? { linkUrl } : {}),\n };\n}\n","/**\n * The open-submenu path of a hover menu — the whole state machine <StartMenu>\n * uses to decide which flyouts are open, at any depth.\n *\n * ## Why this exists\n *\n * The menu used to have TWO mechanisms: one that opened a section's flyout\n * (`hoveredSection` + its timer) and a second, separately written one that\n * opened a flyout under an item inside that flyout (`hoveredChild` + its own\n * timer). They looked alike but behaved differently, and the second one had\n * the bugs — a stale close-timer from a sibling row could fire *after* a\n * submenu had opened and shut it under the cursor, which is what \"the next\n * level is slow, or sometimes doesn't open at all\" actually was. There was\n * also no third mechanism, so a 4th level simply never rendered.\n *\n * One path, one set of rules, any depth. Panels are numbered from the root\n * menu: panel 0 is the menu itself, and `path[i]` is the row that opened panel\n * `i + 1`. So `path.length` is the number of open flyouts, and closing\n * everything under panel `d` is just truncating the path to `d`.\n *\n * All of it is pure: hover geometry comes in as numbers, so the rules can be\n * tested without a DOM.\n */\n\n/** One open flyout: which row opened it, and where it goes. */\nexport interface MenuAnchor {\n /** Identity of the row that opened this panel — a section label or an item\n * route. Also what marks that row as the open one. */\n key: string;\n /** Viewport Y of the opening row's centre; the panel is centred on it. */\n y: number;\n /** Viewport X of the panel's left edge. Resolved when the row is hovered,\n * because it depends on where the panel that owns the row ended up. */\n left: number;\n /** Whether this panel had to open to the LEFT of the one that owns its row.\n * Carried so the next level down can keep going the same way. */\n flipped: boolean;\n}\n\n/**\n * Open `anchor` as the submenu of a row in panel `depth`, closing anything\n * deeper (the previous branch the user was down).\n *\n * Returns the SAME array when that panel is already open for that key and\n * nothing deeper is open, so a repeated `mouseenter` on a row the user is\n * already on can't re-render the menu — and can't reset the panel's open\n * animation.\n */\nexport function openMenuLevel(path: MenuAnchor[], depth: number, anchor: MenuAnchor): MenuAnchor[] {\n if (path.length === depth + 1 && path[depth].key === anchor.key) return path;\n return [...path.slice(0, depth), anchor];\n}\n\n/**\n * Close panel `depth + 1` and everything under it — what hovering a row with\n * no submenu, or leaving a panel, eventually leads to.\n *\n * Also identity-stable: nothing to close means the same array back.\n */\nexport function closeMenuBelow(path: MenuAnchor[], depth: number): MenuAnchor[] {\n const keep = Math.max(0, depth);\n return path.length > keep ? path.slice(0, keep) : path;\n}\n\n/**\n * The items each open panel shows: `[0]` is the flyout the root row opened,\n * `[1]` its child, and so on for as long as the path keeps resolving.\n *\n * Stops early rather than rendering an empty panel, so a path left over from a\n * branch that has since disappeared (a permission change, nav data swapped\n * under the menu) degrades to the levels that still exist.\n */\nexport function resolveMenuLevels<T extends { to: string }>(\n rootItems: T[],\n path: MenuAnchor[],\n childrenOf: (item: T) => T[],\n): T[][] {\n if (path.length === 0 || rootItems.length === 0) return [];\n const levels: T[][] = [rootItems];\n for (let i = 1; i < path.length; i++) {\n const parent = levels[i - 1].find(item => item.to === path[i].key);\n const kids = parent ? childrenOf(parent) : [];\n if (kids.length === 0) break;\n levels.push(kids);\n }\n return levels;\n}\n\n/**\n * Where a panel of height `height` sits when centred on `anchorY`, kept inside\n * the usable vertical span (`minTop`..`maxBottom` — the viewport minus the\n * taskbar edge and a gutter).\n *\n * A panel taller than the span is pinned to the top of it and scrolls; that is\n * why the height is clamped before it is centred, and not after.\n */\nexport function clampMenuTop(anchorY: number, height: number, minTop: number, maxBottom: number): number {\n const span = Math.max(0, maxBottom - minTop);\n const h = Math.min(height, span);\n const top = anchorY - h / 2;\n if (top < minTop) return minTop;\n if (top + h > maxBottom) return Math.max(minTop, maxBottom - h);\n return top;\n}\n\n/**\n * Where a panel of width `width` sits horizontally: to the right of the panel\n * that owns the hovered row, or flipped to its left when there isn't room.\n *\n * The flip is what makes depth open-ended. Every level costs another panel\n * width, so a deep enough branch always runs out of screen — and a menu opened\n * from a right-hand taskbar runs out on the very first one.\n *\n * `preferLeft` keeps a branch that has already turned around going the same\n * way. Without it the levels ping-pong: one flips left over its grandparent,\n * the next finds room on the right again and lands back on top of its\n * grandparent, and the chain folds into two stacks of panels.\n */\nexport function menuPanelLeft(\n ownerLeft: number,\n ownerRight: number,\n width: number,\n viewportW: number,\n gap: number,\n preferLeft = false,\n margin = 8,\n): { left: number; flipped: boolean } {\n const onRight = ownerRight + gap;\n const onLeft = ownerLeft - gap - width;\n const rightFits = onRight + width <= viewportW - margin;\n const leftFits = onLeft >= margin;\n if (preferLeft && leftFits) return { left: onLeft, flipped: true };\n if (rightFits) return { left: onRight, flipped: false };\n if (leftFits) return { left: onLeft, flipped: true };\n // Neither side fits: hug the edge the panel was heading for rather than\n // hang off the screen.\n return preferLeft\n ? { left: margin, flipped: true }\n : { left: Math.max(margin, viewportW - margin - width), flipped: false };\n}\n","import {\n createContext, forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState,\n type ReactNode, type CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { glassStyle, GLASS_DIVIDER } from '../utils/glass';\nimport { keepsNativeMenu } from './contextMenuTarget';\nimport { registerModalEscapeInterceptor } from './escapeInterceptors';\nimport { onOverlayOpen } from './overlayEvents';\nimport { useBrowserLayoutEffect, useHydrated } from './hydration';\nimport { Z_LAYERS } from './zLayers';\nimport { clampMenuTop, closeMenuBelow, menuPanelLeft, openMenuLevel, type MenuAnchor } from './menuPath';\n\n/**\n * Unified popup menu component — used for all context menus, dropdowns, and flyouts.\n * Reads --menu-density CSS variable: 'tight', 'normal' (default), or 'large'.\n */\n\nexport interface PopupMenuProps {\n children: ReactNode;\n style?: CSSProperties;\n className?: string;\n onClose?: () => void;\n minWidth?: number;\n /** Render into document.body instead of in place. Required when the menu\n * is opened from INSIDE a window: the window panel is a transformed /\n * backdrop-filtered `overflow-hidden` container, which both re-anchors\n * `position: fixed` descendants to itself and clips them — a menu\n * positioned at viewport coordinates ends up offset or invisible.\n * Portaling restores true viewport positioning. */\n portal?: boolean;\n}\n\n/** Guarded like `glassStyle`'s reads: a server has no stylesheet to ask, and a\n * menu rendered there takes the density the variable defaults to. */\nfunction getDensity(): 'tight' | 'normal' | 'large' {\n try {\n return (getComputedStyle(document.documentElement).getPropertyValue('--menu-density')?.trim() as any) || 'normal';\n } catch {\n return 'normal';\n }\n}\n\n/** Grace period before a submenu closes, so the pointer can cut the corner\n * across sibling rows on its way into the panel. The Start menu's value. */\nconst SUBMENU_CLOSE_DELAY = 200;\n/** How long the pointer rests on a submenu row before it opens. Short enough\n * to feel immediate, long enough that sweeping down a menu past the row does\n * not flash its panel open. */\nconst SUBMENU_OPEN_DELAY = 120;\n/** Gap between a submenu and the menu it opened from. */\nconst SUBMENU_GAP = 4;\n\n/**\n * What every submenu under one `PopupMenu` shares.\n *\n * The open submenus are ONE path for the whole tree — the same state machine\n * the Start menu runs (`menuPath.ts`): `path[d]` is the submenu open from a row\n * in panel `d`, and there is one close timer. That is what makes sibling\n * submenus exclusive, and it is what stops a stale timer from a row the\n * pointer merely passed over from closing a submenu that has since opened —\n * the bug the Start menu had while it ran a timer per level.\n */\ninterface MenuTree {\n /** Marks every panel of this tree, so a click inside a portalled submenu is\n * not \"outside\" the menu. */\n id: string;\n /** 0 for rows in the root menu, 1 inside its submenu, and so on. */\n depth: number;\n path: MenuAnchor[];\n open: (depth: number, anchor: MenuAnchor) => void;\n /** Close the submenus under panel `depth`, after the grace period. */\n scheduleClose: (depth: number) => void;\n /** Close the submenus under panel `depth` now. */\n closeBelow: (depth: number) => void;\n cancelClose: () => void;\n /** Close every panel — an item inside a submenu was chosen. */\n closeTree: () => void;\n /** The root panel, for measuring what it is layered at. */\n root: React.RefObject<HTMLDivElement | null>;\n}\n\nconst MenuTreeContext = createContext<MenuTree | null>(null);\n\n/** Container for a popup menu — auto-clamps to stay within viewport */\nexport function PopupMenu({ children, style, className = '', onClose, minWidth = 180, portal = false }: PopupMenuProps) {\n const ref = useRef<HTMLDivElement>(null);\n const treeId = useId();\n const [path, setPath] = useState<MenuAnchor[]>([]);\n const closeTimer = useRef<ReturnType<typeof setTimeout>>();\n useEffect(() => () => clearTimeout(closeTimer.current), []);\n // A portal has no place in server HTML — there is no <body> to put it in —\n // so a portalled menu that is open on the first render waits for hydration\n // to end, and both sides agree that it is not there yet.\n const hydrated = useHydrated();\n\n const tree = useMemo<MenuTree>(() => {\n const cancelClose = () => clearTimeout(closeTimer.current);\n return {\n id: treeId,\n depth: 0,\n path,\n open: (depth, anchor) => { cancelClose(); setPath(prev => openMenuLevel(prev, depth, anchor)); },\n scheduleClose: depth => {\n cancelClose();\n closeTimer.current = setTimeout(() => setPath(prev => closeMenuBelow(prev, depth)), SUBMENU_CLOSE_DELAY);\n },\n closeBelow: depth => { cancelClose(); setPath(prev => closeMenuBelow(prev, depth)); },\n cancelClose,\n closeTree: () => { cancelClose(); setPath([]); onClose?.(); },\n root: ref,\n };\n }, [treeId, path, onClose]);\n\n useEffect(() => {\n if (!onClose) return;\n const handleClick = (e: PointerEvent | MouseEvent) => {\n if ((e.target as HTMLElement).closest('[data-menu-toggle]')) return;\n // A submenu is portalled to <body>, so it is outside `ref` in the DOM\n // while still being part of this menu.\n if ((e.target as HTMLElement).closest?.(`[data-popup-menu-tree=\"${treeId}\"]`)) return;\n if (ref.current && !ref.current.contains(e.target as Node)) onClose();\n };\n const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n window.addEventListener('pointerdown', handleClick);\n window.addEventListener('keydown', handleKey);\n // An overlay opening (⌘K's palette, a Dialog) is an outside interaction\n // the pointer listener never sees; left open, the menu would sit over the\n // overlay's backdrop or behind it (see `overlayEvents.ts`).\n const offOverlay = onOverlayOpen(onClose);\n return () => {\n window.removeEventListener('pointerdown', handleClick);\n window.removeEventListener('keydown', handleKey);\n offOverlay();\n };\n }, [onClose, treeId]);\n\n // After render, clamp position to viewport boundaries\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n requestAnimationFrame(() => {\n const rect = el.getBoundingClientRect();\n const margin = 8;\n // Clamp right edge\n if (rect.right > window.innerWidth - margin) {\n const overflow = rect.right - window.innerWidth + margin;\n if (el.style.left) {\n el.style.left = `${parseFloat(el.style.left) - overflow}px`;\n } else if (!el.style.right) {\n el.style.left = `${rect.left - overflow}px`;\n }\n }\n // Clamp left edge\n if (rect.left < margin) {\n if (el.style.left) {\n el.style.left = `${margin}px`;\n } else if (el.style.right) {\n el.style.right = `${window.innerWidth - rect.width - margin}px`;\n }\n }\n // Clamp bottom edge\n if (rect.bottom > window.innerHeight - margin) {\n const overflow = rect.bottom - window.innerHeight + margin;\n if (el.style.top) {\n el.style.top = `${parseFloat(el.style.top) - overflow}px`;\n } else if (el.style.bottom) {\n // Already anchored to bottom, just clamp\n el.style.bottom = `${margin}px`;\n } else {\n el.style.top = `${rect.top - overflow}px`;\n }\n }\n // Clamp top edge\n if (rect.top < margin) {\n if (el.style.top) {\n el.style.top = `${margin}px`;\n } else if (el.style.bottom) {\n el.style.bottom = `${window.innerHeight - rect.height - margin}px`;\n }\n }\n });\n });\n\n const density = getDensity();\n const ownsZ = /(^|[\\s:])!?z-/.test(className);\n\n // A right-click on an open menu is not a request for another menu. Claiming\n // it stops the shell-wide `ShellContextMenu` (which stands down on a\n // prevented event) and the browser's from opening on top of this one. A text\n // field inside a menu still keeps the browser's, as it does everywhere else.\n const menu = (\n <div ref={ref}\n data-popup-menu-tree={treeId}\n className={`fixed rounded-2xl ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'} ${className}`}\n onContextMenu={e => { if (!keepsNativeMenu(e.target)) e.preventDefault(); }}\n // The menu layer is inline now, and an inline z-index beats any class —\n // so a caller that layered its menu with a `z-…` class keeps its own.\n // `popup-in` (and the submenus' keyframes) are declared in ui.css: a\n // `<style>` element here was refused by a strict style-src.\n style={{ ...(ownsZ ? {} : { zIndex: Z_LAYERS.menu }), minWidth, animation: 'popup-in 0.12s ease-out', ...glassStyle(), ...style }}>\n <MenuTreeContext.Provider value={tree}>{children}</MenuTreeContext.Provider>\n </div>\n );\n\n if (!portal) return menu;\n return hydrated ? createPortal(menu, document.body) : null;\n}\n\n/** A clickable menu item */\nexport interface PopupMenuItemProps {\n onClick?: () => void;\n children: ReactNode;\n className?: string;\n danger?: boolean;\n disabled?: boolean;\n /**\n * The menu semantics, when this item is part of one.\n *\n * A context menu on the desktop is a list of buttons and needs none of this;\n * a dropdown attached to a trigger is a `menu` whose items are `menuitem`s\n * with one tab stop between them. Both draw the same item, so the styling\n * lives here rather than being copied into whichever one is newer.\n */\n role?: 'menuitem';\n tabIndex?: number;\n id?: string;\n onMouseEnter?: () => void;\n}\n\n/**\n * `forwardRef`, not a `ref` prop.\n *\n * Taking `ref` as an ordinary prop is React 19 only. This package's peer range\n * is `react: \">=18\"`, and React 18 strips `ref` from a function component's\n * props and warns — so on an 18 consumer the ref would silently never attach,\n * `DropdownMenu`'s `itemRefs` would all be null, and its arrow keys would move\n * `tabIndex` while DOM focus stayed put. A keyboard-dead menu with no error.\n */\n/** The row look shared by an item and a submenu's row. */\nfunction itemLook(danger: boolean | undefined, disabled: boolean | undefined, className: string, highlighted = false) {\n const density = getDensity();\n // Vertical gap between items. `normal` sits a little tighter than the raw\n // size padding; `large` adds a bit more room. Floored at the tight value so\n // the reduced `normal` never drops below `tight` at the smallest menu size.\n const itemPadY = density === 'tight' ? '0.25rem'\n : density === 'large' ? '0.6rem'\n : 'max(0.25rem, calc(var(--menu-padding-y, 0.5rem) - 0.1rem))';\n const tone = danger ? 'text-red-600 hover:bg-red-50'\n : highlighted ? 'bg-blue-50 text-blue-700'\n : 'text-gray-700 hover:bg-blue-50 hover:text-blue-700';\n return {\n className: `w-full flex items-center gap-2 text-left transition-colors rounded-lg mx-auto\n ${tone}\n ${disabled ? 'opacity-40 cursor-not-allowed' : ''}\n ${className}`,\n style: {\n width: 'calc(100% - 8px)',\n marginLeft: 4,\n marginRight: 4,\n fontSize: 'var(--menu-font-size, 14px)',\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: itemPadY,\n paddingBottom: itemPadY,\n } satisfies CSSProperties,\n };\n}\n\nexport const PopupMenuItem = forwardRef<HTMLButtonElement, PopupMenuItemProps>(function PopupMenuItem({\n onClick, children, className = '', danger, disabled,\n role, tabIndex, id, onMouseEnter,\n}, ref) {\n const tree = useContext(MenuTreeContext);\n const look = itemLook(danger, disabled, className);\n return (\n <button\n ref={ref}\n id={id}\n role={role}\n tabIndex={tabIndex}\n onClick={() => {\n onClick?.();\n // Inside a submenu, choosing an item is the end of the whole menu —\n // not just the panel it sat in. A root item keeps its old contract:\n // the caller's own onClick decides.\n if (tree && tree.depth > 0) tree.closeTree();\n }}\n onMouseEnter={() => {\n // Resting on a plain row retires any submenu open beside it, after the\n // same grace the Start menu gives.\n tree?.scheduleClose(tree.depth);\n onMouseEnter?.();\n }}\n disabled={disabled}\n className={look.className}\n style={look.style}\n >\n {children}\n </button>\n );\n});\n\nexport interface PopupSubmenuProps {\n /** The row's text. */\n label: ReactNode;\n /** Drawn before the label, as in a `PopupMenuItem`. */\n icon?: ReactNode;\n /** The submenu's contents — `PopupMenuItem`, `PopupMenuLabel`,\n * `PopupMenuDivider`, or another `PopupSubmenu`. */\n children: ReactNode;\n disabled?: boolean;\n className?: string;\n /** The submenu panel's minimum width. */\n minWidth?: number;\n /** The tallest the panel may grow before its items scroll — a long target\n * list, say `'min(60vh, 420px)'`. Never taller than the viewport less its\n * 8px gutters, whatever is passed. */\n maxHeight?: number | string;\n}\n\nconst chevron = (\n <svg aria-hidden className=\"h-3.5 w-3.5 shrink-0 ml-auto text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n);\n\n/** The z-index a panel must beat: the outermost stacking level `el` sits in\n * at <body>, since a portalled submenu is compared against that, not against\n * whatever the root menu is nested inside. */\nfunction layerOf(el: HTMLElement | null): number {\n let z: number = Z_LAYERS.menu;\n for (let node = el; node && node !== document.body; node = node.parentElement) {\n const value = parseInt(getComputedStyle(node).zIndex, 10);\n if (!Number.isNaN(value)) z = value;\n }\n return z;\n}\n\n/** Enabled items of a panel, in order — what the arrow keys walk. */\nfunction focusables(panel: HTMLElement | null): HTMLElement[] {\n if (!panel) return [];\n return [...panel.querySelectorAll<HTMLElement>('button:not([disabled])')]\n .filter(b => b.closest('[data-popup-submenu-panel]') === panel);\n}\n\n/**\n * A row in a `PopupMenu` that opens a nested menu.\n *\n * Opens on hover after a short rest, and at once on click, Enter, Space or\n * ArrowRight (which also move focus to its first item). ArrowLeft and Escape\n * close it and give focus back to the row. It opens to the right of the menu,\n * or to the left when there is no room, and moves up when it would run off the\n * bottom — the Start menu's placement rules, from `menuPath.ts`. Choosing an\n * item inside closes the whole menu (the root `PopupMenu`'s `onClose`).\n */\nexport function PopupSubmenu({ label, icon, children, disabled, className = '', minWidth = 180, maxHeight }: PopupSubmenuProps) {\n const tree = useContext(MenuTreeContext);\n const key = useId();\n const rowRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const openTimer = useRef<ReturnType<typeof setTimeout>>();\n /** Whether the current opening came from the keyboard — then focus moves in. */\n const focusOnOpen = useRef(false);\n const panelId = `${key}-menu`;\n useEffect(() => () => clearTimeout(openTimer.current), []);\n\n const depth = tree?.depth ?? 0;\n const anchor = tree?.path[depth];\n const isOpen = !!anchor && anchor.key === key && !disabled;\n\n const openNow = useCallback((withFocus: boolean) => {\n if (!tree || disabled || !rowRef.current) return;\n clearTimeout(openTimer.current);\n focusOnOpen.current = withFocus;\n const row = rowRef.current.getBoundingClientRect();\n const owner = (rowRef.current.closest('[data-popup-submenu-panel]')\n ?? rowRef.current.closest('[data-popup-menu-tree]')) as HTMLElement | null;\n const ownerRect = owner?.getBoundingClientRect() ?? row;\n const preferLeft = depth > 0 && (tree.path[depth - 1]?.flipped ?? false);\n const place = menuPanelLeft(ownerRect.left, ownerRect.right, minWidth, window.innerWidth, SUBMENU_GAP, preferLeft);\n tree.open(depth, { key, y: row.top, ...place });\n }, [tree, disabled, depth, key, minWidth]);\n\n const closeSelf = (restoreFocus: boolean) => {\n tree?.closeBelow(depth);\n if (restoreFocus) rowRef.current?.focus();\n };\n\n // Escape through the shell's interceptor seam: inside a window, `Modal`\n // takes Escape in the capture phase and would close the whole window.\n useEffect(() => {\n if (!isOpen) return;\n return registerModalEscapeInterceptor(e => {\n if (e.key !== 'Escape') return false;\n // Registration order cannot say which submenu is deepest: React runs a\n // parent's effects after its children's, so once both re-render the\n // outer submenu is the newest registrant and is asked first. Defer to\n // the open path instead — a deeper level open under this one takes it.\n if (tree && tree.path.length > depth + 1) return false;\n closeSelf(true);\n return true;\n });\n });\n\n // Place the panel against its real size before paint — the estimate used\n // `minWidth` and the row's top — then move focus in if a key opened it.\n useBrowserLayoutEffect(() => {\n if (!isOpen || !anchor) return;\n const place = () => {\n const panel = panelRef.current;\n if (!panel || !rowRef.current) return;\n const margin = 8;\n const owner = (rowRef.current.closest('[data-popup-submenu-panel]')\n ?? rowRef.current.closest('[data-popup-menu-tree]')) as HTMLElement | null;\n const ownerRect = owner?.getBoundingClientRect() ?? rowRef.current.getBoundingClientRect();\n const preferLeft = depth > 0 && (tree?.path[depth - 1]?.flipped ?? false);\n const width = panel.offsetWidth || minWidth;\n const spot = menuPanelLeft(ownerRect.left, ownerRect.right, width, window.innerWidth, SUBMENU_GAP, preferLeft, margin);\n const height = panel.offsetHeight;\n // Top-aligned with the row: centring the clamp on `row top + half the\n // panel` gives exactly that, and still moves the panel up at the bottom.\n const rowTop = rowRef.current.getBoundingClientRect().top;\n const top = clampMenuTop(rowTop + height / 2, height, margin, window.innerHeight - margin);\n panel.style.left = `${spot.left}px`;\n panel.style.top = `${top}px`;\n panel.dataset.flipped = String(spot.flipped);\n };\n place();\n // Again after the root menu's own viewport clamp, which runs in a frame\n // AFTER this effect: on a re-render it can still move the root a few\n // pixels, and the submenu has to follow the menu it hangs off.\n let second = 0;\n const first = requestAnimationFrame(() => { second = requestAnimationFrame(place); });\n return () => { cancelAnimationFrame(first); cancelAnimationFrame(second); };\n });\n\n useEffect(() => {\n if (!isOpen || !focusOnOpen.current) return;\n focusOnOpen.current = false;\n focusables(panelRef.current)[0]?.focus();\n }, [isOpen]);\n\n const childTree = useMemo<MenuTree | null>(() => tree && { ...tree, depth: depth + 1 }, [tree, depth]);\n\n if (!tree) throw new Error('PopupSubmenu must be rendered inside a PopupMenu');\n\n const onRowKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n e.stopPropagation();\n openNow(true);\n }\n };\n\n const onPanelKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n // React bubbles through portals, so a key pressed in a deeper submenu\n // reaches this handler too. That panel handled it already.\n if ((e.target as HTMLElement).closest('[data-popup-submenu-panel]') !== e.currentTarget) return;\n const items = focusables(panelRef.current);\n const at = items.indexOf(document.activeElement as HTMLElement);\n const move = (to: number) => { e.preventDefault(); e.stopPropagation(); items[(to + items.length) % items.length]?.focus(); };\n if (e.key === 'ArrowDown') move(at + 1);\n else if (e.key === 'ArrowUp') move(at < 0 ? items.length - 1 : at - 1);\n else if (e.key === 'Home') move(0);\n else if (e.key === 'End') move(items.length - 1);\n else if (e.key === 'ArrowLeft') { e.preventDefault(); e.stopPropagation(); closeSelf(true); }\n };\n\n const look = itemLook(false, disabled, className, isOpen);\n const density = getDensity();\n\n return (\n <>\n <button\n ref={rowRef}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? panelId : undefined}\n disabled={disabled}\n data-popup-submenu-row\n className={look.className}\n style={look.style}\n onClick={e => {\n // `detail` is 0 for a click the keyboard made (Enter, Space).\n const byKey = e.detail === 0;\n if (!isOpen) openNow(byKey);\n else if (byKey) focusables(panelRef.current)[0]?.focus();\n }}\n onKeyDown={onRowKeyDown}\n onMouseEnter={() => {\n if (disabled) return;\n tree.cancelClose();\n if (isOpen) { tree.closeBelow(depth + 1); return; }\n clearTimeout(openTimer.current);\n openTimer.current = setTimeout(() => openNow(false), SUBMENU_OPEN_DELAY);\n }}\n onMouseLeave={() => {\n clearTimeout(openTimer.current);\n tree.scheduleClose(depth);\n }}\n >\n {icon}\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n {chevron}\n </button>\n {isOpen && createPortal(\n <div\n ref={panelRef}\n id={panelId}\n role=\"menu\"\n data-popup-menu-tree={tree.id}\n data-popup-submenu-panel\n className={`fixed rounded-2xl overflow-x-hidden overflow-y-auto overscroll-contain ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'}`}\n style={{\n left: anchor!.left, top: anchor!.y, minWidth,\n // The viewport cap always applies, so a long list scrolls rather\n // than running off the screen even when no maxHeight is passed.\n maxHeight: maxHeight === undefined\n ? 'calc(100vh - 16px)'\n : `min(${typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight}, calc(100vh - 16px))`,\n zIndex: layerOf(tree.root.current) + depth + 1,\n animation: `${anchor!.flipped ? 'popup-submenu-in-left' : 'popup-submenu-in'} 0.1s ease-out`,\n ...glassStyle(),\n }}\n onKeyDown={onPanelKeyDown}\n onMouseEnter={() => { tree.cancelClose(); clearTimeout(openTimer.current); }}\n onMouseLeave={() => tree.scheduleClose(depth + 1)}\n onContextMenu={e => { if (!keepsNativeMenu(e.target)) e.preventDefault(); }}\n >\n <MenuTreeContext.Provider value={childTree}>{children}</MenuTreeContext.Provider>\n </div>,\n document.body,\n )}\n </>\n );\n}\n\n/** A divider line between menu items */\nexport function PopupMenuDivider() {\n const density = getDensity();\n return <div className={`border-t ${GLASS_DIVIDER} ${density === 'tight' ? 'my-0.5' : density === 'large' ? 'my-1.5' : 'my-1'} mx-3`} />;\n}\n\n/** A section header label */\nexport function PopupMenuLabel({ children }: { children: ReactNode }) {\n const density = getDensity();\n const labelPadY = density === 'tight' ? '0.125rem' : density === 'large' ? '0.375rem' : '0.25rem';\n return (\n <div\n className=\"text-[10px] font-medium text-gray-400 uppercase tracking-wider\"\n style={{\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: labelPadY,\n paddingBottom: labelPadY,\n }}\n >\n {children}\n </div>\n );\n}\n\n/** An icon helper */\nexport function MenuIcon({ d, className = 'h-4 w-4 text-gray-400' }: { d: string; className?: string }) {\n return (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d={d} />\n </svg>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/shell/contextMenuTarget.ts","../src/shell/menuPath.ts","../src/shell/PopupMenu.tsx"],"names":["PopupMenuItem"],"mappings":"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","file":"chunk-QRL64J5F.js","sourcesContent":["/**\n * What the shell's global context menu should offer for a given right-click.\n *\n * Kept free of React (and of the DOM beyond the element it is handed) so the\n * two rules that matter can be tested directly: which targets keep the\n * browser's own menu, and which items a click produces.\n */\n\n/** Input types that put a text caret on screen. Everything else — a checkbox,\n * a range, a color swatch, a file button — has no spellcheck and no Paste,\n * so there is nothing in the browser's menu worth keeping for it. */\nconst TEXT_INPUT_TYPES = new Set([\n 'text', 'search', 'url', 'tel', 'email', 'password', 'number',\n 'date', 'datetime-local', 'month', 'week', 'time',\n]);\n\n/** Elements whose browser menu does what a page cannot: Save image as and Copy\n * image — the picture itself, not its address — on an image or a canvas, and\n * the playback controls on video and audio. */\nconst NATIVE_MEDIA = 'img, picture, canvas, video, audio';\n\n/** Opt-out attribute: anything inside `[data-native-context-menu]` keeps the\n * browser's menu. For surfaces the shell has no business re-skinning — an\n * embedded viewer, a third-party widget. */\nexport const NATIVE_MENU_ATTR = 'data-native-context-menu';\n\nfunction closest(el: Element | null, selector: string): Element | null {\n if (!el || typeof (el as Element).closest !== 'function') return null;\n return el.closest(selector);\n}\n\n/**\n * Does this target keep the browser's native menu?\n *\n * Text inputs, textareas and contenteditable do, deliberately: the browser's\n * spellcheck suggestions and \"Add to dictionary\" cannot be rebuilt by a web\n * page, and a Paste item of our own would need a clipboard permission prompt.\n * Images, canvases and media do for the same reason — Save / Copy image and\n * the playback controls are the browser's. The shell menu is for everywhere\n * else.\n */\nexport function keepsNativeMenu(target: EventTarget | null): boolean {\n const el = target as Element | null;\n if (!el || typeof (el as Element).closest !== 'function') return false;\n\n if (closest(el, `[${NATIVE_MENU_ATTR}]`)) return true;\n if (closest(el, 'textarea')) return true;\n if (closest(el, NATIVE_MEDIA)) return true;\n\n const input = closest(el, 'input') as HTMLInputElement | null;\n if (input) {\n // The property, not the attribute: the browser normalizes it, so a type it\n // does not recognise (`type=\"currency\"`, a typo) reads as the text box it\n // actually renders.\n const type = (input.type || 'text').toLowerCase();\n if (TEXT_INPUT_TYPES.has(type)) return true;\n }\n\n // `isContentEditable` is the property to ask, but jsdom does not implement\n // it, so walk the attribute — which is also what the nesting rules are\n // written in (`contenteditable=\"false\"` re-enables our menu inside an\n // editable region).\n const editable = closest(el, '[contenteditable]');\n if (editable) {\n const value = (editable.getAttribute('contenteditable') || '').toLowerCase();\n if (value === '' || value === 'true' || value === 'plaintext-only') return true;\n }\n\n return false;\n}\n\nexport type ShellContextKind = 'selection' | 'link' | 'default';\n\nexport interface ShellContextTarget {\n /** The most specific thing under the pointer — what the menu leads with. */\n kind: ShellContextKind;\n /** Trimmed selection text, when something is selected. */\n selectionText?: string;\n /** Absolute href of the nearest enclosing link. */\n linkUrl?: string;\n}\n\n/** Schemes worth an Open / Copy link address. A `javascript:` URL is a button\n * wearing a link's clothes, and `href=\"#\"` is the same trick. */\nconst LINK_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:', 'blob:']);\n\nfunction linkAddress(href: string | null | undefined, base?: string): string | undefined {\n if (!href || href === '#') return undefined;\n try {\n // `document.baseURI`, not `location.href`: a page with a `<base>` resolves\n // this the way its own links do.\n const url = new URL(href, base ?? (typeof document !== 'undefined' ? document.baseURI : undefined));\n return LINK_SCHEMES.has(url.protocol) ? url.href : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Describe a right-click: what was under the pointer, and what is selected.\n *\n * A selection inside a link reports both, so the menu can offer both sections\n * the way a browser's does; `kind` names whichever the menu should lead with.\n */\nexport function describeContextTarget(\n target: EventTarget | null,\n selectionText?: string | null,\n baseUrl?: string,\n): ShellContextTarget {\n const el = target as Element | null;\n const link = closest(el, 'a[href]');\n\n const text = (selectionText || '').trim();\n const linkUrl = linkAddress(link?.getAttribute('href'), baseUrl);\n\n const kind: ShellContextKind = text ? 'selection' : linkUrl ? 'link' : 'default';\n\n return {\n kind,\n ...(text ? { selectionText: text } : {}),\n ...(linkUrl ? { linkUrl } : {}),\n };\n}\n","/**\n * The open-submenu path of a hover menu — the whole state machine <StartMenu>\n * uses to decide which flyouts are open, at any depth.\n *\n * ## Why this exists\n *\n * The menu used to have TWO mechanisms: one that opened a section's flyout\n * (`hoveredSection` + its timer) and a second, separately written one that\n * opened a flyout under an item inside that flyout (`hoveredChild` + its own\n * timer). They looked alike but behaved differently, and the second one had\n * the bugs — a stale close-timer from a sibling row could fire *after* a\n * submenu had opened and shut it under the cursor, which is what \"the next\n * level is slow, or sometimes doesn't open at all\" actually was. There was\n * also no third mechanism, so a 4th level simply never rendered.\n *\n * One path, one set of rules, any depth. Panels are numbered from the root\n * menu: panel 0 is the menu itself, and `path[i]` is the row that opened panel\n * `i + 1`. So `path.length` is the number of open flyouts, and closing\n * everything under panel `d` is just truncating the path to `d`.\n *\n * All of it is pure: hover geometry comes in as numbers, so the rules can be\n * tested without a DOM.\n */\n\n/** One open flyout: which row opened it, and where it goes. */\nexport interface MenuAnchor {\n /** Identity of the row that opened this panel — a section label or an item\n * route. Also what marks that row as the open one. */\n key: string;\n /** Viewport Y of the opening row's centre; the panel is centred on it. */\n y: number;\n /** Viewport X of the panel's left edge. Resolved when the row is hovered,\n * because it depends on where the panel that owns the row ended up. */\n left: number;\n /** Whether this panel had to open to the LEFT of the one that owns its row.\n * Carried so the next level down can keep going the same way. */\n flipped: boolean;\n}\n\n/**\n * Open `anchor` as the submenu of a row in panel `depth`, closing anything\n * deeper (the previous branch the user was down).\n *\n * Returns the SAME array when that panel is already open for that key and\n * nothing deeper is open, so a repeated `mouseenter` on a row the user is\n * already on can't re-render the menu — and can't reset the panel's open\n * animation.\n */\nexport function openMenuLevel(path: MenuAnchor[], depth: number, anchor: MenuAnchor): MenuAnchor[] {\n if (path.length === depth + 1 && path[depth].key === anchor.key) return path;\n return [...path.slice(0, depth), anchor];\n}\n\n/**\n * Close panel `depth + 1` and everything under it — what hovering a row with\n * no submenu, or leaving a panel, eventually leads to.\n *\n * Also identity-stable: nothing to close means the same array back.\n */\nexport function closeMenuBelow(path: MenuAnchor[], depth: number): MenuAnchor[] {\n const keep = Math.max(0, depth);\n return path.length > keep ? path.slice(0, keep) : path;\n}\n\n/**\n * The items each open panel shows: `[0]` is the flyout the root row opened,\n * `[1]` its child, and so on for as long as the path keeps resolving.\n *\n * Stops early rather than rendering an empty panel, so a path left over from a\n * branch that has since disappeared (a permission change, nav data swapped\n * under the menu) degrades to the levels that still exist.\n */\nexport function resolveMenuLevels<T extends { to: string }>(\n rootItems: T[],\n path: MenuAnchor[],\n childrenOf: (item: T) => T[],\n): T[][] {\n if (path.length === 0 || rootItems.length === 0) return [];\n const levels: T[][] = [rootItems];\n for (let i = 1; i < path.length; i++) {\n const parent = levels[i - 1].find(item => item.to === path[i].key);\n const kids = parent ? childrenOf(parent) : [];\n if (kids.length === 0) break;\n levels.push(kids);\n }\n return levels;\n}\n\n/**\n * Where a panel of height `height` sits when centred on `anchorY`, kept inside\n * the usable vertical span (`minTop`..`maxBottom` — the viewport minus the\n * taskbar edge and a gutter).\n *\n * A panel taller than the span is pinned to the top of it and scrolls; that is\n * why the height is clamped before it is centred, and not after.\n */\nexport function clampMenuTop(anchorY: number, height: number, minTop: number, maxBottom: number): number {\n const span = Math.max(0, maxBottom - minTop);\n const h = Math.min(height, span);\n const top = anchorY - h / 2;\n if (top < minTop) return minTop;\n if (top + h > maxBottom) return Math.max(minTop, maxBottom - h);\n return top;\n}\n\n/**\n * Where a panel of width `width` sits horizontally: to the right of the panel\n * that owns the hovered row, or flipped to its left when there isn't room.\n *\n * The flip is what makes depth open-ended. Every level costs another panel\n * width, so a deep enough branch always runs out of screen — and a menu opened\n * from a right-hand taskbar runs out on the very first one.\n *\n * `preferLeft` keeps a branch that has already turned around going the same\n * way. Without it the levels ping-pong: one flips left over its grandparent,\n * the next finds room on the right again and lands back on top of its\n * grandparent, and the chain folds into two stacks of panels.\n */\nexport function menuPanelLeft(\n ownerLeft: number,\n ownerRight: number,\n width: number,\n viewportW: number,\n gap: number,\n preferLeft = false,\n margin = 8,\n): { left: number; flipped: boolean } {\n const onRight = ownerRight + gap;\n const onLeft = ownerLeft - gap - width;\n const rightFits = onRight + width <= viewportW - margin;\n const leftFits = onLeft >= margin;\n if (preferLeft && leftFits) return { left: onLeft, flipped: true };\n if (rightFits) return { left: onRight, flipped: false };\n if (leftFits) return { left: onLeft, flipped: true };\n // Neither side fits: hug the edge the panel was heading for rather than\n // hang off the screen.\n return preferLeft\n ? { left: margin, flipped: true }\n : { left: Math.max(margin, viewportW - margin - width), flipped: false };\n}\n","import {\n createContext, forwardRef, useCallback, useContext, useEffect, useId, useMemo, useRef, useState,\n type ReactNode, type CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { glassStyle, GLASS_DIVIDER } from '../utils/glass';\nimport { keepsNativeMenu } from './contextMenuTarget';\nimport { registerModalEscapeInterceptor } from './escapeInterceptors';\nimport { onOverlayOpen } from './overlayEvents';\nimport { useBrowserLayoutEffect, useHydrated } from './hydration';\nimport { Z_LAYERS } from './zLayers';\nimport { clampMenuTop, closeMenuBelow, menuPanelLeft, openMenuLevel, type MenuAnchor } from './menuPath';\n\n/**\n * Unified popup menu component — used for all context menus, dropdowns, and flyouts.\n * Reads --menu-density CSS variable: 'tight', 'normal' (default), or 'large'.\n */\n\nexport interface PopupMenuProps {\n children: ReactNode;\n style?: CSSProperties;\n className?: string;\n onClose?: () => void;\n minWidth?: number;\n /** Render into document.body instead of in place. Required when the menu\n * is opened from INSIDE a window: the window panel is a transformed /\n * backdrop-filtered `overflow-hidden` container, which both re-anchors\n * `position: fixed` descendants to itself and clips them — a menu\n * positioned at viewport coordinates ends up offset or invisible.\n * Portaling restores true viewport positioning. */\n portal?: boolean;\n}\n\n/** Guarded like `glassStyle`'s reads: a server has no stylesheet to ask, and a\n * menu rendered there takes the density the variable defaults to. */\nfunction getDensity(): 'tight' | 'normal' | 'large' {\n try {\n return (getComputedStyle(document.documentElement).getPropertyValue('--menu-density')?.trim() as any) || 'normal';\n } catch {\n return 'normal';\n }\n}\n\n/** Grace period before a submenu closes, so the pointer can cut the corner\n * across sibling rows on its way into the panel. The Start menu's value. */\nconst SUBMENU_CLOSE_DELAY = 200;\n/** How long the pointer rests on a submenu row before it opens. Short enough\n * to feel immediate, long enough that sweeping down a menu past the row does\n * not flash its panel open. */\nconst SUBMENU_OPEN_DELAY = 120;\n/** Gap between a submenu and the menu it opened from. */\nconst SUBMENU_GAP = 4;\n\n/**\n * What every submenu under one `PopupMenu` shares.\n *\n * The open submenus are ONE path for the whole tree — the same state machine\n * the Start menu runs (`menuPath.ts`): `path[d]` is the submenu open from a row\n * in panel `d`, and there is one close timer. That is what makes sibling\n * submenus exclusive, and it is what stops a stale timer from a row the\n * pointer merely passed over from closing a submenu that has since opened —\n * the bug the Start menu had while it ran a timer per level.\n */\ninterface MenuTree {\n /** Marks every panel of this tree, so a click inside a portalled submenu is\n * not \"outside\" the menu. */\n id: string;\n /** 0 for rows in the root menu, 1 inside its submenu, and so on. */\n depth: number;\n path: MenuAnchor[];\n open: (depth: number, anchor: MenuAnchor) => void;\n /** Close the submenus under panel `depth`, after the grace period. */\n scheduleClose: (depth: number) => void;\n /** Close the submenus under panel `depth` now. */\n closeBelow: (depth: number) => void;\n cancelClose: () => void;\n /** Close every panel — an item inside a submenu was chosen. */\n closeTree: () => void;\n /** The root panel, for measuring what it is layered at. */\n root: React.RefObject<HTMLDivElement | null>;\n}\n\nconst MenuTreeContext = createContext<MenuTree | null>(null);\n\n/** Container for a popup menu — auto-clamps to stay within viewport */\nexport function PopupMenu({ children, style, className = '', onClose, minWidth = 180, portal = false }: PopupMenuProps) {\n const ref = useRef<HTMLDivElement>(null);\n const treeId = useId();\n const [path, setPath] = useState<MenuAnchor[]>([]);\n const closeTimer = useRef<ReturnType<typeof setTimeout>>();\n useEffect(() => () => clearTimeout(closeTimer.current), []);\n // A portal has no place in server HTML — there is no <body> to put it in —\n // so a portalled menu that is open on the first render waits for hydration\n // to end, and both sides agree that it is not there yet.\n const hydrated = useHydrated();\n\n const tree = useMemo<MenuTree>(() => {\n const cancelClose = () => clearTimeout(closeTimer.current);\n return {\n id: treeId,\n depth: 0,\n path,\n open: (depth, anchor) => { cancelClose(); setPath(prev => openMenuLevel(prev, depth, anchor)); },\n scheduleClose: depth => {\n cancelClose();\n closeTimer.current = setTimeout(() => setPath(prev => closeMenuBelow(prev, depth)), SUBMENU_CLOSE_DELAY);\n },\n closeBelow: depth => { cancelClose(); setPath(prev => closeMenuBelow(prev, depth)); },\n cancelClose,\n closeTree: () => { cancelClose(); setPath([]); onClose?.(); },\n root: ref,\n };\n }, [treeId, path, onClose]);\n\n useEffect(() => {\n if (!onClose) return;\n const handleClick = (e: PointerEvent | MouseEvent) => {\n if ((e.target as HTMLElement).closest('[data-menu-toggle]')) return;\n // A submenu is portalled to <body>, so it is outside `ref` in the DOM\n // while still being part of this menu.\n if ((e.target as HTMLElement).closest?.(`[data-popup-menu-tree=\"${treeId}\"]`)) return;\n if (ref.current && !ref.current.contains(e.target as Node)) onClose();\n };\n const handleKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); };\n window.addEventListener('pointerdown', handleClick);\n window.addEventListener('keydown', handleKey);\n // An overlay opening (⌘K's palette, a Dialog) is an outside interaction\n // the pointer listener never sees; left open, the menu would sit over the\n // overlay's backdrop or behind it (see `overlayEvents.ts`).\n const offOverlay = onOverlayOpen(onClose);\n return () => {\n window.removeEventListener('pointerdown', handleClick);\n window.removeEventListener('keydown', handleKey);\n offOverlay();\n };\n }, [onClose, treeId]);\n\n // After render, clamp position to viewport boundaries\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n requestAnimationFrame(() => {\n const rect = el.getBoundingClientRect();\n const margin = 8;\n // Clamp right edge\n if (rect.right > window.innerWidth - margin) {\n const overflow = rect.right - window.innerWidth + margin;\n if (el.style.left) {\n el.style.left = `${parseFloat(el.style.left) - overflow}px`;\n } else if (!el.style.right) {\n el.style.left = `${rect.left - overflow}px`;\n }\n }\n // Clamp left edge\n if (rect.left < margin) {\n if (el.style.left) {\n el.style.left = `${margin}px`;\n } else if (el.style.right) {\n el.style.right = `${window.innerWidth - rect.width - margin}px`;\n }\n }\n // Clamp bottom edge\n if (rect.bottom > window.innerHeight - margin) {\n const overflow = rect.bottom - window.innerHeight + margin;\n if (el.style.top) {\n el.style.top = `${parseFloat(el.style.top) - overflow}px`;\n } else if (el.style.bottom) {\n // Already anchored to bottom, just clamp\n el.style.bottom = `${margin}px`;\n } else {\n el.style.top = `${rect.top - overflow}px`;\n }\n }\n // Clamp top edge\n if (rect.top < margin) {\n if (el.style.top) {\n el.style.top = `${margin}px`;\n } else if (el.style.bottom) {\n el.style.bottom = `${window.innerHeight - rect.height - margin}px`;\n }\n }\n });\n });\n\n const density = getDensity();\n const ownsZ = /(^|[\\s:])!?z-/.test(className);\n\n // A right-click on an open menu is not a request for another menu. Claiming\n // it stops the shell-wide `ShellContextMenu` (which stands down on a\n // prevented event) and the browser's from opening on top of this one. A text\n // field inside a menu still keeps the browser's, as it does everywhere else.\n const menu = (\n <div ref={ref}\n data-popup-menu-tree={treeId}\n className={`fixed rounded-2xl ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'} ${className}`}\n onContextMenu={e => { if (!keepsNativeMenu(e.target)) e.preventDefault(); }}\n // The menu layer is inline now, and an inline z-index beats any class —\n // so a caller that layered its menu with a `z-…` class keeps its own.\n // `popup-in` (and the submenus' keyframes) are declared in ui.css: a\n // `<style>` element here was refused by a strict style-src.\n style={{ ...(ownsZ ? {} : { zIndex: Z_LAYERS.menu }), minWidth, animation: 'popup-in 0.12s ease-out', ...glassStyle(), ...style }}>\n <MenuTreeContext.Provider value={tree}>{children}</MenuTreeContext.Provider>\n </div>\n );\n\n if (!portal) return menu;\n return hydrated ? createPortal(menu, document.body) : null;\n}\n\n/** A clickable menu item */\nexport interface PopupMenuItemProps {\n onClick?: () => void;\n children: ReactNode;\n className?: string;\n danger?: boolean;\n disabled?: boolean;\n /**\n * The menu semantics, when this item is part of one.\n *\n * A context menu on the desktop is a list of buttons and needs none of this;\n * a dropdown attached to a trigger is a `menu` whose items are `menuitem`s\n * with one tab stop between them. Both draw the same item, so the styling\n * lives here rather than being copied into whichever one is newer.\n */\n role?: 'menuitem';\n tabIndex?: number;\n id?: string;\n onMouseEnter?: () => void;\n}\n\n/**\n * `forwardRef`, not a `ref` prop.\n *\n * Taking `ref` as an ordinary prop is React 19 only. This package's peer range\n * is `react: \">=18\"`, and React 18 strips `ref` from a function component's\n * props and warns — so on an 18 consumer the ref would silently never attach,\n * `DropdownMenu`'s `itemRefs` would all be null, and its arrow keys would move\n * `tabIndex` while DOM focus stayed put. A keyboard-dead menu with no error.\n */\n/** The row look shared by an item and a submenu's row. */\nfunction itemLook(danger: boolean | undefined, disabled: boolean | undefined, className: string, highlighted = false) {\n const density = getDensity();\n // Vertical gap between items. `normal` sits a little tighter than the raw\n // size padding; `large` adds a bit more room. Floored at the tight value so\n // the reduced `normal` never drops below `tight` at the smallest menu size.\n const itemPadY = density === 'tight' ? '0.25rem'\n : density === 'large' ? '0.6rem'\n : 'max(0.25rem, calc(var(--menu-padding-y, 0.5rem) - 0.1rem))';\n const tone = danger ? 'text-red-600 hover:bg-red-50'\n : highlighted ? 'bg-blue-50 text-blue-700'\n : 'text-gray-700 hover:bg-blue-50 hover:text-blue-700';\n return {\n className: `w-full flex items-center gap-2 text-left transition-colors rounded-lg mx-auto\n ${tone}\n ${disabled ? 'opacity-40 cursor-not-allowed' : ''}\n ${className}`,\n style: {\n width: 'calc(100% - 8px)',\n marginLeft: 4,\n marginRight: 4,\n fontSize: 'var(--menu-font-size, 14px)',\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: itemPadY,\n paddingBottom: itemPadY,\n } satisfies CSSProperties,\n };\n}\n\nexport const PopupMenuItem = forwardRef<HTMLButtonElement, PopupMenuItemProps>(function PopupMenuItem({\n onClick, children, className = '', danger, disabled,\n role, tabIndex, id, onMouseEnter,\n}, ref) {\n const tree = useContext(MenuTreeContext);\n const look = itemLook(danger, disabled, className);\n return (\n <button\n ref={ref}\n id={id}\n role={role}\n tabIndex={tabIndex}\n onClick={() => {\n onClick?.();\n // Inside a submenu, choosing an item is the end of the whole menu —\n // not just the panel it sat in. A root item keeps its old contract:\n // the caller's own onClick decides.\n if (tree && tree.depth > 0) tree.closeTree();\n }}\n onMouseEnter={() => {\n // Resting on a plain row retires any submenu open beside it, after the\n // same grace the Start menu gives.\n tree?.scheduleClose(tree.depth);\n onMouseEnter?.();\n }}\n disabled={disabled}\n className={look.className}\n style={look.style}\n >\n {children}\n </button>\n );\n});\n\nexport interface PopupSubmenuProps {\n /** The row's text. */\n label: ReactNode;\n /** Drawn before the label, as in a `PopupMenuItem`. */\n icon?: ReactNode;\n /** The submenu's contents — `PopupMenuItem`, `PopupMenuLabel`,\n * `PopupMenuDivider`, or another `PopupSubmenu`. */\n children: ReactNode;\n disabled?: boolean;\n className?: string;\n /** The submenu panel's minimum width. */\n minWidth?: number;\n /** The tallest the panel may grow before its items scroll — a long target\n * list, say `'min(60vh, 420px)'`. Never taller than the viewport less its\n * 8px gutters, whatever is passed. */\n maxHeight?: number | string;\n}\n\nconst chevron = (\n <svg aria-hidden className=\"h-3.5 w-3.5 shrink-0 ml-auto text-gray-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" />\n </svg>\n);\n\n/** The z-index a panel must beat: the outermost stacking level `el` sits in\n * at <body>, since a portalled submenu is compared against that, not against\n * whatever the root menu is nested inside. */\nfunction layerOf(el: HTMLElement | null): number {\n let z: number = Z_LAYERS.menu;\n for (let node = el; node && node !== document.body; node = node.parentElement) {\n const value = parseInt(getComputedStyle(node).zIndex, 10);\n if (!Number.isNaN(value)) z = value;\n }\n return z;\n}\n\n/** Enabled items of a panel, in order — what the arrow keys walk. */\nfunction focusables(panel: HTMLElement | null): HTMLElement[] {\n if (!panel) return [];\n return [...panel.querySelectorAll<HTMLElement>('button:not([disabled])')]\n .filter(b => b.closest('[data-popup-submenu-panel]') === panel);\n}\n\n/**\n * A row in a `PopupMenu` that opens a nested menu.\n *\n * Opens on hover after a short rest, and at once on click, Enter, Space or\n * ArrowRight (which also move focus to its first item). ArrowLeft and Escape\n * close it and give focus back to the row. It opens to the right of the menu,\n * or to the left when there is no room, and moves up when it would run off the\n * bottom — the Start menu's placement rules, from `menuPath.ts`. Choosing an\n * item inside closes the whole menu (the root `PopupMenu`'s `onClose`).\n */\nexport function PopupSubmenu({ label, icon, children, disabled, className = '', minWidth = 180, maxHeight }: PopupSubmenuProps) {\n const tree = useContext(MenuTreeContext);\n const key = useId();\n const rowRef = useRef<HTMLButtonElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const openTimer = useRef<ReturnType<typeof setTimeout>>();\n /** Whether the current opening came from the keyboard — then focus moves in. */\n const focusOnOpen = useRef(false);\n const panelId = `${key}-menu`;\n useEffect(() => () => clearTimeout(openTimer.current), []);\n\n const depth = tree?.depth ?? 0;\n const anchor = tree?.path[depth];\n const isOpen = !!anchor && anchor.key === key && !disabled;\n\n const openNow = useCallback((withFocus: boolean) => {\n if (!tree || disabled || !rowRef.current) return;\n clearTimeout(openTimer.current);\n focusOnOpen.current = withFocus;\n const row = rowRef.current.getBoundingClientRect();\n const owner = (rowRef.current.closest('[data-popup-submenu-panel]')\n ?? rowRef.current.closest('[data-popup-menu-tree]')) as HTMLElement | null;\n const ownerRect = owner?.getBoundingClientRect() ?? row;\n const preferLeft = depth > 0 && (tree.path[depth - 1]?.flipped ?? false);\n const place = menuPanelLeft(ownerRect.left, ownerRect.right, minWidth, window.innerWidth, SUBMENU_GAP, preferLeft);\n tree.open(depth, { key, y: row.top, ...place });\n }, [tree, disabled, depth, key, minWidth]);\n\n const closeSelf = (restoreFocus: boolean) => {\n tree?.closeBelow(depth);\n if (restoreFocus) rowRef.current?.focus();\n };\n\n // Escape through the shell's interceptor seam: inside a window, `Modal`\n // takes Escape in the capture phase and would close the whole window.\n useEffect(() => {\n if (!isOpen) return;\n return registerModalEscapeInterceptor(e => {\n if (e.key !== 'Escape') return false;\n // Registration order cannot say which submenu is deepest: React runs a\n // parent's effects after its children's, so once both re-render the\n // outer submenu is the newest registrant and is asked first. Defer to\n // the open path instead — a deeper level open under this one takes it.\n if (tree && tree.path.length > depth + 1) return false;\n closeSelf(true);\n return true;\n });\n });\n\n // Place the panel against its real size before paint — the estimate used\n // `minWidth` and the row's top — then move focus in if a key opened it.\n useBrowserLayoutEffect(() => {\n if (!isOpen || !anchor) return;\n const place = () => {\n const panel = panelRef.current;\n if (!panel || !rowRef.current) return;\n const margin = 8;\n const owner = (rowRef.current.closest('[data-popup-submenu-panel]')\n ?? rowRef.current.closest('[data-popup-menu-tree]')) as HTMLElement | null;\n const ownerRect = owner?.getBoundingClientRect() ?? rowRef.current.getBoundingClientRect();\n const preferLeft = depth > 0 && (tree?.path[depth - 1]?.flipped ?? false);\n const width = panel.offsetWidth || minWidth;\n const spot = menuPanelLeft(ownerRect.left, ownerRect.right, width, window.innerWidth, SUBMENU_GAP, preferLeft, margin);\n const height = panel.offsetHeight;\n // Top-aligned with the row: centring the clamp on `row top + half the\n // panel` gives exactly that, and still moves the panel up at the bottom.\n const rowTop = rowRef.current.getBoundingClientRect().top;\n const top = clampMenuTop(rowTop + height / 2, height, margin, window.innerHeight - margin);\n panel.style.left = `${spot.left}px`;\n panel.style.top = `${top}px`;\n panel.dataset.flipped = String(spot.flipped);\n };\n place();\n // Again after the root menu's own viewport clamp, which runs in a frame\n // AFTER this effect: on a re-render it can still move the root a few\n // pixels, and the submenu has to follow the menu it hangs off.\n let second = 0;\n const first = requestAnimationFrame(() => { second = requestAnimationFrame(place); });\n return () => { cancelAnimationFrame(first); cancelAnimationFrame(second); };\n });\n\n useEffect(() => {\n if (!isOpen || !focusOnOpen.current) return;\n focusOnOpen.current = false;\n focusables(panelRef.current)[0]?.focus();\n }, [isOpen]);\n\n const childTree = useMemo<MenuTree | null>(() => tree && { ...tree, depth: depth + 1 }, [tree, depth]);\n\n if (!tree) throw new Error('PopupSubmenu must be rendered inside a PopupMenu');\n\n const onRowKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'ArrowRight') {\n e.preventDefault();\n e.stopPropagation();\n openNow(true);\n }\n };\n\n const onPanelKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n // React bubbles through portals, so a key pressed in a deeper submenu\n // reaches this handler too. That panel handled it already.\n if ((e.target as HTMLElement).closest('[data-popup-submenu-panel]') !== e.currentTarget) return;\n const items = focusables(panelRef.current);\n const at = items.indexOf(document.activeElement as HTMLElement);\n const move = (to: number) => { e.preventDefault(); e.stopPropagation(); items[(to + items.length) % items.length]?.focus(); };\n if (e.key === 'ArrowDown') move(at + 1);\n else if (e.key === 'ArrowUp') move(at < 0 ? items.length - 1 : at - 1);\n else if (e.key === 'Home') move(0);\n else if (e.key === 'End') move(items.length - 1);\n else if (e.key === 'ArrowLeft') { e.preventDefault(); e.stopPropagation(); closeSelf(true); }\n };\n\n const look = itemLook(false, disabled, className, isOpen);\n const density = getDensity();\n\n return (\n <>\n <button\n ref={rowRef}\n type=\"button\"\n role=\"menuitem\"\n aria-haspopup=\"menu\"\n aria-expanded={isOpen}\n aria-controls={isOpen ? panelId : undefined}\n disabled={disabled}\n data-popup-submenu-row\n className={look.className}\n style={look.style}\n onClick={e => {\n // `detail` is 0 for a click the keyboard made (Enter, Space).\n const byKey = e.detail === 0;\n if (!isOpen) openNow(byKey);\n else if (byKey) focusables(panelRef.current)[0]?.focus();\n }}\n onKeyDown={onRowKeyDown}\n onMouseEnter={() => {\n if (disabled) return;\n tree.cancelClose();\n if (isOpen) { tree.closeBelow(depth + 1); return; }\n clearTimeout(openTimer.current);\n openTimer.current = setTimeout(() => openNow(false), SUBMENU_OPEN_DELAY);\n }}\n onMouseLeave={() => {\n clearTimeout(openTimer.current);\n tree.scheduleClose(depth);\n }}\n >\n {icon}\n <span className=\"min-w-0 flex-1 truncate\">{label}</span>\n {chevron}\n </button>\n {isOpen && createPortal(\n <div\n ref={panelRef}\n id={panelId}\n role=\"menu\"\n data-popup-menu-tree={tree.id}\n data-popup-submenu-panel\n className={`fixed rounded-2xl overflow-x-hidden overflow-y-auto overscroll-contain ${density === 'tight' ? 'py-1' : density === 'large' ? 'py-2' : 'py-1.5'}`}\n style={{\n left: anchor!.left, top: anchor!.y, minWidth,\n // The viewport cap always applies, so a long list scrolls rather\n // than running off the screen even when no maxHeight is passed.\n maxHeight: maxHeight === undefined\n ? 'calc(100vh - 16px)'\n : `min(${typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight}, calc(100vh - 16px))`,\n zIndex: layerOf(tree.root.current) + depth + 1,\n animation: `${anchor!.flipped ? 'popup-submenu-in-left' : 'popup-submenu-in'} 0.1s ease-out`,\n ...glassStyle(),\n }}\n onKeyDown={onPanelKeyDown}\n onMouseEnter={() => { tree.cancelClose(); clearTimeout(openTimer.current); }}\n onMouseLeave={() => tree.scheduleClose(depth + 1)}\n onContextMenu={e => { if (!keepsNativeMenu(e.target)) e.preventDefault(); }}\n >\n <MenuTreeContext.Provider value={childTree}>{children}</MenuTreeContext.Provider>\n </div>,\n document.body,\n )}\n </>\n );\n}\n\n/** A divider line between menu items */\nexport function PopupMenuDivider() {\n const density = getDensity();\n return <div className={`border-t ${GLASS_DIVIDER} ${density === 'tight' ? 'my-0.5' : density === 'large' ? 'my-1.5' : 'my-1'} mx-3`} />;\n}\n\n/** A section header label */\nexport function PopupMenuLabel({ children }: { children: ReactNode }) {\n const density = getDensity();\n const labelPadY = density === 'tight' ? '0.125rem' : density === 'large' ? '0.375rem' : '0.25rem';\n return (\n <div\n className=\"text-[10px] font-medium text-gray-400 uppercase tracking-wider\"\n style={{\n paddingLeft: 'var(--menu-padding-x, 1rem)',\n paddingRight: 'var(--menu-padding-x, 1rem)',\n paddingTop: labelPadY,\n paddingBottom: labelPadY,\n }}\n >\n {children}\n </div>\n );\n}\n\n/** An icon helper */\nexport function MenuIcon({ d, className = 'h-4 w-4 text-gray-400' }: { d: string; className?: string }) {\n return (\n <svg className={className} fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d={d} />\n </svg>\n );\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { toast_default } from './chunk-
|
|
1
|
+
import { toast_default } from './chunk-4P44JXDS.js';
|
|
2
2
|
import { forwardRef, useRef, useState, useMemo, useEffect, useImperativeHandle } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -1099,5 +1099,5 @@ function applyMosaic(ctx, rect) {
|
|
|
1099
1099
|
}
|
|
1100
1100
|
|
|
1101
1101
|
export { ImageAnnotator_default };
|
|
1102
|
-
//# sourceMappingURL=chunk-
|
|
1103
|
-
//# sourceMappingURL=chunk-
|
|
1102
|
+
//# sourceMappingURL=chunk-QSKCQUZE.js.map
|
|
1103
|
+
//# sourceMappingURL=chunk-QSKCQUZE.js.map
|