@ecoma-io/loom 0.1.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/LICENSE +202 -0
- package/README.md +180 -0
- package/dist/a11y.d.ts +2 -0
- package/dist/a11y.d.ts.map +1 -0
- package/dist/a11y.js +2 -0
- package/dist/blocks/AppHeader/AppHeader.js +7 -0
- package/dist/blocks/AppHeader/AppHeader.js.map +1 -0
- package/dist/blocks/AppHeader/AppHeader.vue.d.ts +83 -0
- package/dist/blocks/AppHeader/AppHeader.vue.d.ts.map +1 -0
- package/dist/blocks/AppHeader/AppHeader.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/blocks/AppHeader/AppHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/DashboardGrid/DashboardGrid.js +7 -0
- package/dist/blocks/DashboardGrid/DashboardGrid.js.map +1 -0
- package/dist/blocks/DashboardGrid/DashboardGrid.vue.d.ts +42 -0
- package/dist/blocks/DashboardGrid/DashboardGrid.vue.d.ts.map +1 -0
- package/dist/blocks/DashboardGrid/DashboardGrid.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/blocks/DashboardGrid/DashboardGrid.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/EmptyState/EmptyState.js +7 -0
- package/dist/blocks/EmptyState/EmptyState.js.map +1 -0
- package/dist/blocks/EmptyState/EmptyState.vue.d.ts +50 -0
- package/dist/blocks/EmptyState/EmptyState.vue.d.ts.map +1 -0
- package/dist/blocks/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/blocks/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/PageHeader/PageHeader.js +7 -0
- package/dist/blocks/PageHeader/PageHeader.js.map +1 -0
- package/dist/blocks/PageHeader/PageHeader.vue.d.ts +54 -0
- package/dist/blocks/PageHeader/PageHeader.vue.d.ts.map +1 -0
- package/dist/blocks/PageHeader/PageHeader.vue_vue_type_script_setup_true_lang.js +68 -0
- package/dist/blocks/PageHeader/PageHeader.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/RowActions/RowActions.js +7 -0
- package/dist/blocks/RowActions/RowActions.js.map +1 -0
- package/dist/blocks/RowActions/RowActions.vue.d.ts +14 -0
- package/dist/blocks/RowActions/RowActions.vue.d.ts.map +1 -0
- package/dist/blocks/RowActions/RowActions.vue_vue_type_script_setup_true_lang.js +47 -0
- package/dist/blocks/RowActions/RowActions.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/SidebarNav/SidebarNav.js +7 -0
- package/dist/blocks/SidebarNav/SidebarNav.js.map +1 -0
- package/dist/blocks/SidebarNav/SidebarNav.vue.d.ts +40 -0
- package/dist/blocks/SidebarNav/SidebarNav.vue.d.ts.map +1 -0
- package/dist/blocks/SidebarNav/SidebarNav.vue_vue_type_script_setup_true_lang.js +125 -0
- package/dist/blocks/SidebarNav/SidebarNav.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/TitleBar/TitleBar.js +7 -0
- package/dist/blocks/TitleBar/TitleBar.js.map +1 -0
- package/dist/blocks/TitleBar/TitleBar.vue.d.ts +49 -0
- package/dist/blocks/TitleBar/TitleBar.vue.d.ts.map +1 -0
- package/dist/blocks/TitleBar/TitleBar.vue_vue_type_script_setup_true_lang.js +66 -0
- package/dist/blocks/TitleBar/TitleBar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/blocks/ToastStack/ToastStack.js +7 -0
- package/dist/blocks/ToastStack/ToastStack.js.map +1 -0
- package/dist/blocks/ToastStack/ToastStack.vue.d.ts +24 -0
- package/dist/blocks/ToastStack/ToastStack.vue.d.ts.map +1 -0
- package/dist/blocks/ToastStack/ToastStack.vue_vue_type_script_setup_true_lang.js +38 -0
- package/dist/blocks/ToastStack/ToastStack.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/icons/BrandMark.d.ts +18 -0
- package/dist/icons/BrandMark.d.ts.map +1 -0
- package/dist/icons/BrandMark.js +47 -0
- package/dist/icons/BrandMark.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/lib/a11y-scope.d.ts +19 -0
- package/dist/lib/a11y-scope.d.ts.map +1 -0
- package/dist/lib/a11y-scope.js +28 -0
- package/dist/lib/a11y-scope.js.map +1 -0
- package/dist/lib/attrs.d.ts +18 -0
- package/dist/lib/attrs.d.ts.map +1 -0
- package/dist/lib/attrs.js +28 -0
- package/dist/lib/attrs.js.map +1 -0
- package/dist/lib/cn.d.ts +9 -0
- package/dist/lib/cn.d.ts.map +1 -0
- package/dist/lib/cn.js +16 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/icon-defaults.d.ts +22 -0
- package/dist/lib/icon-defaults.d.ts.map +1 -0
- package/dist/lib/icon-defaults.js +32 -0
- package/dist/lib/icon-defaults.js.map +1 -0
- package/dist/lib/motion.d.ts +14 -0
- package/dist/lib/motion.d.ts.map +1 -0
- package/dist/lib/motion.js +20 -0
- package/dist/lib/motion.js.map +1 -0
- package/dist/lib/props.d.ts +31 -0
- package/dist/lib/props.d.ts.map +1 -0
- package/dist/lib/props.js +37 -0
- package/dist/lib/props.js.map +1 -0
- package/dist/primitives/Avatar/Avatar.js +7 -0
- package/dist/primitives/Avatar/Avatar.js.map +1 -0
- package/dist/primitives/Avatar/Avatar.vue.d.ts +27 -0
- package/dist/primitives/Avatar/Avatar.vue.d.ts.map +1 -0
- package/dist/primitives/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js +33 -0
- package/dist/primitives/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Badge/Badge.js +7 -0
- package/dist/primitives/Badge/Badge.js.map +1 -0
- package/dist/primitives/Badge/Badge.vue.d.ts +27 -0
- package/dist/primitives/Badge/Badge.vue.d.ts.map +1 -0
- package/dist/primitives/Badge/Badge.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/primitives/Badge/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Button/Button.js +7 -0
- package/dist/primitives/Button/Button.js.map +1 -0
- package/dist/primitives/Button/Button.vue.d.ts +70 -0
- package/dist/primitives/Button/Button.vue.d.ts.map +1 -0
- package/dist/primitives/Button/Button.vue_vue_type_script_setup_true_lang.js +122 -0
- package/dist/primitives/Button/Button.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Checkbox/Checkbox.js +7 -0
- package/dist/primitives/Checkbox/Checkbox.js.map +1 -0
- package/dist/primitives/Checkbox/Checkbox.vue.d.ts +34 -0
- package/dist/primitives/Checkbox/Checkbox.vue.d.ts.map +1 -0
- package/dist/primitives/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js +94 -0
- package/dist/primitives/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Dialog/Dialog.js +7 -0
- package/dist/primitives/Dialog/Dialog.js.map +1 -0
- package/dist/primitives/Dialog/Dialog.vue.d.ts +58 -0
- package/dist/primitives/Dialog/Dialog.vue.d.ts.map +1 -0
- package/dist/primitives/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js +88 -0
- package/dist/primitives/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/DropdownMenu/DropdownMenu.js +7 -0
- package/dist/primitives/DropdownMenu/DropdownMenu.js.map +1 -0
- package/dist/primitives/DropdownMenu/DropdownMenu.vue.d.ts +55 -0
- package/dist/primitives/DropdownMenu/DropdownMenu.vue.d.ts.map +1 -0
- package/dist/primitives/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js +82 -0
- package/dist/primitives/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Field/Field.js +7 -0
- package/dist/primitives/Field/Field.js.map +1 -0
- package/dist/primitives/Field/Field.vue.d.ts +53 -0
- package/dist/primitives/Field/Field.vue.d.ts.map +1 -0
- package/dist/primitives/Field/Field.vue_vue_type_script_setup_true_lang.js +84 -0
- package/dist/primitives/Field/Field.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/InlineError/InlineError.js +7 -0
- package/dist/primitives/InlineError/InlineError.js.map +1 -0
- package/dist/primitives/InlineError/InlineError.vue.d.ts +32 -0
- package/dist/primitives/InlineError/InlineError.vue.d.ts.map +1 -0
- package/dist/primitives/InlineError/InlineError.vue_vue_type_script_setup_true_lang.js +44 -0
- package/dist/primitives/InlineError/InlineError.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Menubar/Menubar.js +7 -0
- package/dist/primitives/Menubar/Menubar.js.map +1 -0
- package/dist/primitives/Menubar/Menubar.vue.d.ts +41 -0
- package/dist/primitives/Menubar/Menubar.vue.d.ts.map +1 -0
- package/dist/primitives/Menubar/Menubar.vue_vue_type_script_setup_true_lang.js +184 -0
- package/dist/primitives/Menubar/Menubar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/NumberField/NumberField.js +7 -0
- package/dist/primitives/NumberField/NumberField.js.map +1 -0
- package/dist/primitives/NumberField/NumberField.vue.d.ts +47 -0
- package/dist/primitives/NumberField/NumberField.vue.d.ts.map +1 -0
- package/dist/primitives/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js +204 -0
- package/dist/primitives/NumberField/NumberField.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Popover/Popover.js +7 -0
- package/dist/primitives/Popover/Popover.js.map +1 -0
- package/dist/primitives/Popover/Popover.vue.d.ts +61 -0
- package/dist/primitives/Popover/Popover.vue.d.ts.map +1 -0
- package/dist/primitives/Popover/Popover.vue_vue_type_script_setup_true_lang.js +59 -0
- package/dist/primitives/Popover/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Progress/Progress.js +7 -0
- package/dist/primitives/Progress/Progress.js.map +1 -0
- package/dist/primitives/Progress/Progress.vue.d.ts +30 -0
- package/dist/primitives/Progress/Progress.vue.d.ts.map +1 -0
- package/dist/primitives/Progress/Progress.vue_vue_type_script_setup_true_lang.js +67 -0
- package/dist/primitives/Progress/Progress.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/RadioGroup/RadioGroup.js +7 -0
- package/dist/primitives/RadioGroup/RadioGroup.js.map +1 -0
- package/dist/primitives/RadioGroup/RadioGroup.vue.d.ts +39 -0
- package/dist/primitives/RadioGroup/RadioGroup.vue.d.ts.map +1 -0
- package/dist/primitives/RadioGroup/RadioGroup.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/primitives/RadioGroup/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/SegmentedControl/SegmentedControl.js +7 -0
- package/dist/primitives/SegmentedControl/SegmentedControl.js.map +1 -0
- package/dist/primitives/SegmentedControl/SegmentedControl.vue.d.ts +40 -0
- package/dist/primitives/SegmentedControl/SegmentedControl.vue.d.ts.map +1 -0
- package/dist/primitives/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js +89 -0
- package/dist/primitives/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Select/Select.js +7 -0
- package/dist/primitives/Select/Select.js.map +1 -0
- package/dist/primitives/Select/Select.vue.d.ts +56 -0
- package/dist/primitives/Select/Select.vue.d.ts.map +1 -0
- package/dist/primitives/Select/Select.vue_vue_type_script_setup_true_lang.js +122 -0
- package/dist/primitives/Select/Select.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Separator/Separator.js +7 -0
- package/dist/primitives/Separator/Separator.js.map +1 -0
- package/dist/primitives/Separator/Separator.vue.d.ts +19 -0
- package/dist/primitives/Separator/Separator.vue.d.ts.map +1 -0
- package/dist/primitives/Separator/Separator.vue_vue_type_script_setup_true_lang.js +31 -0
- package/dist/primitives/Separator/Separator.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Skeleton/Skeleton.js +7 -0
- package/dist/primitives/Skeleton/Skeleton.js.map +1 -0
- package/dist/primitives/Skeleton/Skeleton.vue.d.ts +16 -0
- package/dist/primitives/Skeleton/Skeleton.vue.d.ts.map +1 -0
- package/dist/primitives/Skeleton/Skeleton.vue_vue_type_script_setup_true_lang.js +37 -0
- package/dist/primitives/Skeleton/Skeleton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Slider/Slider.js +7 -0
- package/dist/primitives/Slider/Slider.js.map +1 -0
- package/dist/primitives/Slider/Slider.vue.d.ts +58 -0
- package/dist/primitives/Slider/Slider.vue.d.ts.map +1 -0
- package/dist/primitives/Slider/Slider.vue_vue_type_script_setup_true_lang.js +137 -0
- package/dist/primitives/Slider/Slider.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Spinner/Spinner.js +7 -0
- package/dist/primitives/Spinner/Spinner.js.map +1 -0
- package/dist/primitives/Spinner/Spinner.vue.d.ts +21 -0
- package/dist/primitives/Spinner/Spinner.vue.d.ts.map +1 -0
- package/dist/primitives/Spinner/Spinner.vue_vue_type_script_setup_true_lang.js +63 -0
- package/dist/primitives/Spinner/Spinner.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Surface/Surface.js +7 -0
- package/dist/primitives/Surface/Surface.js.map +1 -0
- package/dist/primitives/Surface/Surface.vue.d.ts +42 -0
- package/dist/primitives/Surface/Surface.vue.d.ts.map +1 -0
- package/dist/primitives/Surface/Surface.vue_vue_type_script_setup_true_lang.js +48 -0
- package/dist/primitives/Surface/Surface.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Switch/Switch.js +7 -0
- package/dist/primitives/Switch/Switch.js.map +1 -0
- package/dist/primitives/Switch/Switch.vue.d.ts +24 -0
- package/dist/primitives/Switch/Switch.vue.d.ts.map +1 -0
- package/dist/primitives/Switch/Switch.vue_vue_type_script_setup_true_lang.js +44 -0
- package/dist/primitives/Switch/Switch.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Tabs/Tabs.js +7 -0
- package/dist/primitives/Tabs/Tabs.js.map +1 -0
- package/dist/primitives/Tabs/Tabs.vue.d.ts +33 -0
- package/dist/primitives/Tabs/Tabs.vue.d.ts.map +1 -0
- package/dist/primitives/Tabs/Tabs.vue_vue_type_script_setup_true_lang.js +54 -0
- package/dist/primitives/Tabs/Tabs.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/TextField/TextField.js +7 -0
- package/dist/primitives/TextField/TextField.js.map +1 -0
- package/dist/primitives/TextField/TextField.vue.d.ts +58 -0
- package/dist/primitives/TextField/TextField.vue.d.ts.map +1 -0
- package/dist/primitives/TextField/TextField.vue_vue_type_script_setup_true_lang.js +71 -0
- package/dist/primitives/TextField/TextField.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Textarea/Textarea.js +7 -0
- package/dist/primitives/Textarea/Textarea.js.map +1 -0
- package/dist/primitives/Textarea/Textarea.vue.d.ts +48 -0
- package/dist/primitives/Textarea/Textarea.vue.d.ts.map +1 -0
- package/dist/primitives/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js +54 -0
- package/dist/primitives/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Toast/Toast.js +7 -0
- package/dist/primitives/Toast/Toast.js.map +1 -0
- package/dist/primitives/Toast/Toast.vue.d.ts +46 -0
- package/dist/primitives/Toast/Toast.vue.d.ts.map +1 -0
- package/dist/primitives/Toast/Toast.vue_vue_type_script_setup_true_lang.js +52 -0
- package/dist/primitives/Toast/Toast.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Toast/ToastItem.js +7 -0
- package/dist/primitives/Toast/ToastItem.js.map +1 -0
- package/dist/primitives/Toast/ToastItem.vue.d.ts +38 -0
- package/dist/primitives/Toast/ToastItem.vue.d.ts.map +1 -0
- package/dist/primitives/Toast/ToastItem.vue_vue_type_script_setup_true_lang.js +114 -0
- package/dist/primitives/Toast/ToastItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/Tooltip/Tooltip.js +7 -0
- package/dist/primitives/Tooltip/Tooltip.js.map +1 -0
- package/dist/primitives/Tooltip/Tooltip.vue.d.ts +52 -0
- package/dist/primitives/Tooltip/Tooltip.vue.d.ts.map +1 -0
- package/dist/primitives/Tooltip/Tooltip.vue_vue_type_script_setup_true_lang.js +56 -0
- package/dist/primitives/Tooltip/Tooltip.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/primitives/WindowControls/WindowControls.js +7 -0
- package/dist/primitives/WindowControls/WindowControls.js.map +1 -0
- package/dist/primitives/WindowControls/WindowControls.vue.d.ts +41 -0
- package/dist/primitives/WindowControls/WindowControls.vue.d.ts.map +1 -0
- package/dist/primitives/WindowControls/WindowControls.vue_vue_type_script_setup_true_lang.js +111 -0
- package/dist/primitives/WindowControls/WindowControls.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/styles/fonts/Geist-Variable.woff2 +0 -0
- package/dist/styles/fonts/GeistMono-Variable.woff2 +0 -0
- package/dist/styles/fonts/OFL.txt +92 -0
- package/dist/styles/fonts.css +30 -0
- package/dist/styles/global.css +181 -0
- package/dist/styles/theme.css +212 -0
- package/package.json +123 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.js","names":[],"sources":["../../../src/primitives/DropdownMenu/DropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * One row of the menu, and the three kinds a menu is built from are told apart\n * by which fields are set rather than by a `kind` discriminator: a `separator`\n * is a rule, a `heading` is a non-interactive group label, and anything else\n * is a command.\n *\n * The list is data rather than markup because the primitive stays free of app\n * logic — selecting a row emits its `value` and the host decides what that\n * command means.\n */\nexport interface DropdownMenuEntry {\n /** The visible text of a command or a heading. */\n label?: string;\n /** The command id emitted on select. An entry without one is not selectable. */\n value?: string;\n /** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */\n shortcut?: string;\n /** Render a divider. Every other field is ignored. */\n separator?: boolean;\n /** Render a non-interactive section heading rather than a command. */\n heading?: boolean;\n /** A destructive command — painted in the destructive token so it is not one indistinguishable row. */\n danger?: boolean;\n /** Visible but inert: announced as disabled, and selecting it emits nothing. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n DropdownMenuRoot,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"reka-ui\";\nimport { cn } from \"../../lib/cn\";\nimport { listStaggerDelay } from \"../../lib/motion\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * DropdownMenu — one button that opens a list of commands, with roving focus,\n * typeahead and arrow-key navigation.\n *\n * The escape contract: opening moves focus into the menu and onto the first\n * command; Esc and an outside click both close it; closing returns focus to the\n * trigger. The menu is modal, so while it is open the page behind does not\n * scroll and the rest of the page is hidden from assistive technology — a\n * command list is never scrolled out from under the pointer mid-choice. Focus\n * stays in the menu while it is open, but not the way `Dialog` traps it: a menu\n * has no internal tab order at all. Tab is inert, and the arrow keys plus\n * typeahead are how a row is reached.\n *\n * Selecting an entry emits `select` with its `value`; the host maps that id to\n * an action, which is what keeps the primitive free of app logic.\n *\n * Reach for `Popover` when the panel is interactive content rather than a\n * command list, and for a select control when the list *is* a field's value.\n */\nwithDefaults(\n defineProps<{\n /** The rows, in order. Three kinds, told apart by which fields are set. */\n items: DropdownMenuEntry[];\n /** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */\n open?: boolean | undefined;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined },\n);\nconst emit = defineEmits<{ select: [value: string]; \"update:open\": [value: boolean] }>();\n\n/**\n * A disabled entry, and an entry with no command id, select nothing. Reka\n * already blocks pointer events on the disabled row; this is the second half,\n * for the keyboard path that reaches it anyway.\n */\nfunction choose(item: DropdownMenuEntry): void {\n if (item.disabled || item.value === undefined) return;\n emit(\"select\", item.value);\n}\n</script>\n\n<template>\n <DropdownMenuRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The button that opens the menu. Rendered `as-child`, so the\n caller's own element *is* the trigger. -->\n <DropdownMenuTrigger as-child>\n <slot name=\"trigger\" />\n </DropdownMenuTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuContent\n :side-offset=\"6\"\n align=\"start\"\n :class=\"\n cn(\n 'z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none',\n // Scoped to the open state, never unconditional. Reka's Presence\n // keeps closed content mounted until an animationend arrives, and a\n // mount-only animation never fires a second one — so an\n // unconditional animation class strands an invisible menu over the\n // page, catching clicks meant for what is behind it. Scoped, the\n // closed element computes `animation-name: none` and Presence\n // unmounts it at once. The origin below makes the menu grow out of\n // its trigger rather than out of its own centre.\n 'data-[state=open]:animate-scale-in',\n )\n \"\n style=\"transform-origin: var(--reka-popper-transform-origin)\"\n >\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <DropdownMenuSeparator v-if=\"item.separator\" class=\"my-1 h-px bg-border\" />\n <DropdownMenuLabel\n v-else-if=\"item.heading\"\n class=\"px-2 py-1.5 text-micro font-medium uppercase tracking-wide text-muted-foreground\"\n >\n {{ item.label }}\n </DropdownMenuLabel>\n <!-- The reveal delay comes from the shared stagger vocabulary, capped\n there so a long menu does not tail off, rather than from a\n per-menu number written by hand.\n\n `disabled` is coerced rather than forwarded raw: an entry that\n simply omits the field is not disabled, and Reka's own prop does\n not accept `undefined` as a way of saying so. -->\n <DropdownMenuItem\n v-else\n :disabled=\"item.disabled ?? false\"\n :class=\"\n cn(\n 'flex cursor-pointer items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none',\n 'transition-colors duration-fast ease-out',\n // The rows reveal in sequence as the menu opens, which reads as\n // a list arriving rather than a block appearing. The hover\n // colour transition above is a separate channel, so a row\n // highlighted mid-reveal still responds at once.\n 'animate-fade-rise',\n 'data-[highlighted]:bg-subtle',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-40',\n item.danger &&\n 'text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive',\n )\n \"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n @select=\"choose(item)\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-micro text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </DropdownMenuItem>\n </template>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenuRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One row of the menu, and the three kinds a menu is built from are told apart
|
|
3
|
+
* by which fields are set rather than by a `kind` discriminator: a `separator`
|
|
4
|
+
* is a rule, a `heading` is a non-interactive group label, and anything else
|
|
5
|
+
* is a command.
|
|
6
|
+
*
|
|
7
|
+
* The list is data rather than markup because the primitive stays free of app
|
|
8
|
+
* logic — selecting a row emits its `value` and the host decides what that
|
|
9
|
+
* command means.
|
|
10
|
+
*/
|
|
11
|
+
export interface DropdownMenuEntry {
|
|
12
|
+
/** The visible text of a command or a heading. */
|
|
13
|
+
label?: string;
|
|
14
|
+
/** The command id emitted on select. An entry without one is not selectable. */
|
|
15
|
+
value?: string;
|
|
16
|
+
/** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */
|
|
17
|
+
shortcut?: string;
|
|
18
|
+
/** Render a divider. Every other field is ignored. */
|
|
19
|
+
separator?: boolean;
|
|
20
|
+
/** Render a non-interactive section heading rather than a command. */
|
|
21
|
+
heading?: boolean;
|
|
22
|
+
/** A destructive command — painted in the destructive token so it is not one indistinguishable row. */
|
|
23
|
+
danger?: boolean;
|
|
24
|
+
/** Visible but inert: announced as disabled, and selecting it emits nothing. */
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare const _default: typeof __VLS_export;
|
|
28
|
+
export default _default;
|
|
29
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
30
|
+
/** The rows, in order. Three kinds, told apart by which fields are set. */
|
|
31
|
+
items: DropdownMenuEntry[];
|
|
32
|
+
/** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */
|
|
33
|
+
open?: boolean | undefined;
|
|
34
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
35
|
+
select: (value: string) => any;
|
|
36
|
+
"update:open": (value: boolean) => any;
|
|
37
|
+
}, string, import("vue").PublicProps, Readonly<{
|
|
38
|
+
/** The rows, in order. Three kinds, told apart by which fields are set. */
|
|
39
|
+
items: DropdownMenuEntry[];
|
|
40
|
+
/** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */
|
|
41
|
+
open?: boolean | undefined;
|
|
42
|
+
}> & Readonly<{
|
|
43
|
+
onSelect?: (value: string) => any;
|
|
44
|
+
"onUpdate:open"?: (value: boolean) => any;
|
|
45
|
+
}>, {
|
|
46
|
+
open: boolean | undefined;
|
|
47
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
48
|
+
trigger?: (props: {}) => any;
|
|
49
|
+
}>;
|
|
50
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
51
|
+
new (): {
|
|
52
|
+
$slots: S;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=DropdownMenu.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.vue.d.ts","sourceRoot":"","sources":["../../../src/primitives/DropdownMenu/DropdownMenu.vue"],"names":[],"mappings":"AAmLA;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uGAAuG;IACvG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;wBACoB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAqBd,2EAA2E;WACpE,iBAAiB,EAAE;IAC1B,iGAAiG;WAC1F,OAAO,GAAG,SAAS;;;;;IAH1B,2EAA2E;WACpE,iBAAiB,EAAE;IAC1B,iGAAiG;WAC1F,OAAO,GAAG,SAAS;;;;;UAAnB,OAAO,GAAG,SAAS;;cAqRhB,CAAC,KAAK,IAAiB,KAAK,GAAG;EAWzC,CAAC;AAWL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { listStaggerDelay } from "../../lib/motion.js";
|
|
3
|
+
import { optional } from "../../lib/props.js";
|
|
4
|
+
import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeProps, normalizeClass, normalizeStyle, openBlock, renderList, renderSlot, toDisplayString, unref, withCtx } from "vue";
|
|
5
|
+
import { DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuSeparator, DropdownMenuTrigger } from "reka-ui";
|
|
6
|
+
//#region src/primitives/DropdownMenu/DropdownMenu.vue?vue&type=script&setup=true&lang.ts
|
|
7
|
+
var _hoisted_1 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "tabular text-micro text-muted-foreground"
|
|
10
|
+
};
|
|
11
|
+
var DropdownMenu_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
12
|
+
__name: "DropdownMenu",
|
|
13
|
+
props: {
|
|
14
|
+
items: {},
|
|
15
|
+
open: {
|
|
16
|
+
type: Boolean,
|
|
17
|
+
default: void 0
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
emits: ["select", "update:open"],
|
|
21
|
+
setup(__props, { emit: __emit }) {
|
|
22
|
+
const emit = __emit;
|
|
23
|
+
/**
|
|
24
|
+
* A disabled entry, and an entry with no command id, select nothing. Reka
|
|
25
|
+
* already blocks pointer events on the disabled row; this is the second half,
|
|
26
|
+
* for the keyboard path that reaches it anyway.
|
|
27
|
+
*/
|
|
28
|
+
function choose(item) {
|
|
29
|
+
if (item.disabled || item.value === void 0) return;
|
|
30
|
+
emit("select", item.value);
|
|
31
|
+
}
|
|
32
|
+
return (_ctx, _cache) => {
|
|
33
|
+
return openBlock(), createBlock(unref(DropdownMenuRoot), mergeProps(unref(optional)({ open: __props.open }), { "onUpdate:open": _cache[0] || (_cache[0] = ($event) => _ctx.$emit("update:open", $event)) }), {
|
|
34
|
+
default: withCtx(() => [createVNode(unref(DropdownMenuTrigger), { "as-child": "" }, {
|
|
35
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "trigger")]),
|
|
36
|
+
_: 3
|
|
37
|
+
}), createVNode(unref(DropdownMenuPortal), null, {
|
|
38
|
+
default: withCtx(() => [createVNode(unref(DropdownMenuContent), {
|
|
39
|
+
"side-offset": 6,
|
|
40
|
+
align: "start",
|
|
41
|
+
class: normalizeClass(unref(cn)("z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none", "data-[state=open]:animate-scale-in")),
|
|
42
|
+
style: { "transform-origin": "var(--reka-popper-transform-origin)" }
|
|
43
|
+
}, {
|
|
44
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.items, (item, i) => {
|
|
45
|
+
return openBlock(), createElementBlock(Fragment, { key: i }, [item.separator ? (openBlock(), createBlock(unref(DropdownMenuSeparator), {
|
|
46
|
+
key: 0,
|
|
47
|
+
class: "my-1 h-px bg-border"
|
|
48
|
+
})) : item.heading ? (openBlock(), createBlock(unref(DropdownMenuLabel), {
|
|
49
|
+
key: 1,
|
|
50
|
+
class: "px-2 py-1.5 text-micro font-medium uppercase tracking-wide text-muted-foreground"
|
|
51
|
+
}, {
|
|
52
|
+
default: withCtx(() => [createTextVNode(toDisplayString(item.label), 1)]),
|
|
53
|
+
_: 2
|
|
54
|
+
}, 1024)) : (openBlock(), createBlock(unref(DropdownMenuItem), {
|
|
55
|
+
key: 2,
|
|
56
|
+
disabled: item.disabled ?? false,
|
|
57
|
+
class: normalizeClass(unref(cn)("flex cursor-pointer items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none", "transition-colors duration-fast ease-out", "animate-fade-rise", "data-[highlighted]:bg-subtle", "data-[disabled]:pointer-events-none data-[disabled]:opacity-40", item.danger && "text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive")),
|
|
58
|
+
style: normalizeStyle({ animationDelay: unref(listStaggerDelay)(i) }),
|
|
59
|
+
onSelect: ($event) => choose(item)
|
|
60
|
+
}, {
|
|
61
|
+
default: withCtx(() => [createElementVNode("span", null, toDisplayString(item.label), 1), item.shortcut ? (openBlock(), createElementBlock("span", _hoisted_1, toDisplayString(item.shortcut), 1)) : createCommentVNode("", true)]),
|
|
62
|
+
_: 2
|
|
63
|
+
}, 1032, [
|
|
64
|
+
"disabled",
|
|
65
|
+
"class",
|
|
66
|
+
"style",
|
|
67
|
+
"onSelect"
|
|
68
|
+
]))], 64);
|
|
69
|
+
}), 128))]),
|
|
70
|
+
_: 1
|
|
71
|
+
}, 8, ["class"])]),
|
|
72
|
+
_: 1
|
|
73
|
+
})]),
|
|
74
|
+
_: 3
|
|
75
|
+
}, 16);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
//#endregion
|
|
80
|
+
export { DropdownMenu_vue_vue_type_script_setup_true_lang_default as default };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=DropdownMenu.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropdownMenu.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../src/primitives/DropdownMenu/DropdownMenu.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * One row of the menu, and the three kinds a menu is built from are told apart\n * by which fields are set rather than by a `kind` discriminator: a `separator`\n * is a rule, a `heading` is a non-interactive group label, and anything else\n * is a command.\n *\n * The list is data rather than markup because the primitive stays free of app\n * logic — selecting a row emits its `value` and the host decides what that\n * command means.\n */\nexport interface DropdownMenuEntry {\n /** The visible text of a command or a heading. */\n label?: string;\n /** The command id emitted on select. An entry without one is not selectable. */\n value?: string;\n /** Accelerator hint shown at the trailing edge, e.g. `⌘S`. Display only — the primitive binds no keys. */\n shortcut?: string;\n /** Render a divider. Every other field is ignored. */\n separator?: boolean;\n /** Render a non-interactive section heading rather than a command. */\n heading?: boolean;\n /** A destructive command — painted in the destructive token so it is not one indistinguishable row. */\n danger?: boolean;\n /** Visible but inert: announced as disabled, and selecting it emits nothing. */\n disabled?: boolean;\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n DropdownMenuRoot,\n DropdownMenuTrigger,\n DropdownMenuPortal,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n} from \"reka-ui\";\nimport { cn } from \"../../lib/cn\";\nimport { listStaggerDelay } from \"../../lib/motion\";\nimport { optional } from \"../../lib/props\";\n\n/**\n * DropdownMenu — one button that opens a list of commands, with roving focus,\n * typeahead and arrow-key navigation.\n *\n * The escape contract: opening moves focus into the menu and onto the first\n * command; Esc and an outside click both close it; closing returns focus to the\n * trigger. The menu is modal, so while it is open the page behind does not\n * scroll and the rest of the page is hidden from assistive technology — a\n * command list is never scrolled out from under the pointer mid-choice. Focus\n * stays in the menu while it is open, but not the way `Dialog` traps it: a menu\n * has no internal tab order at all. Tab is inert, and the arrow keys plus\n * typeahead are how a row is reached.\n *\n * Selecting an entry emits `select` with its `value`; the host maps that id to\n * an action, which is what keeps the primitive free of app logic.\n *\n * Reach for `Popover` when the panel is interactive content rather than a\n * command list, and for a select control when the list *is* a field's value.\n */\nwithDefaults(\n defineProps<{\n /** The rows, in order. Three kinds, told apart by which fields are set. */\n items: DropdownMenuEntry[];\n /** Drive the menu from the host with `v-model:open`; omit it and the menu owns its own state. */\n open?: boolean | undefined;\n }>(),\n // `open: undefined` is load-bearing, not a redundant default — it is what\n // keeps the uncontrolled third state reachable past Vue's absent-Boolean\n // casting. `optional()` in the template is the other half; its docblock\n // carries the reasoning for both.\n { open: undefined },\n);\nconst emit = defineEmits<{ select: [value: string]; \"update:open\": [value: boolean] }>();\n\n/**\n * A disabled entry, and an entry with no command id, select nothing. Reka\n * already blocks pointer events on the disabled row; this is the second half,\n * for the keyboard path that reaches it anyway.\n */\nfunction choose(item: DropdownMenuEntry): void {\n if (item.disabled || item.value === undefined) return;\n emit(\"select\", item.value);\n}\n</script>\n\n<template>\n <DropdownMenuRoot v-bind=\"optional({ open })\" @update:open=\"$emit('update:open', $event)\">\n <!-- @slot The button that opens the menu. Rendered `as-child`, so the\n caller's own element *is* the trigger. -->\n <DropdownMenuTrigger as-child>\n <slot name=\"trigger\" />\n </DropdownMenuTrigger>\n\n <DropdownMenuPortal>\n <DropdownMenuContent\n :side-offset=\"6\"\n align=\"start\"\n :class=\"\n cn(\n 'z-50 min-w-[12rem] rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none',\n // Scoped to the open state, never unconditional. Reka's Presence\n // keeps closed content mounted until an animationend arrives, and a\n // mount-only animation never fires a second one — so an\n // unconditional animation class strands an invisible menu over the\n // page, catching clicks meant for what is behind it. Scoped, the\n // closed element computes `animation-name: none` and Presence\n // unmounts it at once. The origin below makes the menu grow out of\n // its trigger rather than out of its own centre.\n 'data-[state=open]:animate-scale-in',\n )\n \"\n style=\"transform-origin: var(--reka-popper-transform-origin)\"\n >\n <template v-for=\"(item, i) in items\" :key=\"i\">\n <DropdownMenuSeparator v-if=\"item.separator\" class=\"my-1 h-px bg-border\" />\n <DropdownMenuLabel\n v-else-if=\"item.heading\"\n class=\"px-2 py-1.5 text-micro font-medium uppercase tracking-wide text-muted-foreground\"\n >\n {{ item.label }}\n </DropdownMenuLabel>\n <!-- The reveal delay comes from the shared stagger vocabulary, capped\n there so a long menu does not tail off, rather than from a\n per-menu number written by hand.\n\n `disabled` is coerced rather than forwarded raw: an entry that\n simply omits the field is not disabled, and Reka's own prop does\n not accept `undefined` as a way of saying so. -->\n <DropdownMenuItem\n v-else\n :disabled=\"item.disabled ?? false\"\n :class=\"\n cn(\n 'flex cursor-pointer items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-sm text-foreground outline-none',\n 'transition-colors duration-fast ease-out',\n // The rows reveal in sequence as the menu opens, which reads as\n // a list arriving rather than a block appearing. The hover\n // colour transition above is a separate channel, so a row\n // highlighted mid-reveal still responds at once.\n 'animate-fade-rise',\n 'data-[highlighted]:bg-subtle',\n 'data-[disabled]:pointer-events-none data-[disabled]:opacity-40',\n item.danger &&\n 'text-destructive data-[highlighted]:bg-destructive/10 data-[highlighted]:text-destructive',\n )\n \"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n @select=\"choose(item)\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-micro text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </DropdownMenuItem>\n </template>\n </DropdownMenuContent>\n </DropdownMenuPortal>\n </DropdownMenuRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EA2EA,MAAM,OAAO;;;;;;EAOb,SAAS,OAAO,MAA+B;GAC7C,IAAI,KAAK,YAAY,KAAK,UAAU,KAAA,GAAW;GAC/C,KAAK,UAAU,KAAK,KAAK;EAC3B;;GAIE,OAAA,UAAA,GAAA,YAuEmB,MAAA,gBAAA,GAvEnB,WAA0B,MAuEP,QAAA,CAvEe,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA,EAAM,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM,GAAA,CAAA,GAAA;IAGrF,SAAA,cAEsB,CAFtB,YAEsB,MAAA,mBAAA,GAAA,EAFD,YAAA,GAAQ,GAAA;KAC3B,SAAA,cAAuB,CAAvB,WAAuB,KAAA,QAAA,SAAA,CAAA,CAAA;;IAGzB,CAAA,GAAA,YA+DqB,MAAA,kBAAA,GAAA,MAAA;KA9DnB,SAAA,cA6DsB,CA7DtB,YA6DsB,MAAA,mBAAA,GAAA;MA5DnB,eAAa;MACd,OAAM;MACL,OAAK,eAAa,MAAA,EAAA,CAAE,CAAA,oHAAA,oCAAA,CAAA;MAcrB,OAAA,EAAA,oBAAA,sCAAA;;MAEU,SAAA,cAA0B,EAApC,UAAA,IAAA,GAAA,mBAyCW,UAAA,MAAA,WAzCmB,QAAA,QAAZ,MAAM,MAAC;OAAkB,OAAA,UAAA,GAAA,mBAAA,UAAA,EAAA,KAAA,EAAC,GAAA,CACb,KAAK,aAAlC,UAAA,GAAA,YAA2E,MAAA,qBAAA,GAAA;;QAA9B,OAAM;OAEtC,CAAA,KAAA,KAAK,WADlB,UAAA,GAAA,YAKoB,MAAA,iBAAA,GAAA;;QAHlB,OAAM;;QAEN,SAAA,cAAgB,CAAb,gBAAA,gBAAA,KAAK,KAAK,GAAA,CAAA,CAAA,CAAA;;OASf,GAAA,IAAA,MAAA,UAAA,GAAA,YAyBmB,MAAA,gBAAA,GAAA;;QAvBhB,UAAU,KAAK,YAAQ;QACvB,OAAK,eAAiB,MAAA,EAAA,CAAE,CAAA,sHAAA,4CAAA,qBAAA,gCAAA,kEAAqqB,KAAK,UAAA,2FAAA,CAAA;QAelsB,OAAK,eAAA,EAAA,gBAAoB,MAAA,gBAAA,CAAgB,CAAC,CAAC,EAAA,CAAA;QAC3C,WAAM,WAAE,OAAO,IAAI;;QAEpB,SAAA,cAA6B,CAA7B,mBAA6B,QAAA,MAAA,gBAApB,KAAK,KAAK,GAAA,CAAA,GACP,KAAK,YAAjB,UAAA,GAAA,mBAEO,QAFP,YAEO,gBADF,KAAK,QAAQ,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Field_vue_vue_type_script_setup_true_lang_default from "./Field.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region src/primitives/Field/Field.vue
|
|
3
|
+
var Field_default = Field_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { Field_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=Field.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Field.js","names":[],"sources":["../../../src/primitives/Field/Field.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/* eslint vuejs-accessibility/label-has-for: [\"error\", { \"required\": \"id\" }] */\n/*\n * The workspace default for this rule requires BOTH a `for`/id pairing AND\n * the control physically nested inside the `<label>` (`required: {every:\n * [\"nesting\",\"id\"]}`). Field labels a control passed through the default\n * slot — never nested inside the `<label>` — so \"nesting\" can never be\n * satisfied by any slot-based composition, regardless of how the label is\n * written. The `for`/id pairing below is itself a fully valid, standard\n * accessible-name association (WCAG 1.3.1 / 4.1.2); this override scopes the\n * rule to that single valid strategy for this file only.\n */\nimport { computed } from \"vue\";\nimport InlineError from \"../InlineError/InlineError.vue\";\n\n/**\n * Field — the form-row wrapper: label + control + one hint-or-error line, so\n * a consumer stops hand-assembling that layout at every call site. Pure\n * composition, not a Reka component — the default slot is the control\n * (TextField, Textarea, a select, …). Setting `error` swaps InlineError in\n * for `hint`, because a row shows one message at a time, never both stacked.\n *\n * `for` does two jobs. It is what associates the `<label>` with the control\n * (WCAG 1.3.1 / 4.1.2) — `label` alone, with no `for`, falls back to a plain\n * `<span>` rather than emit a `<label>` with nothing to point at. And it is\n * what the hint/error line derives its own id from (`${for}-description`),\n * which is the missing half of the accessibility contract: Field cannot\n * reach into an arbitrary slotted control to set `aria-describedby` on it,\n * so it publishes a predictable id instead and leaves that one attribute to\n * the consumer — the same way `invalid` already has to be set on the\n * control directly. TextField and Textarea both forward an `aria-describedby`\n * passed to them straight onto their native element, so wiring the two\n * together is exactly:\n *\n * ```vue\n * <Field label=\"Email\" for=\"email\" error=\"Invalid address\">\n * <TextField id=\"email\" aria-describedby=\"email-description\" invalid />\n * </Field>\n * ```\n */\nconst props = withDefaults(\n defineProps<{\n /** The visible label text. Renders as a real `<label>` only when `for` is also set. */\n label?: string;\n /** Supporting text shown below the control. Hidden while `error` is set. */\n hint?: string;\n /** The error message. Renders through InlineError and replaces `hint`. */\n error?: string;\n /** Marks the field mandatory and appends a destructive asterisk to the label. */\n required?: boolean;\n /** The id of the control in the default slot — drives the label's `for` and the hint/error id. */\n for?: string;\n }>(),\n { required: false },\n);\n\n// The hint and the error are mutually exclusive, so one id covers whichever\n// is showing. Undefined without `for`: an id with no control to point at it\n// would be a landmark for no one.\nconst descriptionId = computed(() => (props.for ? `${props.for}-description` : undefined));\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1.5\">\n <label v-if=\"label && props.for\" :for=\"props.for\" class=\"text-sm font-medium text-foreground\">\n {{ label }} <span v-if=\"required\" class=\"text-destructive\">*</span>\n </label>\n <span v-else-if=\"label\" class=\"text-sm font-medium text-foreground\">\n {{ label }} <span v-if=\"required\" class=\"text-destructive\">*</span>\n </span>\n <slot />\n <p v-if=\"hint && !error\" :id=\"descriptionId\" class=\"text-xs text-muted-foreground\">\n {{ hint }}\n </p>\n <InlineError v-if=\"error\" :id=\"descriptionId\" :message=\"error\" />\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field — the form-row wrapper: label + control + one hint-or-error line, so
|
|
3
|
+
* a consumer stops hand-assembling that layout at every call site. Pure
|
|
4
|
+
* composition, not a Reka component — the default slot is the control
|
|
5
|
+
* (TextField, Textarea, a select, …). Setting `error` swaps InlineError in
|
|
6
|
+
* for `hint`, because a row shows one message at a time, never both stacked.
|
|
7
|
+
*
|
|
8
|
+
* `for` does two jobs. It is what associates the `<label>` with the control
|
|
9
|
+
* (WCAG 1.3.1 / 4.1.2) — `label` alone, with no `for`, falls back to a plain
|
|
10
|
+
* `<span>` rather than emit a `<label>` with nothing to point at. And it is
|
|
11
|
+
* what the hint/error line derives its own id from (`${for}-description`),
|
|
12
|
+
* which is the missing half of the accessibility contract: Field cannot
|
|
13
|
+
* reach into an arbitrary slotted control to set `aria-describedby` on it,
|
|
14
|
+
* so it publishes a predictable id instead and leaves that one attribute to
|
|
15
|
+
* the consumer — the same way `invalid` already has to be set on the
|
|
16
|
+
* control directly. TextField and Textarea both forward an `aria-describedby`
|
|
17
|
+
* passed to them straight onto their native element, so wiring the two
|
|
18
|
+
* together is exactly:
|
|
19
|
+
*
|
|
20
|
+
* ```vue
|
|
21
|
+
* <Field label="Email" for="email" error="Invalid address">
|
|
22
|
+
* <TextField id="email" aria-describedby="email-description" invalid />
|
|
23
|
+
* </Field>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
type __VLS_Props = {
|
|
27
|
+
/** The visible label text. Renders as a real `<label>` only when `for` is also set. */
|
|
28
|
+
label?: string;
|
|
29
|
+
/** Supporting text shown below the control. Hidden while `error` is set. */
|
|
30
|
+
hint?: string;
|
|
31
|
+
/** The error message. Renders through InlineError and replaces `hint`. */
|
|
32
|
+
error?: string;
|
|
33
|
+
/** Marks the field mandatory and appends a destructive asterisk to the label. */
|
|
34
|
+
required?: boolean;
|
|
35
|
+
/** The id of the control in the default slot — drives the label's `for` and the hint/error id. */
|
|
36
|
+
for?: string;
|
|
37
|
+
};
|
|
38
|
+
declare var __VLS_1: {};
|
|
39
|
+
type __VLS_Slots = {} & {
|
|
40
|
+
default?: (props: typeof __VLS_1) => any;
|
|
41
|
+
};
|
|
42
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
43
|
+
required: boolean;
|
|
44
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
45
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
46
|
+
declare const _default: typeof __VLS_export;
|
|
47
|
+
export default _default;
|
|
48
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
49
|
+
new (): {
|
|
50
|
+
$slots: S;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=Field.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Field.vue.d.ts","sourceRoot":"","sources":["../../../src/primitives/Field/Field.vue"],"names":[],"mappings":"AA8FA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,KAAK,WAAW,GAAG;IACf,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kGAAkG;IAClG,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AA0FJ,QAAA,IAAI,OAAO,IAAW,CAAE;AACxB,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,CAAC;AAK/C,QAAA,MAAM,UAAU;cApGD,OAAO;6EAuGpB,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AAWzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import InlineError_default from "../InlineError/InlineError.js";
|
|
2
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createTextVNode, defineComponent, openBlock, renderSlot, toDisplayString } from "vue";
|
|
3
|
+
//#region src/primitives/Field/Field.vue?vue&type=script&setup=true&lang.ts
|
|
4
|
+
var _hoisted_1 = { class: "flex flex-col gap-1.5" };
|
|
5
|
+
var _hoisted_2 = ["for"];
|
|
6
|
+
var _hoisted_3 = {
|
|
7
|
+
key: 0,
|
|
8
|
+
class: "text-destructive"
|
|
9
|
+
};
|
|
10
|
+
var _hoisted_4 = {
|
|
11
|
+
key: 1,
|
|
12
|
+
class: "text-sm font-medium text-foreground"
|
|
13
|
+
};
|
|
14
|
+
var _hoisted_5 = {
|
|
15
|
+
key: 0,
|
|
16
|
+
class: "text-destructive"
|
|
17
|
+
};
|
|
18
|
+
var _hoisted_6 = ["id"];
|
|
19
|
+
/**
|
|
20
|
+
* Field — the form-row wrapper: label + control + one hint-or-error line, so
|
|
21
|
+
* a consumer stops hand-assembling that layout at every call site. Pure
|
|
22
|
+
* composition, not a Reka component — the default slot is the control
|
|
23
|
+
* (TextField, Textarea, a select, …). Setting `error` swaps InlineError in
|
|
24
|
+
* for `hint`, because a row shows one message at a time, never both stacked.
|
|
25
|
+
*
|
|
26
|
+
* `for` does two jobs. It is what associates the `<label>` with the control
|
|
27
|
+
* (WCAG 1.3.1 / 4.1.2) — `label` alone, with no `for`, falls back to a plain
|
|
28
|
+
* `<span>` rather than emit a `<label>` with nothing to point at. And it is
|
|
29
|
+
* what the hint/error line derives its own id from (`${for}-description`),
|
|
30
|
+
* which is the missing half of the accessibility contract: Field cannot
|
|
31
|
+
* reach into an arbitrary slotted control to set `aria-describedby` on it,
|
|
32
|
+
* so it publishes a predictable id instead and leaves that one attribute to
|
|
33
|
+
* the consumer — the same way `invalid` already has to be set on the
|
|
34
|
+
* control directly. TextField and Textarea both forward an `aria-describedby`
|
|
35
|
+
* passed to them straight onto their native element, so wiring the two
|
|
36
|
+
* together is exactly:
|
|
37
|
+
*
|
|
38
|
+
* ```vue
|
|
39
|
+
* <Field label="Email" for="email" error="Invalid address">
|
|
40
|
+
* <TextField id="email" aria-describedby="email-description" invalid />
|
|
41
|
+
* </Field>
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
var Field_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
45
|
+
__name: "Field",
|
|
46
|
+
props: {
|
|
47
|
+
label: {},
|
|
48
|
+
hint: {},
|
|
49
|
+
error: {},
|
|
50
|
+
required: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
default: false
|
|
53
|
+
},
|
|
54
|
+
for: {}
|
|
55
|
+
},
|
|
56
|
+
setup(__props) {
|
|
57
|
+
const props = __props;
|
|
58
|
+
const descriptionId = computed(() => props.for ? `${props.for}-description` : void 0);
|
|
59
|
+
return (_ctx, _cache) => {
|
|
60
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
61
|
+
__props.label && props.for ? (openBlock(), createElementBlock("label", {
|
|
62
|
+
key: 0,
|
|
63
|
+
for: props.for,
|
|
64
|
+
class: "text-sm font-medium text-foreground"
|
|
65
|
+
}, [createTextVNode(toDisplayString(__props.label) + " ", 1), __props.required ? (openBlock(), createElementBlock("span", _hoisted_3, "*")) : createCommentVNode("", true)], 8, _hoisted_2)) : __props.label ? (openBlock(), createElementBlock("span", _hoisted_4, [createTextVNode(toDisplayString(__props.label) + " ", 1), __props.required ? (openBlock(), createElementBlock("span", _hoisted_5, "*")) : createCommentVNode("", true)])) : createCommentVNode("", true),
|
|
66
|
+
renderSlot(_ctx.$slots, "default"),
|
|
67
|
+
__props.hint && !__props.error ? (openBlock(), createElementBlock("p", {
|
|
68
|
+
key: 2,
|
|
69
|
+
id: descriptionId.value,
|
|
70
|
+
class: "text-xs text-muted-foreground"
|
|
71
|
+
}, toDisplayString(__props.hint), 9, _hoisted_6)) : createCommentVNode("", true),
|
|
72
|
+
__props.error ? (openBlock(), createBlock(InlineError_default, {
|
|
73
|
+
key: 3,
|
|
74
|
+
id: descriptionId.value,
|
|
75
|
+
message: __props.error
|
|
76
|
+
}, null, 8, ["id", "message"])) : createCommentVNode("", true)
|
|
77
|
+
]);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//#endregion
|
|
82
|
+
export { Field_vue_vue_type_script_setup_true_lang_default as default };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=Field.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Field.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/primitives/Field/Field.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/* eslint vuejs-accessibility/label-has-for: [\"error\", { \"required\": \"id\" }] */\n/*\n * The workspace default for this rule requires BOTH a `for`/id pairing AND\n * the control physically nested inside the `<label>` (`required: {every:\n * [\"nesting\",\"id\"]}`). Field labels a control passed through the default\n * slot — never nested inside the `<label>` — so \"nesting\" can never be\n * satisfied by any slot-based composition, regardless of how the label is\n * written. The `for`/id pairing below is itself a fully valid, standard\n * accessible-name association (WCAG 1.3.1 / 4.1.2); this override scopes the\n * rule to that single valid strategy for this file only.\n */\nimport { computed } from \"vue\";\nimport InlineError from \"../InlineError/InlineError.vue\";\n\n/**\n * Field — the form-row wrapper: label + control + one hint-or-error line, so\n * a consumer stops hand-assembling that layout at every call site. Pure\n * composition, not a Reka component — the default slot is the control\n * (TextField, Textarea, a select, …). Setting `error` swaps InlineError in\n * for `hint`, because a row shows one message at a time, never both stacked.\n *\n * `for` does two jobs. It is what associates the `<label>` with the control\n * (WCAG 1.3.1 / 4.1.2) — `label` alone, with no `for`, falls back to a plain\n * `<span>` rather than emit a `<label>` with nothing to point at. And it is\n * what the hint/error line derives its own id from (`${for}-description`),\n * which is the missing half of the accessibility contract: Field cannot\n * reach into an arbitrary slotted control to set `aria-describedby` on it,\n * so it publishes a predictable id instead and leaves that one attribute to\n * the consumer — the same way `invalid` already has to be set on the\n * control directly. TextField and Textarea both forward an `aria-describedby`\n * passed to them straight onto their native element, so wiring the two\n * together is exactly:\n *\n * ```vue\n * <Field label=\"Email\" for=\"email\" error=\"Invalid address\">\n * <TextField id=\"email\" aria-describedby=\"email-description\" invalid />\n * </Field>\n * ```\n */\nconst props = withDefaults(\n defineProps<{\n /** The visible label text. Renders as a real `<label>` only when `for` is also set. */\n label?: string;\n /** Supporting text shown below the control. Hidden while `error` is set. */\n hint?: string;\n /** The error message. Renders through InlineError and replaces `hint`. */\n error?: string;\n /** Marks the field mandatory and appends a destructive asterisk to the label. */\n required?: boolean;\n /** The id of the control in the default slot — drives the label's `for` and the hint/error id. */\n for?: string;\n }>(),\n { required: false },\n);\n\n// The hint and the error are mutually exclusive, so one id covers whichever\n// is showing. Undefined without `for`: an id with no control to point at it\n// would be a landmark for no one.\nconst descriptionId = computed(() => (props.for ? `${props.for}-description` : undefined));\n</script>\n\n<template>\n <div class=\"flex flex-col gap-1.5\">\n <label v-if=\"label && props.for\" :for=\"props.for\" class=\"text-sm font-medium text-foreground\">\n {{ label }} <span v-if=\"required\" class=\"text-destructive\">*</span>\n </label>\n <span v-else-if=\"label\" class=\"text-sm font-medium text-foreground\">\n {{ label }} <span v-if=\"required\" class=\"text-destructive\">*</span>\n </span>\n <slot />\n <p v-if=\"hint && !error\" :id=\"descriptionId\" class=\"text-xs text-muted-foreground\">\n {{ hint }}\n </p>\n <InlineError v-if=\"error\" :id=\"descriptionId\" :message=\"error\" />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCA,MAAM,QAAQ;EAmBd,MAAM,gBAAgB,eAAgB,MAAM,MAAM,GAAG,MAAM,IAAI,gBAAgB,KAAA,CAAU;;GAIvF,OAAA,UAAA,GAAA,mBAYM,OAZN,YAYM;IAXS,QAAA,SAAS,MAAM,OAA5B,UAAA,GAAA,mBAEQ,SAAA;;KAF0B,KAAK,MAAM;KAAK,OAAM;IACnD,GAAA,CAAA,gBAAA,gBAAA,QAAA,KAAK,IAAG,KAAC,CAAA,GAAY,QAAA,YAAZ,UAAA,GAAA,mBAAuD,QAAvD,YAA+C,GAAC,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,GAAA,UAAA,KAE7C,QAAA,SAAjB,UAAA,GAAA,mBAEO,QAFP,YAEO,CADF,gBAAA,gBAAA,QAAA,KAAK,IAAG,KAAC,CAAA,GAAY,QAAA,YAAZ,UAAA,GAAA,mBAAuD,QAAvD,YAA+C,GAAC,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAE9D,WAAQ,KAAA,QAAA,SAAA;IACC,QAAA,QAAI,CAAK,QAAA,SAAlB,UAAA,GAAA,mBAEI,KAAA;;KAFsB,IAAI,cAAA;KAAe,OAAM;IAC9C,GAAA,gBAAA,QAAA,IAAI,GAAA,GAAA,UAAA,KAAA,mBAAA,IAAA,IAAA;IAEU,QAAA,SAAnB,UAAA,GAAA,YAAiE,qBAAA;;KAAtC,IAAI,cAAA;KAAgB,SAAS,QAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import InlineError_vue_vue_type_script_setup_true_lang_default from "./InlineError.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region src/primitives/InlineError/InlineError.vue
|
|
3
|
+
var InlineError_default = InlineError_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { InlineError_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=InlineError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InlineError.js","names":[],"sources":["../../../src/primitives/InlineError/InlineError.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { TriangleAlert } from \"@lucide/vue\";\nimport { cn } from \"../../lib/cn\";\n\n/**\n * InlineError — the one error contract for a field or a section, instead of\n * a paragraph here and a badge there. `role=\"alert\"` on the root means a\n * screen reader announces it the instant it enters the DOM, which is also\n * why a host must mount it with `v-if` rather than hide it with `v-show`:\n * CSS visibility leaves the node in the DOM, so a message change under\n * `v-show` may not re-announce.\n *\n * Persists until the host resolves the cause it names — there is no\n * auto-dismiss timer and no built-in close button. A transient notice that\n * hides itself belongs to a toast, not here.\n */\ndefineProps<{\n /** The error text. A default-slot child overrides this when both are given. */\n message?: string;\n}>();\n</script>\n\n<template>\n <div\n role=\"alert\"\n :class=\"\n cn(\n 'flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive',\n 'animate-fade-rise',\n )\n \"\n >\n <TriangleAlert class=\"mt-0.5 h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n <div class=\"min-w-0 flex-1\">\n <slot>{{ message }}</slot>\n </div>\n <div v-if=\"$slots.action\" class=\"shrink-0\"><slot name=\"action\" /></div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InlineError — the one error contract for a field or a section, instead of
|
|
3
|
+
* a paragraph here and a badge there. `role="alert"` on the root means a
|
|
4
|
+
* screen reader announces it the instant it enters the DOM, which is also
|
|
5
|
+
* why a host must mount it with `v-if` rather than hide it with `v-show`:
|
|
6
|
+
* CSS visibility leaves the node in the DOM, so a message change under
|
|
7
|
+
* `v-show` may not re-announce.
|
|
8
|
+
*
|
|
9
|
+
* Persists until the host resolves the cause it names — there is no
|
|
10
|
+
* auto-dismiss timer and no built-in close button. A transient notice that
|
|
11
|
+
* hides itself belongs to a toast, not here.
|
|
12
|
+
*/
|
|
13
|
+
type __VLS_Props = {
|
|
14
|
+
/** The error text. A default-slot child overrides this when both are given. */
|
|
15
|
+
message?: string;
|
|
16
|
+
};
|
|
17
|
+
declare var __VLS_6: {}, __VLS_8: {};
|
|
18
|
+
type __VLS_Slots = {} & {
|
|
19
|
+
default?: (props: typeof __VLS_6) => any;
|
|
20
|
+
} & {
|
|
21
|
+
action?: (props: typeof __VLS_8) => any;
|
|
22
|
+
};
|
|
23
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
25
|
+
declare const _default: typeof __VLS_export;
|
|
26
|
+
export default _default;
|
|
27
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
28
|
+
new (): {
|
|
29
|
+
$slots: S;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=InlineError.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InlineError.vue.d.ts","sourceRoot":"","sources":["../../../src/primitives/InlineError/InlineError.vue"],"names":[],"mappings":"AA6CA;;;;;;;;;;;GAWG;AACH,KAAK,WAAW,GAAG;IACjB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA+DF,QAAA,IAAI,OAAO,IAAU,EAAE,OAAO,IAAW,CAAE;AAC3C,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,GAC5C;IAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,OAAO,KAAK,GAAG,CAAA;CAAE,CAAC;AAK9C,QAAA,MAAM,UAAU,kSAEd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { cn } from "../../lib/cn.js";
|
|
2
|
+
import { TriangleAlert } from "@lucide/vue";
|
|
3
|
+
import { createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, renderSlot, toDisplayString, unref } from "vue";
|
|
4
|
+
//#region src/primitives/InlineError/InlineError.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
+
var _hoisted_1 = { class: "min-w-0 flex-1" };
|
|
6
|
+
var _hoisted_2 = {
|
|
7
|
+
key: 0,
|
|
8
|
+
class: "shrink-0"
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* InlineError — the one error contract for a field or a section, instead of
|
|
12
|
+
* a paragraph here and a badge there. `role="alert"` on the root means a
|
|
13
|
+
* screen reader announces it the instant it enters the DOM, which is also
|
|
14
|
+
* why a host must mount it with `v-if` rather than hide it with `v-show`:
|
|
15
|
+
* CSS visibility leaves the node in the DOM, so a message change under
|
|
16
|
+
* `v-show` may not re-announce.
|
|
17
|
+
*
|
|
18
|
+
* Persists until the host resolves the cause it names — there is no
|
|
19
|
+
* auto-dismiss timer and no built-in close button. A transient notice that
|
|
20
|
+
* hides itself belongs to a toast, not here.
|
|
21
|
+
*/
|
|
22
|
+
var InlineError_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
23
|
+
__name: "InlineError",
|
|
24
|
+
props: { message: {} },
|
|
25
|
+
setup(__props) {
|
|
26
|
+
return (_ctx, _cache) => {
|
|
27
|
+
return openBlock(), createElementBlock("div", {
|
|
28
|
+
role: "alert",
|
|
29
|
+
class: normalizeClass(unref(cn)("flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive", "animate-fade-rise"))
|
|
30
|
+
}, [
|
|
31
|
+
createVNode(unref(TriangleAlert), {
|
|
32
|
+
class: "mt-0.5 h-4 w-4 shrink-0",
|
|
33
|
+
"aria-hidden": "true"
|
|
34
|
+
}),
|
|
35
|
+
createElementVNode("div", _hoisted_1, [renderSlot(_ctx.$slots, "default", {}, () => [createTextVNode(toDisplayString(__props.message), 1)])]),
|
|
36
|
+
_ctx.$slots.action ? (openBlock(), createElementBlock("div", _hoisted_2, [renderSlot(_ctx.$slots, "action")])) : createCommentVNode("", true)
|
|
37
|
+
], 2);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
42
|
+
export { InlineError_vue_vue_type_script_setup_true_lang_default as default };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=InlineError.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InlineError.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../src/primitives/InlineError/InlineError.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { TriangleAlert } from \"@lucide/vue\";\nimport { cn } from \"../../lib/cn\";\n\n/**\n * InlineError — the one error contract for a field or a section, instead of\n * a paragraph here and a badge there. `role=\"alert\"` on the root means a\n * screen reader announces it the instant it enters the DOM, which is also\n * why a host must mount it with `v-if` rather than hide it with `v-show`:\n * CSS visibility leaves the node in the DOM, so a message change under\n * `v-show` may not re-announce.\n *\n * Persists until the host resolves the cause it names — there is no\n * auto-dismiss timer and no built-in close button. A transient notice that\n * hides itself belongs to a toast, not here.\n */\ndefineProps<{\n /** The error text. A default-slot child overrides this when both are given. */\n message?: string;\n}>();\n</script>\n\n<template>\n <div\n role=\"alert\"\n :class=\"\n cn(\n 'flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive',\n 'animate-fade-rise',\n )\n \"\n >\n <TriangleAlert class=\"mt-0.5 h-4 w-4 shrink-0\" aria-hidden=\"true\" />\n <div class=\"min-w-0 flex-1\">\n <slot>{{ message }}</slot>\n </div>\n <div v-if=\"$slots.action\" class=\"shrink-0\"><slot name=\"action\" /></div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;GAuBE,OAAA,UAAA,GAAA,mBAcM,OAAA;IAbJ,MAAK;IACJ,OAAK,eAAS,MAAA,EAAA,CAAE,CAAA,uHAAA,mBAAA,CAAA;;IAOjB,YAAoE,MAAA,aAAA,GAAA;KAArD,OAAM;KAA0B,eAAY;;IAC3D,mBAEM,OAFN,YAEM,CADJ,WAA0B,KAAA,QAAA,WAAA,CAAA,SAAA,CAAjB,gBAAA,gBAAA,QAAA,OAAO,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA;IAEPA,KAAAA,OAAO,UAAlB,UAAA,GAAA,mBAAuE,OAAvE,YAAuE,CAA5B,WAAsB,KAAA,QAAA,QAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Menubar_vue_vue_type_script_setup_true_lang_default from "./Menubar.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region src/primitives/Menubar/Menubar.vue
|
|
3
|
+
var Menubar_default = Menubar_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { Menubar_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=Menubar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menubar.js","names":[],"sources":["../../../src/primitives/Menubar/Menubar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Menubar — the classic desktop-app menu strip (File · View · Help …).\n *\n * Data-driven: pass a `menus` array; the component owns open/close, hover-to-switch,\n * keyboard nav and click-outside. Selecting an item emits `select` with its `command`\n * id — the host maps that id to an action (keeps the primitive free of app logic).\n *\n * A11y: WAI-ARIA menubar semantics (roles + aria-expanded/haspopup) with Escape,\n * Arrow, Home/End and Enter/Space handling. When richer focus-trapping is needed we\n * can later delegate to Reka UI without changing this call-site contract.\n */\nexport interface MenubarItem {\n /** Visible label. */\n label: string;\n /** Command id emitted on select. Omit for a non-actionable row. */\n command?: string;\n /** Right-aligned accelerator hint, e.g. \"Ctrl+S\". */\n shortcut?: string;\n /** Render a divider instead of an item (label ignored). */\n separator?: boolean;\n disabled?: boolean;\n}\n\nexport interface MenubarMenu {\n id: string;\n label: string;\n items: MenubarItem[];\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { onMounted, onBeforeUnmount, ref, nextTick } from \"vue\";\nimport { listStaggerDelay } from \"../../lib/motion\";\n\nconst props = defineProps<{\n /** The top-level menus, left to right, each carrying its own item list. */\n menus: MenubarMenu[];\n}>();\nconst emit = defineEmits<{ (e: \"select\", command: string): void }>();\n\nconst openId = ref<string | null>(null);\nconst activeIndex = ref(-1); // highlighted item within the open menu\n\nconst openMenu = (menu: MenubarMenu | undefined) => menu?.items ?? [];\nconst firstEnabled = (items: MenubarItem[], from = 0, dir = 1): number => {\n for (let i = from; i >= 0 && i < items.length; i += dir) {\n const item = items[i];\n if (item && !item.separator && !item.disabled) return i;\n }\n return -1;\n};\n\nfunction toggle(id: string): void {\n openId.value = openId.value === id ? null : id;\n activeIndex.value = -1;\n}\nfunction hover(id: string): void {\n if (openId.value !== null && openId.value !== id) {\n openId.value = id;\n activeIndex.value = -1;\n }\n}\nfunction choose(item: MenubarItem): void {\n if (item.separator || item.disabled) return;\n close();\n if (item.command) emit(\"select\", item.command);\n}\nfunction close(): void {\n openId.value = null;\n activeIndex.value = -1;\n}\n\nfunction moveMenu(dir: 1 | -1): void {\n const ids = props.menus.map((m) => m.id);\n const cur = openId.value ? ids.indexOf(openId.value) : 0;\n const next = (cur + dir + ids.length) % ids.length;\n openId.value = ids[next] ?? null;\n activeIndex.value = -1;\n}\n\nasync function onTriggerKeydown(e: KeyboardEvent, menu: MenubarMenu): Promise<void> {\n switch (e.key) {\n case \"ArrowDown\":\n case \"Enter\":\n case \" \":\n e.preventDefault();\n openId.value = menu.id;\n await nextTick();\n activeIndex.value = firstEnabled(menu.items);\n break;\n case \"ArrowRight\":\n e.preventDefault();\n if (openId.value) moveMenu(1);\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n if (openId.value) moveMenu(-1);\n break;\n case \"Escape\":\n close();\n break;\n }\n}\n\nfunction onMenuKeydown(e: KeyboardEvent, menu: MenubarMenu): void {\n const items = menu.items;\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n activeIndex.value = firstEnabled(items, Math.max(activeIndex.value, -1) + 1, 1);\n if (activeIndex.value === -1) activeIndex.value = firstEnabled(items, 0, 1);\n break;\n case \"ArrowUp\":\n e.preventDefault();\n activeIndex.value =\n activeIndex.value <= 0\n ? firstEnabled(items, items.length - 1, -1)\n : firstEnabled(items, activeIndex.value - 1, -1);\n break;\n case \"Home\":\n e.preventDefault();\n activeIndex.value = firstEnabled(items, 0, 1);\n break;\n case \"End\":\n e.preventDefault();\n activeIndex.value = firstEnabled(items, items.length - 1, -1);\n break;\n case \"ArrowRight\":\n e.preventDefault();\n moveMenu(1);\n break;\n case \"ArrowLeft\":\n e.preventDefault();\n moveMenu(-1);\n break;\n case \"Enter\":\n case \" \": {\n e.preventDefault();\n const item = activeIndex.value >= 0 ? items[activeIndex.value] : undefined;\n if (item) choose(item);\n break;\n }\n case \"Escape\":\n e.preventDefault();\n close();\n break;\n }\n}\n\nonMounted(() => window.addEventListener(\"click\", close));\nonBeforeUnmount(() => window.removeEventListener(\"click\", close));\n</script>\n\n<template>\n <div\n role=\"menubar\"\n class=\"flex items-center gap-0.5\"\n style=\"-webkit-app-region: no-drag\"\n @click.stop\n >\n <div v-for=\"menu in menus\" :key=\"menu.id\" class=\"relative\">\n <button\n :id=\"`menubar-trigger-${menu.id}`\"\n type=\"button\"\n role=\"menuitem\"\n :aria-haspopup=\"true\"\n :aria-expanded=\"openId === menu.id\"\n class=\"rounded-sm px-2 py-1 text-xs text-muted-foreground transition-colors duration-fast ease-out hover:bg-subtle hover:text-foreground focus-visible:bg-subtle focus-visible:text-foreground\"\n :class=\"openId === menu.id && 'bg-subtle text-foreground'\"\n @click.stop=\"toggle(menu.id)\"\n @mouseenter=\"hover(menu.id)\"\n @focus=\"hover(menu.id)\"\n @keydown=\"onTriggerKeydown($event, menu)\"\n >\n {{ menu.label }}\n </button>\n\n <div\n v-if=\"openId === menu.id\"\n role=\"menu\"\n :aria-label=\"menu.label\"\n tabindex=\"-1\"\n class=\"absolute left-0 top-full z-50 mt-1 min-w-[13rem] origin-top-left rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md animate-scale-in\"\n @click.stop\n @keydown=\"onMenuKeydown($event, menu)\"\n >\n <template v-for=\"(item, i) in openMenu(menu)\" :key=\"i\">\n <div v-if=\"item.separator\" role=\"separator\" class=\"my-1 h-px bg-border\" />\n <button\n v-else\n type=\"button\"\n role=\"menuitem\"\n :disabled=\"item.disabled\"\n :data-highlighted=\"activeIndex === i || undefined\"\n :style=\"{ animationDelay: listStaggerDelay(i) }\"\n class=\"flex w-full items-center justify-between gap-6 rounded-sm px-2 py-1.5 text-left text-xs text-foreground transition-colors duration-fast ease-out hover:bg-subtle disabled:pointer-events-none disabled:opacity-40 data-[highlighted]:bg-subtle animate-fade-rise\"\n @click=\"choose(item)\"\n @mouseenter=\"activeIndex = i\"\n @focus=\"activeIndex = i\"\n >\n <span>{{ item.label }}</span>\n <span v-if=\"item.shortcut\" class=\"tabular text-[0.6875rem] text-muted-foreground\">\n {{ item.shortcut }}\n </span>\n </button>\n </template>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Menubar — the classic desktop-app menu strip (File · View · Help …).
|
|
3
|
+
*
|
|
4
|
+
* Data-driven: pass a `menus` array; the component owns open/close, hover-to-switch,
|
|
5
|
+
* keyboard nav and click-outside. Selecting an item emits `select` with its `command`
|
|
6
|
+
* id — the host maps that id to an action (keeps the primitive free of app logic).
|
|
7
|
+
*
|
|
8
|
+
* A11y: WAI-ARIA menubar semantics (roles + aria-expanded/haspopup) with Escape,
|
|
9
|
+
* Arrow, Home/End and Enter/Space handling. When richer focus-trapping is needed we
|
|
10
|
+
* can later delegate to Reka UI without changing this call-site contract.
|
|
11
|
+
*/
|
|
12
|
+
export interface MenubarItem {
|
|
13
|
+
/** Visible label. */
|
|
14
|
+
label: string;
|
|
15
|
+
/** Command id emitted on select. Omit for a non-actionable row. */
|
|
16
|
+
command?: string;
|
|
17
|
+
/** Right-aligned accelerator hint, e.g. "Ctrl+S". */
|
|
18
|
+
shortcut?: string;
|
|
19
|
+
/** Render a divider instead of an item (label ignored). */
|
|
20
|
+
separator?: boolean;
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface MenubarMenu {
|
|
24
|
+
id: string;
|
|
25
|
+
label: string;
|
|
26
|
+
items: MenubarItem[];
|
|
27
|
+
}
|
|
28
|
+
declare const _default: typeof __VLS_export;
|
|
29
|
+
export default _default;
|
|
30
|
+
declare const __VLS_export: import("vue").DefineComponent<{
|
|
31
|
+
/** The top-level menus, left to right, each carrying its own item list. */
|
|
32
|
+
menus: MenubarMenu[];
|
|
33
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
34
|
+
select: (command: string) => any;
|
|
35
|
+
}, string, import("vue").PublicProps, Readonly<{
|
|
36
|
+
/** The top-level menus, left to right, each carrying its own item list. */
|
|
37
|
+
menus: MenubarMenu[];
|
|
38
|
+
}> & Readonly<{
|
|
39
|
+
onSelect?: (command: string) => any;
|
|
40
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
41
|
+
//# sourceMappingURL=Menubar.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Menubar.vue.d.ts","sourceRoot":"","sources":["../../../src/primitives/Menubar/Menubar.vue"],"names":[],"mappings":"AA0NA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;wBACoB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAEhB,2EAA2E;WACpE,WAAW,EAAE;;;;IADpB,2EAA2E;WACpE,WAAW,EAAE;;;kFAiUlB,CAAC"}
|