@sparklingopinions/sparkling-ui 0.5.0 → 0.7.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 +1 -1
- package/dist/aliases.d.ts +176 -26
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Avatar/Avatar.vue.d.ts +3 -2
- package/dist/components/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js +20 -14
- package/dist/components/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/Button/Button.vue.d.ts +1 -1
- package/dist/components/Button/Button.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/components/Button/Button.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.js +7 -0
- package/dist/components/ContextMenu/ContextMenu.js.map +1 -0
- package/dist/components/ContextMenu/ContextMenu.vue.d.ts +44 -0
- package/dist/components/ContextMenu/ContextMenu.vue_vue_type_script_setup_true_lang.js +84 -0
- package/dist/components/ContextMenu/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/ContextMenu/index.d.ts +2 -0
- package/dist/components/DataList/DataList.js.map +1 -1
- package/dist/components/DataList/DataList.vue.d.ts +8 -0
- package/dist/components/DataList/DataList.vue_vue_type_script_setup_true_lang.js +71 -52
- package/dist/components/DataList/DataList.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/DataList/types.d.ts +5 -1
- package/dist/components/Dialog/Dialog.js.map +1 -1
- package/dist/components/Dialog/Dialog.vue.d.ts +22 -6
- package/dist/components/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js +174 -72
- package/dist/components/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Dialog/useSheetDrag.d.ts +16 -0
- package/dist/components/Dialog/useSheetDrag.js +37 -0
- package/dist/components/Dialog/useSheetDrag.js.map +1 -0
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.vue.d.ts +6 -30
- package/dist/components/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js +54 -74
- package/dist/components/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.vue.d.ts +9 -3
- package/dist/components/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js +35 -7
- package/dist/components/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/FilterMenu.js.map +1 -1
- package/dist/components/Filters/FilterMenu.vue.d.ts +20 -1
- package/dist/components/Filters/FilterMenu.vue_vue_type_script_setup_true_lang.js +78 -56
- package/dist/components/Filters/FilterMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/SortMenu.js.map +1 -1
- package/dist/components/Filters/SortMenu.vue.d.ts +21 -1
- package/dist/components/Filters/SortMenu.vue_vue_type_script_setup_true_lang.js +90 -67
- package/dist/components/Filters/SortMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/index.d.ts +1 -0
- package/dist/components/Filters/trigger.d.ts +17 -0
- package/dist/components/Filters/trigger.js +33 -0
- package/dist/components/Filters/trigger.js.map +1 -0
- package/dist/components/FormField/FormField.js.map +1 -1
- package/dist/components/FormField/FormField.vue.d.ts +10 -0
- package/dist/components/FormField/FormField.vue_vue_type_script_setup_true_lang.js +31 -20
- package/dist/components/FormField/FormField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Icon/iconSet.js +25 -1
- package/dist/components/Icon/iconSet.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.vue.d.ts +6 -3
- package/dist/components/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js +20 -17
- package/dist/components/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/SegmentedControl/index.d.ts +1 -1
- package/dist/components/Table/Table.js.map +1 -1
- package/dist/components/Table/Table.vue.d.ts +4 -0
- package/dist/components/Table/Table.vue_vue_type_script_setup_true_lang.js +65 -56
- package/dist/components/Table/Table.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Tag/Tag.js.map +1 -1
- package/dist/components/Tag/Tag.vue.d.ts +16 -3
- package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js +42 -19
- package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Toast/ToastMessage.js.map +1 -1
- package/dist/components/Toast/ToastMessage.vue.d.ts +13 -1
- package/dist/components/Toast/ToastMessage.vue_vue_type_script_setup_true_lang.js +58 -23
- package/dist/components/Toast/ToastMessage.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Toast/Toaster.js.map +1 -1
- package/dist/components/Toast/Toaster.vue.d.ts +7 -3
- package/dist/components/Toast/Toaster.vue_vue_type_script_setup_true_lang.js +50 -21
- package/dist/components/Toast/Toaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/internal/MenuItemContent.js +7 -0
- package/dist/components/internal/MenuItemContent.js.map +1 -0
- package/dist/components/internal/MenuItemContent.vue.d.ts +7 -0
- package/dist/components/internal/MenuItemContent.vue_vue_type_script_setup_true_lang.js +26 -0
- package/dist/components/internal/MenuItemContent.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/internal/menu.d.ts +46 -0
- package/dist/components/internal/menu.js +33 -0
- package/dist/components/internal/menu.js.map +1 -0
- package/dist/composables/toast.d.ts +20 -5
- package/dist/composables/toast.js +28 -20
- package/dist/composables/toast.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +34 -33
- package/package.json +1 -1
- package/src/styles/tailwind.css +29 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type DropdownMenuAction, type DropdownMenuEntry, type MenuConfirmLabels } from '../internal/menu';
|
|
2
|
+
export interface ContextMenuProps extends MenuConfirmLabels {
|
|
3
|
+
items?: DropdownMenuEntry[];
|
|
4
|
+
/** The menu's accessible name, e.g. "Post actions". */
|
|
5
|
+
label?: string;
|
|
6
|
+
/** Lets the browser's own menu through. */
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
declare var __VLS_17: {};
|
|
10
|
+
type __VLS_Slots = {} & {
|
|
11
|
+
default?: (props: typeof __VLS_17) => any;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_base: import("vue").DefineComponent<ContextMenuProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
14
|
+
"click-item": (item: DropdownMenuAction & {
|
|
15
|
+
id?: string;
|
|
16
|
+
}) => any;
|
|
17
|
+
"update:open": (open: boolean) => any;
|
|
18
|
+
show: () => any;
|
|
19
|
+
hide: () => any;
|
|
20
|
+
}, string, import("vue").PublicProps, Readonly<ContextMenuProps> & Readonly<{
|
|
21
|
+
"onClick-item"?: ((item: DropdownMenuAction & {
|
|
22
|
+
id?: string;
|
|
23
|
+
}) => any) | undefined;
|
|
24
|
+
"onUpdate:open"?: ((open: boolean) => any) | undefined;
|
|
25
|
+
onShow?: (() => any) | undefined;
|
|
26
|
+
onHide?: (() => any) | undefined;
|
|
27
|
+
}>, {
|
|
28
|
+
label: string;
|
|
29
|
+
confirmLabel: string;
|
|
30
|
+
cancelLabel: string;
|
|
31
|
+
id: string;
|
|
32
|
+
confirmTitle: string;
|
|
33
|
+
confirmMessage: string;
|
|
34
|
+
deleteLabel: string;
|
|
35
|
+
items: DropdownMenuEntry[];
|
|
36
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
37
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
38
|
+
declare const _default: typeof __VLS_export;
|
|
39
|
+
export default _default;
|
|
40
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
41
|
+
new (): {
|
|
42
|
+
$slots: S;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import e from "../internal/MenuItemContent.js";
|
|
2
|
+
import { menuConfirmDefaults as t, menuContentClass as n, menuItemClass as r, menuSeparatorClass as i, useMenuSelect as a } from "../internal/menu.js";
|
|
3
|
+
import { Fragment as o, createBlock as s, createElementBlock as c, createVNode as l, defineComponent as u, mergeDefaults as d, normalizeClass as f, openBlock as p, renderList as m, renderSlot as h, unref as g, withCtx as _ } from "vue";
|
|
4
|
+
import { ContextMenuContent as v, ContextMenuItem as y, ContextMenuPortal as b, ContextMenuRoot as x, ContextMenuSeparator as S, ContextMenuTrigger as C } from "reka-ui";
|
|
5
|
+
//#region src/components/ContextMenu/ContextMenu.vue?vue&type=script&setup=true&lang.ts
|
|
6
|
+
var w = /*@__PURE__*/ u({
|
|
7
|
+
__name: "ContextMenu",
|
|
8
|
+
props: /*@__PURE__*/ d({
|
|
9
|
+
items: {},
|
|
10
|
+
label: {},
|
|
11
|
+
disabled: { type: Boolean },
|
|
12
|
+
id: {},
|
|
13
|
+
confirmTitle: {},
|
|
14
|
+
confirmMessage: {},
|
|
15
|
+
confirmLabel: {},
|
|
16
|
+
deleteLabel: {},
|
|
17
|
+
cancelLabel: {}
|
|
18
|
+
}, {
|
|
19
|
+
...t,
|
|
20
|
+
items: () => [],
|
|
21
|
+
label: void 0
|
|
22
|
+
}),
|
|
23
|
+
emits: [
|
|
24
|
+
"click-item",
|
|
25
|
+
"update:open",
|
|
26
|
+
"show",
|
|
27
|
+
"hide"
|
|
28
|
+
],
|
|
29
|
+
setup(t, { emit: u }) {
|
|
30
|
+
let d = t, w = u, T = a(d, (e) => w("click-item", e));
|
|
31
|
+
function E(e) {
|
|
32
|
+
w("update:open", e), w(e ? "show" : "hide");
|
|
33
|
+
}
|
|
34
|
+
function D(e) {
|
|
35
|
+
if (d.disabled || !(e.key === "F10" && e.shiftKey || e.key === "ContextMenu")) return;
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
let t = e.currentTarget, n = e.target.getBoundingClientRect();
|
|
38
|
+
t.dispatchEvent(new MouseEvent("contextmenu", {
|
|
39
|
+
bubbles: !0,
|
|
40
|
+
cancelable: !0,
|
|
41
|
+
clientX: n.left + 8,
|
|
42
|
+
clientY: n.top + Math.min(n.height, 32)
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
return (a, u) => (p(), s(g(x), {
|
|
46
|
+
modal: !1,
|
|
47
|
+
"onUpdate:open": E
|
|
48
|
+
}, {
|
|
49
|
+
default: _(() => [l(g(C), {
|
|
50
|
+
"as-child": "",
|
|
51
|
+
disabled: t.disabled,
|
|
52
|
+
onKeydown: D
|
|
53
|
+
}, {
|
|
54
|
+
default: _(() => [h(a.$slots, "default")]),
|
|
55
|
+
_: 3
|
|
56
|
+
}, 8, ["disabled"]), l(g(b), null, {
|
|
57
|
+
default: _(() => [l(g(v), {
|
|
58
|
+
"aria-label": t.label,
|
|
59
|
+
class: f([g(n), "min-w-48"]),
|
|
60
|
+
"collision-padding": 8
|
|
61
|
+
}, {
|
|
62
|
+
default: _(() => [(p(!0), c(o, null, m(t.items, (t, n) => (p(), c(o, { key: n }, [t.type === "separator" ? (p(), s(g(S), {
|
|
63
|
+
key: 0,
|
|
64
|
+
class: f(g(i))
|
|
65
|
+
}, null, 8, ["class"])) : (p(), s(g(y), {
|
|
66
|
+
key: 1,
|
|
67
|
+
class: f(g(r)),
|
|
68
|
+
onSelect: (e) => g(T)(t)
|
|
69
|
+
}, {
|
|
70
|
+
default: _(() => [l(e, { item: t }, null, 8, ["item"])]),
|
|
71
|
+
_: 2
|
|
72
|
+
}, 1032, ["class", "onSelect"]))], 64))), 128))]),
|
|
73
|
+
_: 1
|
|
74
|
+
}, 8, ["aria-label", "class"])]),
|
|
75
|
+
_: 1
|
|
76
|
+
})]),
|
|
77
|
+
_: 3
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
//#endregion
|
|
82
|
+
export { w as default };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=ContextMenu.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContextMenu.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_withCtx","_createVNode","_renderSlot","_normalizeClass","_createElementBlock","_Fragment","_renderList","MenuItemContent","MenuItemContent_default","ContextMenu_vue_vue_type_script_setup_true_lang_default","menuConfirmDefaults","__emit","props","__props","emit","select","useMenuSelect","item","onOpenChange","open","onKeydown","event","target","rect","openBlock","createBlock","unref","ContextMenuRoot","withCtx","createVNode","ContextMenuTrigger","renderSlot","_ctx","ContextMenuPortal","ContextMenuContent","normalizeClass","menuContentClass","createElementBlock","Fragment","renderList","index","ContextMenuSeparator","menuSeparatorClass","ContextMenuItem","menuItemClass"],"sources":["../../../src/components/ContextMenu/ContextMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuPortal,\n ContextMenuRoot,\n ContextMenuSeparator,\n ContextMenuTrigger\n} from 'reka-ui'\n\nimport MenuItemContent from '../internal/MenuItemContent.vue'\nimport {\n type DropdownMenuAction,\n type DropdownMenuEntry,\n type MenuConfirmLabels,\n menuConfirmDefaults,\n menuContentClass,\n menuItemClass,\n menuSeparatorClass,\n useMenuSelect\n} from '../internal/menu'\n\nexport interface ContextMenuProps extends MenuConfirmLabels {\n items?: DropdownMenuEntry[]\n /** The menu's accessible name, e.g. \"Post actions\". */\n label?: string\n /** Lets the browser's own menu through. */\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ContextMenuProps>(), {\n ...menuConfirmDefaults,\n items: () => [],\n label: undefined\n})\n\nconst emit = defineEmits<{\n 'click-item': [item: DropdownMenuAction & { id?: string }]\n 'update:open': [open: boolean]\n show: []\n hide: []\n}>()\n\nconst select = useMenuSelect(props, (item) => emit('click-item', item))\n\nfunction onOpenChange(open: boolean) {\n emit('update:open', open)\n if (open) emit('show')\n else emit('hide')\n}\n\n// Shift+F10 and the Menu key open it at the focused element. Browsers fire `contextmenu` for these only on some\n// platforms, so the keys open it themselves, the way a right-click would, near the element's top left.\nfunction onKeydown(event: KeyboardEvent) {\n if (props.disabled || !((event.key === 'F10' && event.shiftKey) || event.key === 'ContextMenu')) return\n event.preventDefault()\n const target = event.currentTarget as HTMLElement\n const rect = (event.target as HTMLElement).getBoundingClientRect()\n target.dispatchEvent(\n new MouseEvent('contextmenu', {\n bubbles: true,\n cancelable: true,\n clientX: rect.left + 8,\n clientY: rect.top + Math.min(rect.height, 32)\n })\n )\n}\n</script>\n\n<!-- DropdownMenu's items at the pointer: right-click the default slot, or Shift+F10 or the Menu key while it has focus. -->\n<template>\n <ContextMenuRoot :modal=\"false\" @update:open=\"onOpenChange\">\n <ContextMenuTrigger as-child :disabled=\"disabled\" @keydown=\"onKeydown\">\n <slot />\n </ContextMenuTrigger>\n <ContextMenuPortal>\n <ContextMenuContent :aria-label=\"label\" :class=\"[menuContentClass, 'min-w-48']\" :collision-padding=\"8\">\n <template v-for=\"(item, index) in items\" :key=\"index\">\n <ContextMenuSeparator v-if=\"item.type === 'separator'\" :class=\"menuSeparatorClass\" />\n <ContextMenuItem v-else :class=\"menuItemClass\" @select=\"select(item)\">\n <MenuItemContent :item=\"item\" />\n </ContextMenuItem>\n </template>\n </ContextMenuContent>\n </ContextMenuPortal>\n </ContextMenuRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BA,IAAMe,IAAQC,GAMRC,IAAOH,GAOPI,IAASC,EAAcJ,IAAQ,MAASE,EAAK,cAAcG,CAAI,CAAC;EAEtE,SAASC,EAAa,GAAe;GAEnC,AADAJ,EAAK,eAAeK,CAAI,GACdL,EAANK,IAAW,SACL,MAAM;EAClB;EAIA,SAASC,EAAU,GAAsB;GACvC,IAAIR,EAAM,YAAY,EAAGS,EAAM,QAAQ,SAASA,EAAM,YAAaA,EAAM,QAAQ,gBAAgB;GACjG,EAAM,eAAe;GACrB,IAAMC,IAASD,EAAM,eACfE,IAAQF,EAAM,OAAuB,sBAAsB;GACjE,EAAO,cACL,IAAI,WAAW,eAAe;IAC5B,SAAS;IACT,YAAY;IACZ,SAASE,EAAK,OAAO;IACrB,SAASA,EAAK,MAAM,KAAK,IAAIA,EAAK,QAAQ,EAAE;GAC9C,CAAC,CACH;EACF;E,kBAKE1B,EAAA,GAAAC,EAckBC,EAAA4B,CAAA,GAAA;GAdA,OAAO;GAAQ,iBAAaT;;GAC5C,SAAAlB,QAEqB,CAFrBC,EAEqBF,EAAA+B,CAAA,GAAA;IAFD,YAAA;IAAU,UAAUjB,EAAA;IAAoB;;IAC1D,SAAAb,QAAQ,CAARE,EAAQ8B,EAAA,QAAA,SAAA,C;;G,qBAEV/B,EASoBF,EAAAkC,CAAA,GAAA,MAAA;IARlB,SAAAjC,QAOqB,CAPrBC,EAOqBF,EAAAmC,CAAA,GAAA;KAPA,cAAYrB,EAAA;KAAQ,OAAKV,EAAA,CAAGJ,EAAAqC,CAAA,GAAgB,UAAA,CAAA;KAAgB,qBAAmB;;KACxF,SAAApC,QAA8B,EAAxCH,EAAA,EAAA,GAAAO,EAKWC,GAAA,MAAAC,EALuBO,EAAA,QAAhB,GAAM,OAAuBhB,EAAA,GAAAO,EAAAC,GAAA,EAAA,KAAAmC,EAAK,GAAA,CACtBvB,EAAK,SAAI,eAArCpB,EAAA,GAAAC,EAAqFC,EAAA0C,CAAA,GAAA;;MAA7B,OAAKtC,EAAEJ,EAAA2C,CAAA,CAAkB;K,2BACjF7C,EAAA,GAAAC,EAEkBC,EAAA4C,CAAA,GAAA;;MAFO,OAAKxC,EAAEJ,EAAA6C,CAAA,CAAa;MAAG,WAAM,MAAE7C,EAAAgB,CAAA,CAAM,CAACE,CAAI;;MACjE,SAAAjB,QAAgC,CAAhCC,EAAgCM,GAAA,EAAR,QAAI,GAAA,MAAA,GAAA,CAAA,MAAA,CAAA,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataList.js","names":["_sfc_main","DataList_vue_vue_type_script_setup_true_lang_default","DataList_default"],"sources":["../../../src/components/DataList/DataList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { computed, useId, useTemplateRef } from 'vue'\n\nimport Checkbox from '../Checkbox/Checkbox.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { DataListGroup } from './types'\n\nexport interface DataListProps<T> {\n groups: DataListGroup<T>[]\n /** The item field that identifies it. An item's `depth` indents it as a child. */\n rowKey?: keyof T & string\n /** A checkbox gutter bound to `v-model:selected`: it shows on hover, and on every row once anything is selected. */\n selectable?: boolean\n loading?: boolean\n /** The group headings' level, to fit the page's outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n /** Makes rows open on click and Enter (`@row-click`). */\n onRowClick?: (item: T) => void\n /** Adds a + to each group's header (`@new`). */\n onNew?: (group: DataListGroup<T>) => void\n /** The + button's name; `{group}` is the group's name. */\n newLabel?: string\n collapseLabel?: string\n expandLabel?: string\n selectLabel?: string\n}\n\nconst props = withDefaults(defineProps<DataListProps<T>>(), {\n rowKey: 'id' as never,\n headingLevel: 3,\n onRowClick: undefined,\n onNew: undefined,\n newLabel: 'New in {group}',\n collapseLabel: 'Collapse group',\n expandLabel: 'Expand group',\n selectLabel: 'Select'\n})\n\nconst selected = defineModel<unknown[]>('selected', { default: () => [] })\nconst collapsed = defineModel<string[]>('collapsed', { default: () => [] })\n\ndefineSlots<{\n /** Marks before a group's name, e.g. a status icon. */\n 'group-leading'?: (props: { group: DataListGroup<T> }) => unknown\n /** Small marks before an item's title: a status icon, an id, an avatar. */\n leading?: (props: { item: T }) => unknown\n /** The item's title; `item.name` by default. */\n title?: (props: { item: T }) => unknown\n /** Right-aligned meta: tags, an avatar, a date. Keep it to three or four things. */\n meta?: (props: { item: T }) => unknown\n /** Shown when every group is empty, e.g. an EmptyState. */\n empty?: () => unknown\n}>()\n\nconst id = useId()\nconst root = useTemplateRef<HTMLElement>('root')\n\nconst idOf = (item: T): unknown => item[props.rowKey ?? 'id']\nconst interactive = computed(() => !!props.onRowClick || props.selectable)\nconst isEmpty = computed(() => !props.loading && props.groups.every((group) => group.items.length === 0))\n\nfunction toggleGroup(groupId: string) {\n collapsed.value = collapsed.value.includes(groupId)\n ? collapsed.value.filter((x) => x !== groupId)\n : [...collapsed.value, groupId]\n}\n\nfunction toggle(item: T) {\n const key = idOf(item)\n selected.value = selected.value.includes(key) ? selected.value.filter((x) => x !== key) : [...selected.value, key]\n}\n\n// ↑ ↓ or j k move between rows, x selects, Enter and Space open.\nfunction onKeydown(event: KeyboardEvent, item: T) {\n if (event.target !== event.currentTarget || event.metaKey || event.ctrlKey || event.altKey) return\n const rows = [...(root.value?.querySelectorAll<HTMLElement>('[data-list-row]') ?? [])]\n const i = rows.indexOf(event.currentTarget as HTMLElement)\n if (event.key === 'ArrowDown' || event.key === 'j') rows[Math.min(rows.length - 1, i + 1)]?.focus()\n else if (event.key === 'ArrowUp' || event.key === 'k') rows[Math.max(0, i - 1)]?.focus()\n else if (event.key === 'x' && props.selectable) toggle(item)\n else if ((event.key === 'Enter' || event.key === ' ') && props.onRowClick) props.onRowClick(item)\n else return\n event.preventDefault()\n}\n\nconst skeletonWidths = ['42%', '58%', '35%', '50%', '46%']\nconst iconButton =\n 'flex size-6 shrink-0 items-center justify-center rounded-md text-sui-ink-muted outline-hidden transition-colors duration-150 hover:bg-sui-line-subtle hover:text-sui-ink focus-visible:sui-ring-inset'\n</script>\n\n<!-- A grouped list: one dense line per record under sticky, collapsible group headers. -->\n<template>\n <div\n ref=\"root\"\n class=\"flex flex-col overflow-clip rounded-xl border border-sui-line-subtle bg-sui-surface text-13 text-sui-ink\"\n >\n <slot v-if=\"isEmpty && $slots.empty\" name=\"empty\" />\n <section v-else-if=\"loading\" :aria-busy=\"true\">\n <div\n v-for=\"(width, i) in skeletonWidths\"\n :key=\"i\"\n aria-hidden=\"true\"\n class=\"flex h-10 items-center gap-2.5 pr-4 pl-1\"\n >\n <span class=\"w-6 shrink-0\" />\n <Skeleton height=\"0.75rem\" :width=\"width\" />\n <span class=\"flex-1\" />\n <Skeleton height=\"0.75rem\" width=\"64px\" />\n </div>\n </section>\n <template v-else>\n <section\n v-for=\"group in groups\"\n :key=\"group.id\"\n :aria-labelledby=\"group.name ? `${id}-${group.id}-name` : undefined\"\n class=\"border-t border-sui-line-subtle first:border-t-0\"\n >\n <header\n v-if=\"group.name\"\n class=\"sticky top-0 z-10 flex h-9 items-center gap-2 border-b border-sui-line-subtle bg-sui-sunken pr-2 pl-1.5\"\n >\n <button\n :aria-controls=\"`${id}-${group.id}`\"\n :aria-expanded=\"!collapsed.includes(group.id)\"\n :aria-label=\"collapsed.includes(group.id) ? expandLabel : collapseLabel\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"toggleGroup(group.id)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3 fill-none stroke-current transition-transform duration-150\"\n :class=\"collapsed.includes(group.id) && '-rotate-90'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 12 12\"\n >\n <path d=\"M3 4.5 6 7.5l3-3\" />\n </svg>\n </button>\n <slot :group=\"group\" name=\"group-leading\" />\n <component\n :is=\"`h${headingLevel}`\"\n :id=\"`${id}-${group.id}-name`\"\n class=\"m-0 text-13 font-medium text-sui-ink\"\n >\n {{ group.name }}\n </component>\n <span class=\"text-13 text-sui-ink-muted tabular-nums\">{{ group.count ?? group.items.length }}</span>\n <button\n v-if=\"onNew\"\n :aria-label=\"newLabel.replace('{group}', group.name)\"\n class=\"ml-auto\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"onNew(group)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3.5 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 14 14\"\n >\n <path d=\"M7 2.5v9M2.5 7h9\" />\n </svg>\n </button>\n </header>\n <ul v-show=\"!collapsed.includes(group.id)\" :id=\"`${id}-${group.id}`\" class=\"m-0 list-none p-0\">\n <li\n v-for=\"item in group.items\"\n :key=\"idOf(item) as PropertyKey\"\n class=\"group/row flex h-10 items-center transition-colors duration-100 has-focus-visible:sui-ring-inset\"\n :class=\"\n selected.includes(idOf(item))\n ? 'bg-sui-accent-wash'\n : 'hover:bg-sui-sunken has-focus-visible:bg-sui-sunken'\n \"\n :style=\"item.depth ? { paddingLeft: `${item.depth * 22}px` } : undefined\"\n >\n <span\n v-if=\"selectable\"\n class=\"flex w-7 shrink-0 justify-end transition-opacity duration-100\"\n :class=\"selected.length ? 'opacity-100' : 'opacity-0 group-hover/row:opacity-100'\"\n >\n <Checkbox\n :aria-label=\"selectLabel\"\n :model-value=\"selected.includes(idOf(item))\"\n tabindex=\"-1\"\n @update:model-value=\"toggle(item)\"\n />\n </span>\n <div\n class=\"flex h-full min-w-0 flex-1 items-center gap-2.5 pr-4 outline-hidden\"\n :class=\"[selectable ? 'pl-2.5' : 'pl-4', onRowClick && 'cursor-pointer']\"\n data-list-row\n :role=\"onRowClick ? 'button' : undefined\"\n :tabindex=\"interactive ? 0 : undefined\"\n @click=\"onRowClick?.(item)\"\n @keydown=\"onKeydown($event, item)\"\n >\n <span v-if=\"$slots.leading\" class=\"flex shrink-0 items-center gap-2 text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"leading\" />\n </span>\n <span class=\"min-w-0 flex-1 truncate font-medium text-sui-ink\">\n <slot :item=\"item\" name=\"title\">{{ item.name }}</slot>\n </span>\n <span v-if=\"$slots.meta\" class=\"flex shrink-0 items-center gap-2 text-xs text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"meta\" />\n </span>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"DataList.js","names":["_sfc_main","DataList_vue_vue_type_script_setup_true_lang_default","DataList_default"],"sources":["../../../src/components/DataList/DataList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { computed, useId, useTemplateRef } from 'vue'\n\nimport Checkbox from '../Checkbox/Checkbox.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { DataListGroup } from './types'\n\nexport interface DataListProps<T> {\n groups: DataListGroup<T>[]\n /** The item field that identifies it. An item's `depth` indents it as a child. */\n rowKey?: keyof T & string\n /** A checkbox gutter bound to `v-model:selected`: it shows on hover, and on every row once anything is selected. */\n selectable?: boolean\n loading?: boolean\n /** joined: one frame, sticky sunken group headers. cards: each group its own card, 12px apart, headers not sticky. */\n variant?: 'joined' | 'cards'\n /** The group headings' level, to fit the page's outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n /** Makes rows open on click and Enter (`@row-click`). */\n onRowClick?: (item: T) => void\n /** Adds a + to each group's header (`@new`). */\n onNew?: (group: DataListGroup<T>) => void\n /** The + button's name; `{group}` is the group's name. */\n newLabel?: string\n collapseLabel?: string\n expandLabel?: string\n selectLabel?: string\n}\n\nconst props = withDefaults(defineProps<DataListProps<T>>(), {\n rowKey: 'id' as never,\n variant: 'joined',\n headingLevel: 3,\n onRowClick: undefined,\n onNew: undefined,\n newLabel: 'New in {group}',\n collapseLabel: 'Collapse group',\n expandLabel: 'Expand group',\n selectLabel: 'Select'\n})\n\nconst selected = defineModel<unknown[]>('selected', { default: () => [] })\nconst collapsed = defineModel<string[]>('collapsed', { default: () => [] })\n\nconst slots = defineSlots<{\n /** Marks before a group's name, e.g. a status icon. */\n 'group-leading'?: (props: { group: DataListGroup<T> }) => unknown\n /** Your own group header in place of the name and count, e.g. a status Badge and a count; inside the heading. */\n 'group-header'?: (props: { group: DataListGroup<T>; count: number; collapsed: boolean }) => unknown\n /** Small marks before an item's title: a status icon, an id, an avatar. */\n leading?: (props: { item: T }) => unknown\n /** The item's title; `item.name` by default. */\n title?: (props: { item: T }) => unknown\n /** Right-aligned meta: tags, an avatar, a date. Keep it to three or four things. */\n meta?: (props: { item: T }) => unknown\n /** Shown when every group is empty, e.g. an EmptyState. */\n empty?: () => unknown\n}>()\n\nconst id = useId()\nconst root = useTemplateRef<HTMLElement>('root')\n\nconst idOf = (item: T): unknown => item[props.rowKey ?? 'id']\nconst interactive = computed(() => !!props.onRowClick || props.selectable)\nconst isEmpty = computed(() => !props.loading && props.groups.every((group) => group.items.length === 0))\n\nfunction toggleGroup(groupId: string) {\n collapsed.value = collapsed.value.includes(groupId)\n ? collapsed.value.filter((x) => x !== groupId)\n : [...collapsed.value, groupId]\n}\n\nfunction toggle(item: T) {\n const key = idOf(item)\n selected.value = selected.value.includes(key) ? selected.value.filter((x) => x !== key) : [...selected.value, key]\n}\n\n// ↑ ↓ or j k move between rows, x selects, Enter and Space open.\nfunction onKeydown(event: KeyboardEvent, item: T) {\n if (event.target !== event.currentTarget || event.metaKey || event.ctrlKey || event.altKey) return\n const rows = [...(root.value?.querySelectorAll<HTMLElement>('[data-list-row]') ?? [])]\n const i = rows.indexOf(event.currentTarget as HTMLElement)\n if (event.key === 'ArrowDown' || event.key === 'j') rows[Math.min(rows.length - 1, i + 1)]?.focus()\n else if (event.key === 'ArrowUp' || event.key === 'k') rows[Math.max(0, i - 1)]?.focus()\n else if (event.key === 'x' && props.selectable) toggle(item)\n else if ((event.key === 'Enter' || event.key === ' ') && props.onRowClick) props.onRowClick(item)\n else return\n event.preventDefault()\n}\n\nconst cards = computed(() => props.variant === 'cards')\nconst card = 'overflow-clip rounded-xl border border-sui-line-subtle bg-sui-surface'\nconst custom = (group: DataListGroup<T>) => !!slots['group-header'] || group.header !== undefined\nconst groupName = (group: DataListGroup<T>) => group.label ?? group.name ?? ''\n\nconst skeletonWidths = ['42%', '58%', '35%', '50%', '46%']\nconst iconButton =\n 'flex size-6 shrink-0 items-center justify-center rounded-md text-sui-ink-muted outline-hidden transition-colors duration-150 hover:bg-sui-line-subtle hover:text-sui-ink focus-visible:sui-ring-inset'\n</script>\n\n<!-- A grouped list: one dense line per record under sticky, collapsible group headers. -->\n<template>\n <div ref=\"root\" class=\"flex flex-col text-13 text-sui-ink\" :class=\"cards ? 'gap-3' : card\">\n <div v-if=\"isEmpty && $slots.empty\" :class=\"cards && card\">\n <slot name=\"empty\" />\n </div>\n <section v-else-if=\"loading\" :aria-busy=\"true\" :class=\"cards && card\">\n <div\n v-for=\"(width, i) in skeletonWidths\"\n :key=\"i\"\n aria-hidden=\"true\"\n class=\"flex h-10 items-center gap-2.5 pr-4 pl-1\"\n >\n <span class=\"w-6 shrink-0\" />\n <Skeleton height=\"0.75rem\" :width=\"width\" />\n <span class=\"flex-1\" />\n <Skeleton height=\"0.75rem\" width=\"64px\" />\n </div>\n </section>\n <template v-else>\n <section\n v-for=\"group in groups\"\n :key=\"group.id\"\n :aria-label=\"group.label\"\n :aria-labelledby=\"!group.label && (group.name || custom(group)) ? `${id}-${group.id}-name` : undefined\"\n :class=\"cards ? card : 'border-t border-sui-line-subtle first:border-t-0'\"\n >\n <header\n v-if=\"group.name || custom(group)\"\n class=\"flex h-9 items-center gap-2 border-b border-sui-line-subtle pr-2 pl-1.5\"\n :class=\"cards ? 'bg-sui-surface' : 'sticky top-0 z-10 bg-sui-sunken'\"\n >\n <button\n :aria-controls=\"`${id}-${group.id}`\"\n :aria-expanded=\"!collapsed.includes(group.id)\"\n :aria-label=\"collapsed.includes(group.id) ? expandLabel : collapseLabel\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"toggleGroup(group.id)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3 fill-none stroke-current transition-transform duration-150\"\n :class=\"collapsed.includes(group.id) && '-rotate-90'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 12 12\"\n >\n <path d=\"M3 4.5 6 7.5l3-3\" />\n </svg>\n </button>\n <component\n :is=\"`h${headingLevel}`\"\n v-if=\"custom(group)\"\n :id=\"`${id}-${group.id}-name`\"\n class=\"m-0 flex min-w-0 items-center gap-2 text-13 font-medium text-sui-ink\"\n >\n <slot\n :collapsed=\"collapsed.includes(group.id)\"\n :count=\"group.count ?? group.items.length\"\n :group=\"group\"\n name=\"group-header\"\n >{{ group.header }}</slot\n >\n </component>\n <template v-else>\n <slot :group=\"group\" name=\"group-leading\" />\n <component\n :is=\"`h${headingLevel}`\"\n :id=\"`${id}-${group.id}-name`\"\n class=\"m-0 text-13 font-medium text-sui-ink\"\n >\n {{ group.name }}\n </component>\n <span class=\"text-13 text-sui-ink-muted tabular-nums\">{{ group.count ?? group.items.length }}</span>\n </template>\n <button\n v-if=\"onNew\"\n :aria-label=\"newLabel.replace('{group}', groupName(group))\"\n class=\"ml-auto\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"onNew(group)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3.5 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 14 14\"\n >\n <path d=\"M7 2.5v9M2.5 7h9\" />\n </svg>\n </button>\n </header>\n <ul v-show=\"!collapsed.includes(group.id)\" :id=\"`${id}-${group.id}`\" class=\"m-0 list-none p-0\">\n <li\n v-for=\"item in group.items\"\n :key=\"idOf(item) as PropertyKey\"\n class=\"group/row flex h-10 items-center transition-colors duration-100 has-focus-visible:sui-ring-inset\"\n :class=\"\n selected.includes(idOf(item))\n ? 'bg-sui-accent-wash'\n : 'hover:bg-sui-sunken has-focus-visible:bg-sui-sunken'\n \"\n :style=\"item.depth ? { paddingLeft: `${item.depth * 22}px` } : undefined\"\n >\n <span\n v-if=\"selectable\"\n class=\"flex w-7 shrink-0 justify-end transition-opacity duration-100\"\n :class=\"selected.length ? 'opacity-100' : 'opacity-0 group-hover/row:opacity-100'\"\n >\n <Checkbox\n :aria-label=\"selectLabel\"\n :model-value=\"selected.includes(idOf(item))\"\n tabindex=\"-1\"\n @update:model-value=\"toggle(item)\"\n />\n </span>\n <div\n class=\"flex h-full min-w-0 flex-1 items-center gap-2.5 pr-4 outline-hidden\"\n :class=\"[selectable ? 'pl-2.5' : 'pl-4', onRowClick && 'cursor-pointer']\"\n data-list-row\n :role=\"onRowClick ? 'button' : undefined\"\n :tabindex=\"interactive ? 0 : undefined\"\n @click=\"onRowClick?.(item)\"\n @keydown=\"onKeydown($event, item)\"\n >\n <span v-if=\"$slots.leading\" class=\"flex shrink-0 items-center gap-2 text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"leading\" />\n </span>\n <span class=\"min-w-0 flex-1 truncate font-medium text-sui-ink\">\n <slot :item=\"item\" name=\"title\">{{ item.name }}</slot>\n </span>\n <span v-if=\"$slots.meta\" class=\"flex shrink-0 items-center gap-2 text-xs text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"meta\" />\n </span>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -6,6 +6,8 @@ export interface DataListProps<T> {
|
|
|
6
6
|
/** A checkbox gutter bound to `v-model:selected`: it shows on hover, and on every row once anything is selected. */
|
|
7
7
|
selectable?: boolean;
|
|
8
8
|
loading?: boolean;
|
|
9
|
+
/** joined: one frame, sticky sunken group headers. cards: each group its own card, 12px apart, headers not sticky. */
|
|
10
|
+
variant?: 'joined' | 'cards';
|
|
9
11
|
/** The group headings' level, to fit the page's outline. */
|
|
10
12
|
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
11
13
|
/** Makes rows open on click and Enter (`@row-click`). */
|
|
@@ -35,6 +37,12 @@ declare const __VLS_export: <T extends Record<string, any>>(__VLS_props: NonNull
|
|
|
35
37
|
'group-leading'?: (props: {
|
|
36
38
|
group: DataListGroup<T>;
|
|
37
39
|
}) => unknown;
|
|
40
|
+
/** Your own group header in place of the name and count, e.g. a status Badge and a count; inside the heading. */
|
|
41
|
+
'group-header'?: (props: {
|
|
42
|
+
group: DataListGroup<T>;
|
|
43
|
+
count: number;
|
|
44
|
+
collapsed: boolean;
|
|
45
|
+
}) => unknown;
|
|
38
46
|
/** Small marks before an item's title: a status icon, an id, an avatar. */
|
|
39
47
|
leading?: (props: {
|
|
40
48
|
item: T;
|
|
@@ -1,36 +1,31 @@
|
|
|
1
1
|
import e from "../Skeleton/Skeleton.js";
|
|
2
2
|
import t from "../Checkbox/Checkbox.js";
|
|
3
|
-
import { Fragment as n, computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createTextVNode as c, createVNode as l, defineComponent as u, mergeModels as d, normalizeClass as f, normalizeStyle as p, openBlock as m, renderList as h, renderSlot as g, resolveDynamicComponent as _, toDisplayString as v, unref as y, useId as b, useModel as x,
|
|
3
|
+
import { Fragment as n, computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createTextVNode as c, createVNode as l, defineComponent as u, mergeModels as d, normalizeClass as f, normalizeStyle as p, openBlock as m, renderList as h, renderSlot as g, resolveDynamicComponent as _, toDisplayString as v, unref as y, useId as b, useModel as x, useSlots as S, useTemplateRef as C, vShow as w, withCtx as T, withDirectives as E } from "vue";
|
|
4
4
|
//#region src/components/DataList/DataList.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
-
var
|
|
6
|
-
key: 1,
|
|
7
|
-
"aria-busy": !0
|
|
8
|
-
}, D = ["aria-labelledby"], O = {
|
|
9
|
-
key: 0,
|
|
10
|
-
class: "sticky top-0 z-10 flex h-9 items-center gap-2 border-b border-sui-line-subtle bg-sui-sunken pr-2 pl-1.5"
|
|
11
|
-
}, k = [
|
|
5
|
+
var D = ["aria-label", "aria-labelledby"], O = [
|
|
12
6
|
"aria-controls",
|
|
13
7
|
"aria-expanded",
|
|
14
8
|
"aria-label",
|
|
15
9
|
"onClick"
|
|
16
|
-
],
|
|
10
|
+
], k = { class: "text-13 text-sui-ink-muted tabular-nums" }, A = ["aria-label", "onClick"], j = ["id"], M = [
|
|
17
11
|
"role",
|
|
18
12
|
"tabindex",
|
|
19
13
|
"onClick",
|
|
20
14
|
"onKeydown"
|
|
21
|
-
],
|
|
15
|
+
], N = {
|
|
22
16
|
key: 0,
|
|
23
17
|
class: "flex shrink-0 items-center gap-2 text-sui-ink-muted tabular-nums"
|
|
24
|
-
},
|
|
18
|
+
}, P = { class: "min-w-0 flex-1 truncate font-medium text-sui-ink" }, F = {
|
|
25
19
|
key: 1,
|
|
26
20
|
class: "flex shrink-0 items-center gap-2 text-xs text-sui-ink-muted tabular-nums"
|
|
27
|
-
}, L = "flex size-6 shrink-0 items-center justify-center rounded-md text-sui-ink-muted outline-hidden transition-colors duration-150 hover:bg-sui-line-subtle hover:text-sui-ink focus-visible:sui-ring-inset", R = /*@__PURE__*/ u({
|
|
21
|
+
}, I = "overflow-clip rounded-xl border border-sui-line-subtle bg-sui-surface", L = "flex size-6 shrink-0 items-center justify-center rounded-md text-sui-ink-muted outline-hidden transition-colors duration-150 hover:bg-sui-line-subtle hover:text-sui-ink focus-visible:sui-ring-inset", R = /*@__PURE__*/ u({
|
|
28
22
|
__name: "DataList",
|
|
29
23
|
props: /*@__PURE__*/ d({
|
|
30
24
|
groups: {},
|
|
31
25
|
rowKey: { default: "id" },
|
|
32
26
|
selectable: { type: Boolean },
|
|
33
27
|
loading: { type: Boolean },
|
|
28
|
+
variant: { default: "joined" },
|
|
34
29
|
headingLevel: { default: 3 },
|
|
35
30
|
onRowClick: {
|
|
36
31
|
type: Function,
|
|
@@ -52,25 +47,25 @@ var E = {
|
|
|
52
47
|
}),
|
|
53
48
|
emits: ["update:selected", "update:collapsed"],
|
|
54
49
|
setup(u) {
|
|
55
|
-
let d = u, R = x(u, "selected"), z = x(u, "collapsed"), B =
|
|
56
|
-
function
|
|
50
|
+
let d = u, R = x(u, "selected"), z = x(u, "collapsed"), B = S(), V = b(), H = C("root"), U = (e) => e[d.rowKey ?? "id"], W = r(() => !!d.onRowClick || d.selectable), G = r(() => !d.loading && d.groups.every((e) => e.items.length === 0));
|
|
51
|
+
function K(e) {
|
|
57
52
|
z.value = z.value.includes(e) ? z.value.filter((t) => t !== e) : [...z.value, e];
|
|
58
53
|
}
|
|
59
|
-
function
|
|
60
|
-
let t =
|
|
54
|
+
function q(e) {
|
|
55
|
+
let t = U(e);
|
|
61
56
|
R.value = R.value.includes(t) ? R.value.filter((e) => e !== t) : [...R.value, t];
|
|
62
57
|
}
|
|
63
|
-
function
|
|
58
|
+
function J(e, t) {
|
|
64
59
|
if (e.target !== e.currentTarget || e.metaKey || e.ctrlKey || e.altKey) return;
|
|
65
|
-
let n = [...
|
|
60
|
+
let n = [...H.value?.querySelectorAll("[data-list-row]") ?? []], r = n.indexOf(e.currentTarget);
|
|
66
61
|
if (e.key === "ArrowDown" || e.key === "j") n[Math.min(n.length - 1, r + 1)]?.focus();
|
|
67
62
|
else if (e.key === "ArrowUp" || e.key === "k") n[Math.max(0, r - 1)]?.focus();
|
|
68
|
-
else if (e.key === "x" && d.selectable)
|
|
63
|
+
else if (e.key === "x" && d.selectable) q(t);
|
|
69
64
|
else if ((e.key === "Enter" || e.key === " ") && d.onRowClick) d.onRowClick(t);
|
|
70
65
|
else return;
|
|
71
66
|
e.preventDefault();
|
|
72
67
|
}
|
|
73
|
-
let
|
|
68
|
+
let Y = r(() => d.variant === "cards"), X = (e) => !!B["group-header"] || e.header !== void 0, Z = (e) => e.label ?? e.name ?? "", Q = [
|
|
74
69
|
"42%",
|
|
75
70
|
"58%",
|
|
76
71
|
"35%",
|
|
@@ -79,9 +74,16 @@ var E = {
|
|
|
79
74
|
];
|
|
80
75
|
return (r, d) => (m(), o("div", {
|
|
81
76
|
ref_key: "root",
|
|
82
|
-
ref:
|
|
83
|
-
class: "flex flex-col
|
|
84
|
-
}, [
|
|
77
|
+
ref: H,
|
|
78
|
+
class: f(["flex flex-col text-13 text-sui-ink", Y.value ? "gap-3" : I])
|
|
79
|
+
}, [G.value && r.$slots.empty ? (m(), o("div", {
|
|
80
|
+
key: 0,
|
|
81
|
+
class: f(Y.value && I)
|
|
82
|
+
}, [g(r.$slots, "empty")], 2)) : u.loading ? (m(), o("section", {
|
|
83
|
+
key: 1,
|
|
84
|
+
"aria-busy": !0,
|
|
85
|
+
class: f(Y.value && I)
|
|
86
|
+
}, [(m(), o(n, null, h(Q, (t, n) => s("div", {
|
|
85
87
|
key: n,
|
|
86
88
|
"aria-hidden": "true",
|
|
87
89
|
class: "flex h-10 items-center gap-2.5 pr-4 pl-1"
|
|
@@ -96,18 +98,22 @@ var E = {
|
|
|
96
98
|
height: "0.75rem",
|
|
97
99
|
width: "64px"
|
|
98
100
|
})
|
|
99
|
-
])), 64))])) : (m(!0), o(n, { key: 2 }, h(u.groups, (e) => (m(), o("section", {
|
|
101
|
+
])), 64))], 2)) : (m(!0), o(n, { key: 2 }, h(u.groups, (e) => (m(), o("section", {
|
|
100
102
|
key: e.id,
|
|
101
|
-
"aria-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
"aria-label": e.label,
|
|
104
|
+
"aria-labelledby": !e.label && (e.name || X(e)) ? `${y(V)}-${e.id}-name` : void 0,
|
|
105
|
+
class: f(Y.value ? I : "border-t border-sui-line-subtle first:border-t-0")
|
|
106
|
+
}, [e.name || X(e) ? (m(), o("header", {
|
|
107
|
+
key: 0,
|
|
108
|
+
class: f(["flex h-9 items-center gap-2 border-b border-sui-line-subtle pr-2 pl-1.5", Y.value ? "bg-sui-surface" : "sticky top-0 z-10 bg-sui-sunken"])
|
|
109
|
+
}, [
|
|
104
110
|
s("button", {
|
|
105
|
-
"aria-controls": `${y(
|
|
111
|
+
"aria-controls": `${y(V)}-${e.id}`,
|
|
106
112
|
"aria-expanded": !z.value.includes(e.id),
|
|
107
113
|
"aria-label": z.value.includes(e.id) ? u.expandLabel : u.collapseLabel,
|
|
108
114
|
class: f(L),
|
|
109
115
|
type: "button",
|
|
110
|
-
onClick: (t) =>
|
|
116
|
+
onClick: (t) => K(e.id)
|
|
111
117
|
}, [(m(), o("svg", {
|
|
112
118
|
"aria-hidden": "true",
|
|
113
119
|
class: f(["size-3 fill-none stroke-current transition-transform duration-150", z.value.includes(e.id) && "-rotate-90"]),
|
|
@@ -115,19 +121,32 @@ var E = {
|
|
|
115
121
|
"stroke-linejoin": "round",
|
|
116
122
|
"stroke-width": "1.5",
|
|
117
123
|
viewBox: "0 0 12 12"
|
|
118
|
-
}, [...d[2] ||= [s("path", { d: "M3 4.5 6 7.5l3-3" }, null, -1)]], 2))], 8,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
id: `${y(
|
|
122
|
-
class: "m-0 text-13 font-medium text-sui-ink"
|
|
124
|
+
}, [...d[2] ||= [s("path", { d: "M3 4.5 6 7.5l3-3" }, null, -1)]], 2))], 8, O),
|
|
125
|
+
X(e) ? (m(), i(_(`h${u.headingLevel}`), {
|
|
126
|
+
key: 0,
|
|
127
|
+
id: `${y(V)}-${e.id}-name`,
|
|
128
|
+
class: "m-0 flex min-w-0 items-center gap-2 text-13 font-medium text-sui-ink"
|
|
123
129
|
}, {
|
|
124
|
-
default:
|
|
130
|
+
default: T(() => [g(r.$slots, "group-header", {
|
|
131
|
+
collapsed: z.value.includes(e.id),
|
|
132
|
+
count: e.count ?? e.items.length,
|
|
133
|
+
group: e
|
|
134
|
+
}, () => [c(v(e.header), 1)])]),
|
|
125
135
|
_: 2
|
|
126
|
-
}, 1032, ["id"])),
|
|
127
|
-
|
|
136
|
+
}, 1032, ["id"])) : (m(), o(n, { key: 1 }, [
|
|
137
|
+
g(r.$slots, "group-leading", { group: e }),
|
|
138
|
+
(m(), i(_(`h${u.headingLevel}`), {
|
|
139
|
+
id: `${y(V)}-${e.id}-name`,
|
|
140
|
+
class: "m-0 text-13 font-medium text-sui-ink"
|
|
141
|
+
}, {
|
|
142
|
+
default: T(() => [c(v(e.name), 1)]),
|
|
143
|
+
_: 2
|
|
144
|
+
}, 1032, ["id"])),
|
|
145
|
+
s("span", k, v(e.count ?? e.items.length), 1)
|
|
146
|
+
], 64)),
|
|
128
147
|
u.onNew ? (m(), o("button", {
|
|
129
|
-
key:
|
|
130
|
-
"aria-label": u.newLabel.replace("{group}", e
|
|
148
|
+
key: 2,
|
|
149
|
+
"aria-label": u.newLabel.replace("{group}", Z(e)),
|
|
131
150
|
class: f(["ml-auto", L]),
|
|
132
151
|
type: "button",
|
|
133
152
|
onClick: (t) => u.onNew(e)
|
|
@@ -137,22 +156,22 @@ var E = {
|
|
|
137
156
|
"stroke-linecap": "round",
|
|
138
157
|
"stroke-width": "1.5",
|
|
139
158
|
viewBox: "0 0 14 14"
|
|
140
|
-
}, [s("path", { d: "M7 2.5v9M2.5 7h9" })], -1)]], 8,
|
|
141
|
-
])) : a("", !0),
|
|
142
|
-
id: `${y(
|
|
159
|
+
}, [s("path", { d: "M7 2.5v9M2.5 7h9" })], -1)]], 8, A)) : a("", !0)
|
|
160
|
+
], 2)) : a("", !0), E(s("ul", {
|
|
161
|
+
id: `${y(V)}-${e.id}`,
|
|
143
162
|
class: "m-0 list-none p-0"
|
|
144
163
|
}, [(m(!0), o(n, null, h(e.items, (e) => (m(), o("li", {
|
|
145
|
-
key:
|
|
146
|
-
class: f(["group/row flex h-10 items-center transition-colors duration-100 has-focus-visible:sui-ring-inset", R.value.includes(
|
|
164
|
+
key: U(e),
|
|
165
|
+
class: f(["group/row flex h-10 items-center transition-colors duration-100 has-focus-visible:sui-ring-inset", R.value.includes(U(e)) ? "bg-sui-accent-wash" : "hover:bg-sui-sunken has-focus-visible:bg-sui-sunken"]),
|
|
147
166
|
style: p(e.depth ? { paddingLeft: `${e.depth * 22}px` } : void 0)
|
|
148
167
|
}, [u.selectable ? (m(), o("span", {
|
|
149
168
|
key: 0,
|
|
150
169
|
class: f(["flex w-7 shrink-0 justify-end transition-opacity duration-100", R.value.length ? "opacity-100" : "opacity-0 group-hover/row:opacity-100"])
|
|
151
170
|
}, [l(t, {
|
|
152
171
|
"aria-label": u.selectLabel,
|
|
153
|
-
"model-value": R.value.includes(
|
|
172
|
+
"model-value": R.value.includes(U(e)),
|
|
154
173
|
tabindex: "-1",
|
|
155
|
-
"onUpdate:modelValue": (t) =>
|
|
174
|
+
"onUpdate:modelValue": (t) => q(e)
|
|
156
175
|
}, null, 8, [
|
|
157
176
|
"aria-label",
|
|
158
177
|
"model-value",
|
|
@@ -161,14 +180,14 @@ var E = {
|
|
|
161
180
|
class: f(["flex h-full min-w-0 flex-1 items-center gap-2.5 pr-4 outline-hidden", [u.selectable ? "pl-2.5" : "pl-4", u.onRowClick && "cursor-pointer"]]),
|
|
162
181
|
"data-list-row": "",
|
|
163
182
|
role: u.onRowClick ? "button" : void 0,
|
|
164
|
-
tabindex:
|
|
183
|
+
tabindex: W.value ? 0 : void 0,
|
|
165
184
|
onClick: (t) => u.onRowClick?.(e),
|
|
166
|
-
onKeydown: (t) =>
|
|
185
|
+
onKeydown: (t) => J(t, e)
|
|
167
186
|
}, [
|
|
168
|
-
r.$slots.leading ? (m(), o("span",
|
|
169
|
-
s("span",
|
|
170
|
-
r.$slots.meta ? (m(), o("span",
|
|
171
|
-
], 42,
|
|
187
|
+
r.$slots.leading ? (m(), o("span", N, [g(r.$slots, "leading", { item: e })])) : a("", !0),
|
|
188
|
+
s("span", P, [g(r.$slots, "title", { item: e }, () => [c(v(e.name), 1)])]),
|
|
189
|
+
r.$slots.meta ? (m(), o("span", F, [g(r.$slots, "meta", { item: e })])) : a("", !0)
|
|
190
|
+
], 42, M)], 6))), 128))], 8, j), [[w, !z.value.includes(e.id)]])], 10, D))), 128))], 2));
|
|
172
191
|
}
|
|
173
192
|
});
|
|
174
193
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataList.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","_openBlock","_createElementBlock","_renderSlot","_Fragment","_renderList","_createElementVNode","_createVNode","Skeleton","_unref","_normalizeClass","_createBlock","_resolveDynamicComponent","_withCtx","_createTextVNode","_toDisplayString","_createCommentVNode","_withDirectives","_normalizeStyle","Checkbox","_vShow","Skeleton_default","Checkbox_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","_hoisted_11","iconButton","DataList_vue_vue_type_script_setup_true_lang_default","\"selected\"","\"selectedModifiers\"","\"collapsed\"","\"collapsedModifiers\"","props","__props","selected","useModel","collapsed","id","useId","root","useTemplateRef","idOf","item","interactive","computed","isEmpty","group","toggleGroup","groupId","x","toggle","key","onKeydown","event","rows","i","skeletonWidths","openBlock","createElementBlock","_ctx","renderSlot","Fragment","renderList","createElementVNode","_cache","createVNode","unref","normalizeClass","createBlock","resolveDynamicComponent","withCtx","createTextVNode","toDisplayString","createCommentVNode","withDirectives","normalizeStyle","$event","vShow"],"sources":["../../../src/components/DataList/DataList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { computed, useId, useTemplateRef } from 'vue'\n\nimport Checkbox from '../Checkbox/Checkbox.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { DataListGroup } from './types'\n\nexport interface DataListProps<T> {\n groups: DataListGroup<T>[]\n /** The item field that identifies it. An item's `depth` indents it as a child. */\n rowKey?: keyof T & string\n /** A checkbox gutter bound to `v-model:selected`: it shows on hover, and on every row once anything is selected. */\n selectable?: boolean\n loading?: boolean\n /** The group headings' level, to fit the page's outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n /** Makes rows open on click and Enter (`@row-click`). */\n onRowClick?: (item: T) => void\n /** Adds a + to each group's header (`@new`). */\n onNew?: (group: DataListGroup<T>) => void\n /** The + button's name; `{group}` is the group's name. */\n newLabel?: string\n collapseLabel?: string\n expandLabel?: string\n selectLabel?: string\n}\n\nconst props = withDefaults(defineProps<DataListProps<T>>(), {\n rowKey: 'id' as never,\n headingLevel: 3,\n onRowClick: undefined,\n onNew: undefined,\n newLabel: 'New in {group}',\n collapseLabel: 'Collapse group',\n expandLabel: 'Expand group',\n selectLabel: 'Select'\n})\n\nconst selected = defineModel<unknown[]>('selected', { default: () => [] })\nconst collapsed = defineModel<string[]>('collapsed', { default: () => [] })\n\ndefineSlots<{\n /** Marks before a group's name, e.g. a status icon. */\n 'group-leading'?: (props: { group: DataListGroup<T> }) => unknown\n /** Small marks before an item's title: a status icon, an id, an avatar. */\n leading?: (props: { item: T }) => unknown\n /** The item's title; `item.name` by default. */\n title?: (props: { item: T }) => unknown\n /** Right-aligned meta: tags, an avatar, a date. Keep it to three or four things. */\n meta?: (props: { item: T }) => unknown\n /** Shown when every group is empty, e.g. an EmptyState. */\n empty?: () => unknown\n}>()\n\nconst id = useId()\nconst root = useTemplateRef<HTMLElement>('root')\n\nconst idOf = (item: T): unknown => item[props.rowKey ?? 'id']\nconst interactive = computed(() => !!props.onRowClick || props.selectable)\nconst isEmpty = computed(() => !props.loading && props.groups.every((group) => group.items.length === 0))\n\nfunction toggleGroup(groupId: string) {\n collapsed.value = collapsed.value.includes(groupId)\n ? collapsed.value.filter((x) => x !== groupId)\n : [...collapsed.value, groupId]\n}\n\nfunction toggle(item: T) {\n const key = idOf(item)\n selected.value = selected.value.includes(key) ? selected.value.filter((x) => x !== key) : [...selected.value, key]\n}\n\n// ↑ ↓ or j k move between rows, x selects, Enter and Space open.\nfunction onKeydown(event: KeyboardEvent, item: T) {\n if (event.target !== event.currentTarget || event.metaKey || event.ctrlKey || event.altKey) return\n const rows = [...(root.value?.querySelectorAll<HTMLElement>('[data-list-row]') ?? [])]\n const i = rows.indexOf(event.currentTarget as HTMLElement)\n if (event.key === 'ArrowDown' || event.key === 'j') rows[Math.min(rows.length - 1, i + 1)]?.focus()\n else if (event.key === 'ArrowUp' || event.key === 'k') rows[Math.max(0, i - 1)]?.focus()\n else if (event.key === 'x' && props.selectable) toggle(item)\n else if ((event.key === 'Enter' || event.key === ' ') && props.onRowClick) props.onRowClick(item)\n else return\n event.preventDefault()\n}\n\nconst skeletonWidths = ['42%', '58%', '35%', '50%', '46%']\nconst iconButton =\n 'flex size-6 shrink-0 items-center justify-center rounded-md text-sui-ink-muted outline-hidden transition-colors duration-150 hover:bg-sui-line-subtle hover:text-sui-ink focus-visible:sui-ring-inset'\n</script>\n\n<!-- A grouped list: one dense line per record under sticky, collapsible group headers. -->\n<template>\n <div\n ref=\"root\"\n class=\"flex flex-col overflow-clip rounded-xl border border-sui-line-subtle bg-sui-surface text-13 text-sui-ink\"\n >\n <slot v-if=\"isEmpty && $slots.empty\" name=\"empty\" />\n <section v-else-if=\"loading\" :aria-busy=\"true\">\n <div\n v-for=\"(width, i) in skeletonWidths\"\n :key=\"i\"\n aria-hidden=\"true\"\n class=\"flex h-10 items-center gap-2.5 pr-4 pl-1\"\n >\n <span class=\"w-6 shrink-0\" />\n <Skeleton height=\"0.75rem\" :width=\"width\" />\n <span class=\"flex-1\" />\n <Skeleton height=\"0.75rem\" width=\"64px\" />\n </div>\n </section>\n <template v-else>\n <section\n v-for=\"group in groups\"\n :key=\"group.id\"\n :aria-labelledby=\"group.name ? `${id}-${group.id}-name` : undefined\"\n class=\"border-t border-sui-line-subtle first:border-t-0\"\n >\n <header\n v-if=\"group.name\"\n class=\"sticky top-0 z-10 flex h-9 items-center gap-2 border-b border-sui-line-subtle bg-sui-sunken pr-2 pl-1.5\"\n >\n <button\n :aria-controls=\"`${id}-${group.id}`\"\n :aria-expanded=\"!collapsed.includes(group.id)\"\n :aria-label=\"collapsed.includes(group.id) ? expandLabel : collapseLabel\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"toggleGroup(group.id)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3 fill-none stroke-current transition-transform duration-150\"\n :class=\"collapsed.includes(group.id) && '-rotate-90'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 12 12\"\n >\n <path d=\"M3 4.5 6 7.5l3-3\" />\n </svg>\n </button>\n <slot :group=\"group\" name=\"group-leading\" />\n <component\n :is=\"`h${headingLevel}`\"\n :id=\"`${id}-${group.id}-name`\"\n class=\"m-0 text-13 font-medium text-sui-ink\"\n >\n {{ group.name }}\n </component>\n <span class=\"text-13 text-sui-ink-muted tabular-nums\">{{ group.count ?? group.items.length }}</span>\n <button\n v-if=\"onNew\"\n :aria-label=\"newLabel.replace('{group}', group.name)\"\n class=\"ml-auto\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"onNew(group)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3.5 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 14 14\"\n >\n <path d=\"M7 2.5v9M2.5 7h9\" />\n </svg>\n </button>\n </header>\n <ul v-show=\"!collapsed.includes(group.id)\" :id=\"`${id}-${group.id}`\" class=\"m-0 list-none p-0\">\n <li\n v-for=\"item in group.items\"\n :key=\"idOf(item) as PropertyKey\"\n class=\"group/row flex h-10 items-center transition-colors duration-100 has-focus-visible:sui-ring-inset\"\n :class=\"\n selected.includes(idOf(item))\n ? 'bg-sui-accent-wash'\n : 'hover:bg-sui-sunken has-focus-visible:bg-sui-sunken'\n \"\n :style=\"item.depth ? { paddingLeft: `${item.depth * 22}px` } : undefined\"\n >\n <span\n v-if=\"selectable\"\n class=\"flex w-7 shrink-0 justify-end transition-opacity duration-100\"\n :class=\"selected.length ? 'opacity-100' : 'opacity-0 group-hover/row:opacity-100'\"\n >\n <Checkbox\n :aria-label=\"selectLabel\"\n :model-value=\"selected.includes(idOf(item))\"\n tabindex=\"-1\"\n @update:model-value=\"toggle(item)\"\n />\n </span>\n <div\n class=\"flex h-full min-w-0 flex-1 items-center gap-2.5 pr-4 outline-hidden\"\n :class=\"[selectable ? 'pl-2.5' : 'pl-4', onRowClick && 'cursor-pointer']\"\n data-list-row\n :role=\"onRowClick ? 'button' : undefined\"\n :tabindex=\"interactive ? 0 : undefined\"\n @click=\"onRowClick?.(item)\"\n @keydown=\"onKeydown($event, item)\"\n >\n <span v-if=\"$slots.leading\" class=\"flex shrink-0 items-center gap-2 text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"leading\" />\n </span>\n <span class=\"min-w-0 flex-1 truncate font-medium text-sui-ink\">\n <slot :item=\"item\" name=\"title\">{{ item.name }}</slot>\n </span>\n <span v-if=\"$slots.meta\" class=\"flex shrink-0 items-center gap-2 text-xs text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"meta\" />\n </span>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataList.vue_vue_type_script_setup_true_lang.js","names":["$slots","_useModel","_useSlots","_openBlock","_createElementBlock","_normalizeClass","_renderSlot","_Fragment","_renderList","_createElementVNode","_createVNode","Skeleton","_unref","_createBlock","_resolveDynamicComponent","_withCtx","_createTextVNode","_toDisplayString","_createCommentVNode","_withDirectives","_normalizeStyle","Checkbox","_vShow","Skeleton_default","Checkbox_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","card","iconButton","DataList_vue_vue_type_script_setup_true_lang_default","\"selected\"","\"selectedModifiers\"","\"collapsed\"","\"collapsedModifiers\"","props","__props","selected","useModel","collapsed","slots","useSlots","id","useId","root","useTemplateRef","idOf","item","interactive","computed","isEmpty","group","toggleGroup","groupId","x","toggle","key","onKeydown","event","rows","i","cards","custom","groupName","skeletonWidths","openBlock","createElementBlock","normalizeClass","_ctx","renderSlot","Fragment","renderList","createElementVNode","_cache","createVNode","unref","createBlock","resolveDynamicComponent","withCtx","createTextVNode","toDisplayString","createCommentVNode","withDirectives","normalizeStyle","$event","vShow"],"sources":["../../../src/components/DataList/DataList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { computed, useId, useTemplateRef } from 'vue'\n\nimport Checkbox from '../Checkbox/Checkbox.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { DataListGroup } from './types'\n\nexport interface DataListProps<T> {\n groups: DataListGroup<T>[]\n /** The item field that identifies it. An item's `depth` indents it as a child. */\n rowKey?: keyof T & string\n /** A checkbox gutter bound to `v-model:selected`: it shows on hover, and on every row once anything is selected. */\n selectable?: boolean\n loading?: boolean\n /** joined: one frame, sticky sunken group headers. cards: each group its own card, 12px apart, headers not sticky. */\n variant?: 'joined' | 'cards'\n /** The group headings' level, to fit the page's outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n /** Makes rows open on click and Enter (`@row-click`). */\n onRowClick?: (item: T) => void\n /** Adds a + to each group's header (`@new`). */\n onNew?: (group: DataListGroup<T>) => void\n /** The + button's name; `{group}` is the group's name. */\n newLabel?: string\n collapseLabel?: string\n expandLabel?: string\n selectLabel?: string\n}\n\nconst props = withDefaults(defineProps<DataListProps<T>>(), {\n rowKey: 'id' as never,\n variant: 'joined',\n headingLevel: 3,\n onRowClick: undefined,\n onNew: undefined,\n newLabel: 'New in {group}',\n collapseLabel: 'Collapse group',\n expandLabel: 'Expand group',\n selectLabel: 'Select'\n})\n\nconst selected = defineModel<unknown[]>('selected', { default: () => [] })\nconst collapsed = defineModel<string[]>('collapsed', { default: () => [] })\n\nconst slots = defineSlots<{\n /** Marks before a group's name, e.g. a status icon. */\n 'group-leading'?: (props: { group: DataListGroup<T> }) => unknown\n /** Your own group header in place of the name and count, e.g. a status Badge and a count; inside the heading. */\n 'group-header'?: (props: { group: DataListGroup<T>; count: number; collapsed: boolean }) => unknown\n /** Small marks before an item's title: a status icon, an id, an avatar. */\n leading?: (props: { item: T }) => unknown\n /** The item's title; `item.name` by default. */\n title?: (props: { item: T }) => unknown\n /** Right-aligned meta: tags, an avatar, a date. Keep it to three or four things. */\n meta?: (props: { item: T }) => unknown\n /** Shown when every group is empty, e.g. an EmptyState. */\n empty?: () => unknown\n}>()\n\nconst id = useId()\nconst root = useTemplateRef<HTMLElement>('root')\n\nconst idOf = (item: T): unknown => item[props.rowKey ?? 'id']\nconst interactive = computed(() => !!props.onRowClick || props.selectable)\nconst isEmpty = computed(() => !props.loading && props.groups.every((group) => group.items.length === 0))\n\nfunction toggleGroup(groupId: string) {\n collapsed.value = collapsed.value.includes(groupId)\n ? collapsed.value.filter((x) => x !== groupId)\n : [...collapsed.value, groupId]\n}\n\nfunction toggle(item: T) {\n const key = idOf(item)\n selected.value = selected.value.includes(key) ? selected.value.filter((x) => x !== key) : [...selected.value, key]\n}\n\n// ↑ ↓ or j k move between rows, x selects, Enter and Space open.\nfunction onKeydown(event: KeyboardEvent, item: T) {\n if (event.target !== event.currentTarget || event.metaKey || event.ctrlKey || event.altKey) return\n const rows = [...(root.value?.querySelectorAll<HTMLElement>('[data-list-row]') ?? [])]\n const i = rows.indexOf(event.currentTarget as HTMLElement)\n if (event.key === 'ArrowDown' || event.key === 'j') rows[Math.min(rows.length - 1, i + 1)]?.focus()\n else if (event.key === 'ArrowUp' || event.key === 'k') rows[Math.max(0, i - 1)]?.focus()\n else if (event.key === 'x' && props.selectable) toggle(item)\n else if ((event.key === 'Enter' || event.key === ' ') && props.onRowClick) props.onRowClick(item)\n else return\n event.preventDefault()\n}\n\nconst cards = computed(() => props.variant === 'cards')\nconst card = 'overflow-clip rounded-xl border border-sui-line-subtle bg-sui-surface'\nconst custom = (group: DataListGroup<T>) => !!slots['group-header'] || group.header !== undefined\nconst groupName = (group: DataListGroup<T>) => group.label ?? group.name ?? ''\n\nconst skeletonWidths = ['42%', '58%', '35%', '50%', '46%']\nconst iconButton =\n 'flex size-6 shrink-0 items-center justify-center rounded-md text-sui-ink-muted outline-hidden transition-colors duration-150 hover:bg-sui-line-subtle hover:text-sui-ink focus-visible:sui-ring-inset'\n</script>\n\n<!-- A grouped list: one dense line per record under sticky, collapsible group headers. -->\n<template>\n <div ref=\"root\" class=\"flex flex-col text-13 text-sui-ink\" :class=\"cards ? 'gap-3' : card\">\n <div v-if=\"isEmpty && $slots.empty\" :class=\"cards && card\">\n <slot name=\"empty\" />\n </div>\n <section v-else-if=\"loading\" :aria-busy=\"true\" :class=\"cards && card\">\n <div\n v-for=\"(width, i) in skeletonWidths\"\n :key=\"i\"\n aria-hidden=\"true\"\n class=\"flex h-10 items-center gap-2.5 pr-4 pl-1\"\n >\n <span class=\"w-6 shrink-0\" />\n <Skeleton height=\"0.75rem\" :width=\"width\" />\n <span class=\"flex-1\" />\n <Skeleton height=\"0.75rem\" width=\"64px\" />\n </div>\n </section>\n <template v-else>\n <section\n v-for=\"group in groups\"\n :key=\"group.id\"\n :aria-label=\"group.label\"\n :aria-labelledby=\"!group.label && (group.name || custom(group)) ? `${id}-${group.id}-name` : undefined\"\n :class=\"cards ? card : 'border-t border-sui-line-subtle first:border-t-0'\"\n >\n <header\n v-if=\"group.name || custom(group)\"\n class=\"flex h-9 items-center gap-2 border-b border-sui-line-subtle pr-2 pl-1.5\"\n :class=\"cards ? 'bg-sui-surface' : 'sticky top-0 z-10 bg-sui-sunken'\"\n >\n <button\n :aria-controls=\"`${id}-${group.id}`\"\n :aria-expanded=\"!collapsed.includes(group.id)\"\n :aria-label=\"collapsed.includes(group.id) ? expandLabel : collapseLabel\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"toggleGroup(group.id)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3 fill-none stroke-current transition-transform duration-150\"\n :class=\"collapsed.includes(group.id) && '-rotate-90'\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 12 12\"\n >\n <path d=\"M3 4.5 6 7.5l3-3\" />\n </svg>\n </button>\n <component\n :is=\"`h${headingLevel}`\"\n v-if=\"custom(group)\"\n :id=\"`${id}-${group.id}-name`\"\n class=\"m-0 flex min-w-0 items-center gap-2 text-13 font-medium text-sui-ink\"\n >\n <slot\n :collapsed=\"collapsed.includes(group.id)\"\n :count=\"group.count ?? group.items.length\"\n :group=\"group\"\n name=\"group-header\"\n >{{ group.header }}</slot\n >\n </component>\n <template v-else>\n <slot :group=\"group\" name=\"group-leading\" />\n <component\n :is=\"`h${headingLevel}`\"\n :id=\"`${id}-${group.id}-name`\"\n class=\"m-0 text-13 font-medium text-sui-ink\"\n >\n {{ group.name }}\n </component>\n <span class=\"text-13 text-sui-ink-muted tabular-nums\">{{ group.count ?? group.items.length }}</span>\n </template>\n <button\n v-if=\"onNew\"\n :aria-label=\"newLabel.replace('{group}', groupName(group))\"\n class=\"ml-auto\"\n :class=\"iconButton\"\n type=\"button\"\n @click=\"onNew(group)\"\n >\n <svg\n aria-hidden=\"true\"\n class=\"size-3.5 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-width=\"1.5\"\n viewBox=\"0 0 14 14\"\n >\n <path d=\"M7 2.5v9M2.5 7h9\" />\n </svg>\n </button>\n </header>\n <ul v-show=\"!collapsed.includes(group.id)\" :id=\"`${id}-${group.id}`\" class=\"m-0 list-none p-0\">\n <li\n v-for=\"item in group.items\"\n :key=\"idOf(item) as PropertyKey\"\n class=\"group/row flex h-10 items-center transition-colors duration-100 has-focus-visible:sui-ring-inset\"\n :class=\"\n selected.includes(idOf(item))\n ? 'bg-sui-accent-wash'\n : 'hover:bg-sui-sunken has-focus-visible:bg-sui-sunken'\n \"\n :style=\"item.depth ? { paddingLeft: `${item.depth * 22}px` } : undefined\"\n >\n <span\n v-if=\"selectable\"\n class=\"flex w-7 shrink-0 justify-end transition-opacity duration-100\"\n :class=\"selected.length ? 'opacity-100' : 'opacity-0 group-hover/row:opacity-100'\"\n >\n <Checkbox\n :aria-label=\"selectLabel\"\n :model-value=\"selected.includes(idOf(item))\"\n tabindex=\"-1\"\n @update:model-value=\"toggle(item)\"\n />\n </span>\n <div\n class=\"flex h-full min-w-0 flex-1 items-center gap-2.5 pr-4 outline-hidden\"\n :class=\"[selectable ? 'pl-2.5' : 'pl-4', onRowClick && 'cursor-pointer']\"\n data-list-row\n :role=\"onRowClick ? 'button' : undefined\"\n :tabindex=\"interactive ? 0 : undefined\"\n @click=\"onRowClick?.(item)\"\n @keydown=\"onKeydown($event, item)\"\n >\n <span v-if=\"$slots.leading\" class=\"flex shrink-0 items-center gap-2 text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"leading\" />\n </span>\n <span class=\"min-w-0 flex-1 truncate font-medium text-sui-ink\">\n <slot :item=\"item\" name=\"title\">{{ item.name }}</slot>\n </span>\n <span v-if=\"$slots.meta\" class=\"flex shrink-0 items-center gap-2 text-xs text-sui-ink-muted tabular-nums\">\n <slot :item=\"item\" name=\"meta\" />\n </span>\n </div>\n </li>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export interface DataListGroup<T> {
|
|
2
2
|
id: string;
|
|
3
|
-
/** Without a name the group has no header. */
|
|
3
|
+
/** Without a name (or a `header`) the group has no header. */
|
|
4
4
|
name?: string;
|
|
5
|
+
/** Text in place of the name and count; for richer content, the `group-header` slot. Give such a group a `label`. */
|
|
6
|
+
header?: string;
|
|
7
|
+
/** The group's accessible name, when its header isn't just its name. */
|
|
8
|
+
label?: string;
|
|
5
9
|
/** Shown instead of the number of items, e.g. a total beyond the loaded page. */
|
|
6
10
|
count?: number;
|
|
7
11
|
items: T[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.js","names":["_sfc_main","Dialog_vue_vue_type_script_setup_true_lang_default","Dialog_default"],"sources":["../../../src/components/Dialog/Dialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\n\nimport { DialogContent, DialogOverlay, DialogPortal, DialogRoot, DialogTitle } from 'reka-ui'\n\nimport Button from '../Button/Button.vue'\nimport Icon from '../Icon/Icon.vue'\nimport Tooltip from '../Tooltip/Tooltip.vue'\nimport CloseIcon from '../internal/CloseIcon.vue'\nimport { focusFirst } from './focus'\n\nexport interface DialogProps {\n title?: string\n /** The close button in the header. */\n closable?: boolean\n closeLabel?: string\n cancelLabel?: string\n /** Shows the standard footer's confirm button, which emits `confirm`, as ⌘ or Ctrl Enter does. */\n confirmLabel?: string\n confirmIcon?: string\n confirmVariant?: 'primary' | 'danger' | 'contrast'\n confirmDisabled?: boolean\n /** sm 432px, md 512px, record 520px (the default with a band), lg 672px, xl 768px. */\n size?: 'sm' | 'md' | 'record' | 'lg' | 'xl'\n /** A record form's tinted header band: a CSS colour such as `rgb(var(--sui-hue-blue-soft))`, or `true` for the accent\n * wash. The `hero` slot sits centred in it. */\n band?: string | boolean\n /** Muted text at the footer's start, e.g. `saveHint()`. */\n hint?: string\n /** The body draws its own header, edge to edge. */\n headerless?: boolean\n}\n\nconst props = withDefaults(defineProps<DialogProps>(), {\n title: undefined,\n closable: true,\n closeLabel: 'Close',\n cancelLabel: 'Cancel',\n confirmLabel: undefined,\n confirmIcon: undefined,\n confirmVariant: 'primary',\n size: undefined,\n band: undefined,\n hint: undefined\n})\n\nconst emit = defineEmits<{ confirm: []; cancel: [] }>()\n\nconst open = defineModel<boolean>('open', { default: false })\n\nconst slots = useSlots()\nconst hasFooter = computed(() => !!slots.footer || !!props.confirmLabel)\nconst banded = computed(() => !!props.band || !!slots.hero)\n\nconst sizes = {\n sm: 'max-w-[27rem]',\n md: 'max-w-lg',\n record: 'max-w-130',\n lg: 'max-w-2xl',\n xl: 'max-w-3xl'\n}\nconst width = computed(() => sizes[props.size ?? (banded.value ? 'record' : 'md')])\nconst bandColour = computed(() => (typeof props.band === 'string' ? props.band : 'rgb(var(--sui-accent-wash))'))\n\nconst iconColours = { primary: 'on-accent', danger: 'on-danger', contrast: 'on-contrast' } as const\n\nfunction cancel() {\n emit('cancel')\n open.value = false\n}\n\nfunction confirmFromKeyboard(event: KeyboardEvent) {\n if (!event.metaKey && !event.ctrlKey) return\n event.preventDefault()\n if (props.confirmLabel && !props.confirmDisabled) emit('confirm')\n}\n</script>\n\n<!-- The one dialog: a header, body and canvas footer; or, with a `band` or `hero`, a record form under a tinted band. -->\n<template>\n <DialogRoot v-model:open=\"open\">\n <DialogPortal>\n <DialogOverlay\n class=\"fixed inset-0 z-[1100] flex items-center justify-center bg-sui-scrim/40 backdrop-blur-[4px] data-[state=closed]:animate-sui-scrim-out data-[state=open]:animate-sui-scrim-in\"\n >\n <DialogContent\n :aria-describedby=\"undefined\"\n class=\"flex max-h-[90vh] w-full flex-col bg-sui-surface shadow-xl outline-hidden data-[state=closed]:animate-sui-dialog-out data-[state=open]:animate-sui-dialog-in\"\n :class=\"[width, banded ? 'rounded-2xl' : 'rounded-xl']\"\n @escape-key-down=\"emit('cancel')\"\n @interact-outside.prevent\n @keydown.enter=\"confirmFromKeyboard\"\n @open-auto-focus=\"focusFirst\"\n >\n <div\n v-if=\"banded\"\n class=\"shrink-0 rounded-t-2xl pt-3.5 pr-3.5 pb-4.5 pl-5 transition-colors duration-200\"\n data-modal-header\n :style=\"{ backgroundColor: bandColour }\"\n >\n <div class=\"flex items-center justify-between gap-3\">\n <DialogTitle class=\"text-13 leading-5 font-medium text-sui-ink-body\">{{ title }}</DialogTitle>\n <Tooltip v-if=\"closable\" :text=\"closeLabel\">\n <Button :aria-label=\"closeLabel\" class=\"shrink-0\" icon-only size=\"sm\" variant=\"ghost\" @click=\"cancel\">\n <CloseIcon class=\"fill-sui-ink-muted stroke-sui-ink-muted\" />\n </Button>\n </Tooltip>\n </div>\n <div v-if=\"$slots.hero\" class=\"mt-0.5 flex flex-col items-center gap-1.5 pr-1.5\">\n <slot name=\"hero\" />\n </div>\n </div>\n <div\n v-else-if=\"!headerless\"\n class=\"flex shrink-0 items-center justify-between gap-3 rounded-t-xl border-b border-sui-line-subtle bg-sui-surface px-5 py-4 text-sui-ink\"\n data-modal-header\n >\n <DialogTitle class=\"min-w-0 truncate text-base font-semibold text-sui-ink\">\n <slot name=\"title\">{{ title }}</slot>\n </DialogTitle>\n <Tooltip v-if=\"closable\" :text=\"closeLabel\">\n <Button :aria-label=\"closeLabel\" class=\"shrink-0\" icon-only size=\"sm\" variant=\"ghost\" @click=\"cancel\">\n <CloseIcon class=\"fill-sui-ink-subtle stroke-sui-ink-subtle\" />\n </Button>\n </Tooltip>\n </div>\n <DialogTitle v-else class=\"sr-only\">{{ title }}</DialogTitle>\n\n <div\n class=\"overflow-y-auto bg-sui-surface text-sm text-sui-ink-emphasis\"\n :class=\"[banded ? 'px-5 pt-0.5 pb-4' : 'p-5', !hasFooter && (banded ? 'rounded-b-2xl' : 'rounded-b-xl')]\"\n data-modal-body\n >\n <slot />\n </div>\n\n <div\n v-if=\"hasFooter\"\n class=\"flex shrink-0 items-center border-t border-sui-line-subtle bg-sui-canvas text-sui-ink\"\n :class=\"banded ? 'gap-2 rounded-b-2xl py-3 pr-4 pl-5' : 'gap-3 rounded-b-xl px-5 py-4'\"\n data-modal-footer\n >\n <slot name=\"footer\">\n <slot name=\"footer-start\" />\n <span v-if=\"hint\" class=\"min-w-0 truncate text-xs text-sui-ink-muted\">{{ hint }}</span>\n <span class=\"flex-1\" />\n <Button type=\"button\" variant=\"ghost\" @click=\"cancel\">{{ cancelLabel }}</Button>\n <Button :disabled=\"confirmDisabled\" type=\"button\" :variant=\"confirmVariant\" @click=\"emit('confirm')\">\n <Icon v-if=\"confirmIcon\" :color=\"iconColours[confirmVariant]\" :name=\"confirmIcon\" />\n {{ confirmLabel }}\n </Button>\n </slot>\n </div>\n </DialogContent>\n </DialogOverlay>\n </DialogPortal>\n </DialogRoot>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Dialog.js","names":["_sfc_main","Dialog_vue_vue_type_script_setup_true_lang_default","Dialog_default"],"sources":["../../../src/components/Dialog/Dialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useSlots, useTemplateRef, watch } from 'vue'\n\nimport { DialogContent, DialogOverlay, DialogPortal, DialogRoot, DialogTitle } from 'reka-ui'\n\nimport Button from '../Button/Button.vue'\nimport Icon from '../Icon/Icon.vue'\nimport Tooltip from '../Tooltip/Tooltip.vue'\nimport CloseIcon from '../internal/CloseIcon.vue'\nimport { focusFirst } from './focus'\nimport { useSheetDrag } from './useSheetDrag'\n\nexport interface DialogProps {\n title?: string\n /** The close button in the header. */\n closable?: boolean\n closeLabel?: string\n cancelLabel?: string\n /** Shows the standard footer's confirm button, which emits `confirm`, as ⌘ or Ctrl Enter does. */\n confirmLabel?: string\n confirmIcon?: string\n confirmVariant?: 'primary' | 'danger' | 'contrast'\n confirmDisabled?: boolean\n /** sm 432px, md 512px, record 520px (the default with a band), lg 672px, xl 768px. */\n size?: 'sm' | 'md' | 'record' | 'lg' | 'xl'\n /** A record form's tinted header band: a CSS colour such as `rgb(var(--sui-hue-blue-soft))`, or `true` for the accent\n * wash. The `hero` slot sits centred in it. */\n band?: string | boolean\n /** Muted text at the footer's start, e.g. `saveHint()`. */\n hint?: string\n /** The body draws its own header, edge to edge. */\n headerless?: boolean\n /** sheet: a bottom sheet with a grab handle, drag to dismiss and a full-width confirm. auto: a sheet on a viewport\n * narrower than `sheetBelow`, a dialog otherwise. A sheet draws no Cancel and no band: dragging it down, the\n * scrim and Escape cancel. */\n presentation?: 'dialog' | 'sheet' | 'auto'\n /** The width in px below which `auto` is a sheet. */\n sheetBelow?: number\n}\n\nconst props = withDefaults(defineProps<DialogProps>(), {\n title: undefined,\n closable: true,\n closeLabel: 'Close',\n cancelLabel: 'Cancel',\n confirmLabel: undefined,\n confirmIcon: undefined,\n confirmVariant: 'primary',\n size: undefined,\n band: undefined,\n hint: undefined,\n presentation: 'dialog',\n sheetBelow: 640\n})\n\nconst emit = defineEmits<{ confirm: []; cancel: [] }>()\n\nconst open = defineModel<boolean>('open', { default: false })\n\nconst slots = useSlots()\nconst hasFooter = computed(() => !!slots.footer || !!props.confirmLabel)\nconst banded = computed(() => !!props.band || !!slots.hero)\n\nconst sizes = {\n sm: 'max-w-[27rem]',\n md: 'max-w-lg',\n record: 'max-w-130',\n lg: 'max-w-2xl',\n xl: 'max-w-3xl'\n}\nconst width = computed(() => sizes[props.size ?? (banded.value ? 'record' : 'md')])\nconst bandColour = computed(() => (typeof props.band === 'string' ? props.band : 'rgb(var(--sui-accent-wash))'))\n\nconst narrow = ref(false)\nlet query: MediaQueryList | undefined\nconst onQuery = () => (narrow.value = !!query?.matches)\nwatch(\n () => [props.presentation, props.sheetBelow] as const,\n ([presentation, below]) => {\n query?.removeEventListener('change', onQuery)\n query =\n presentation === 'auto' && typeof window !== 'undefined'\n ? window.matchMedia(`(max-width: ${below - 0.02}px)`)\n : undefined\n query?.addEventListener('change', onQuery)\n onQuery()\n },\n { immediate: true }\n)\nonBeforeUnmount(() => query?.removeEventListener('change', onQuery))\n\nconst sheet = computed(() => props.presentation === 'sheet' || (props.presentation === 'auto' && narrow.value))\nconst panel = useTemplateRef<InstanceType<typeof DialogContent>>('panel')\nconst drag = useSheetDrag(\n computed(() => panel.value?.$el as HTMLElement | undefined),\n () => cancel()\n)\nwatch(open, (isOpen) => isOpen && drag.reset())\n\nconst iconColours = { primary: 'on-accent', danger: 'on-danger', contrast: 'on-contrast' } as const\n\nfunction cancel() {\n emit('cancel')\n open.value = false\n}\n\n// A sheet closes on a tap on the scrim; a dialog only from its buttons and Escape.\nfunction onOutside(event: Event) {\n if (sheet.value) emit('cancel')\n else event.preventDefault()\n}\n\nfunction confirmFromKeyboard(event: KeyboardEvent) {\n if (!event.metaKey && !event.ctrlKey) return\n event.preventDefault()\n if (props.confirmLabel && !props.confirmDisabled) emit('confirm')\n}\n</script>\n\n<!-- The one dialog: a header, body and canvas footer; or, with a `band` or `hero`, a record form under a tinted band; or\n a bottom sheet. -->\n<template>\n <DialogRoot v-model:open=\"open\">\n <DialogPortal>\n <DialogOverlay\n class=\"fixed inset-0 z-[1100] flex justify-center bg-sui-scrim/40 backdrop-blur-[4px] data-[state=closed]:animate-sui-scrim-out data-[state=open]:animate-sui-scrim-in\"\n :class=\"sheet ? 'items-end' : 'items-center'\"\n >\n <DialogContent\n v-if=\"sheet\"\n ref=\"panel\"\n :aria-describedby=\"undefined\"\n class=\"relative flex max-h-[calc(100dvh-1.5rem)] w-full flex-col rounded-t-(--sui-radius-sheet) bg-sui-surface shadow-xl outline-hidden data-[state=closed]:animate-sui-sheet-out data-[state=open]:animate-sui-sheet-in motion-reduce:animate-none motion-reduce:transition-none\"\n :class=\"!drag.dragging.value && 'transition-transform duration-[250ms] ease-[cubic-bezier(0.32,0.72,0,1)]'\"\n :style=\"drag.style.value\"\n @escape-key-down=\"emit('cancel')\"\n @interact-outside=\"onOutside\"\n @keydown.enter=\"confirmFromKeyboard\"\n @open-auto-focus=\"focusFirst\"\n >\n <!-- Without a close button, keyboard and screen-reader users still get one: it shows when focused. -->\n <button\n v-if=\"!closable\"\n class=\"pointer-events-none absolute top-2 right-3 h-7 rounded-md bg-sui-sunken px-2.5 text-13 font-medium text-sui-ink opacity-0 outline-hidden focus-visible:pointer-events-auto focus-visible:opacity-100 focus-visible:sui-ring\"\n type=\"button\"\n @click=\"cancel\"\n >\n {{ closeLabel }}\n </button>\n <div class=\"shrink-0 cursor-grab touch-none pt-2\" data-modal-header v-bind=\"drag.handlers\">\n <span aria-hidden=\"true\" class=\"mx-auto block h-1 w-9 rounded-full bg-sui-mark-faint\" />\n <div v-if=\"!headerless\" class=\"flex min-h-11 items-center justify-between gap-3 pt-2 pr-4 pb-1 pl-5\">\n <DialogTitle class=\"min-w-0 truncate text-base font-semibold text-sui-ink\">\n <slot name=\"title\">{{ title }}</slot>\n </DialogTitle>\n <Tooltip v-if=\"closable\" :text=\"closeLabel\">\n <Button :aria-label=\"closeLabel\" class=\"shrink-0\" icon-only size=\"sm\" variant=\"ghost\" @click=\"cancel\">\n <CloseIcon class=\"fill-sui-ink-subtle stroke-sui-ink-subtle\" />\n </Button>\n </Tooltip>\n </div>\n <DialogTitle v-else class=\"sr-only\">{{ title }}</DialogTitle>\n </div>\n\n <div\n class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain px-5 pt-2 pb-5 text-sm leading-5 text-sui-ink-emphasis\"\n data-modal-body\n >\n <slot />\n </div>\n\n <div\n v-if=\"hasFooter\"\n class=\"flex shrink-0 flex-col gap-2.5 border-t border-sui-line-subtle bg-sui-surface px-4 pt-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]\"\n data-modal-footer\n >\n <slot name=\"footer\">\n <div v-if=\"$slots['footer-start'] || hint\" class=\"flex min-w-0 items-center justify-center gap-2\">\n <slot name=\"footer-start\" />\n <span v-if=\"hint\" class=\"min-w-0 truncate text-xs text-sui-ink-muted\">{{ hint }}</span>\n </div>\n <Button\n class=\"w-full\"\n :disabled=\"confirmDisabled\"\n size=\"lg\"\n type=\"button\"\n :variant=\"confirmVariant\"\n @click=\"emit('confirm')\"\n >\n <Icon v-if=\"confirmIcon\" :color=\"iconColours[confirmVariant]\" :name=\"confirmIcon\" />\n {{ confirmLabel }}\n </Button>\n </slot>\n </div>\n </DialogContent>\n <DialogContent\n v-else\n :aria-describedby=\"undefined\"\n class=\"flex max-h-[90vh] w-full flex-col bg-sui-surface shadow-xl outline-hidden data-[state=closed]:animate-sui-dialog-out data-[state=open]:animate-sui-dialog-in\"\n :class=\"[width, banded ? 'rounded-2xl' : 'rounded-xl']\"\n @escape-key-down=\"emit('cancel')\"\n @interact-outside.prevent\n @keydown.enter=\"confirmFromKeyboard\"\n @open-auto-focus=\"focusFirst\"\n >\n <div\n v-if=\"banded\"\n class=\"shrink-0 rounded-t-2xl pt-3.5 pr-3.5 pb-4.5 pl-5 transition-colors duration-200\"\n data-modal-header\n :style=\"{ backgroundColor: bandColour }\"\n >\n <div class=\"flex items-center justify-between gap-3\">\n <DialogTitle class=\"text-13 leading-5 font-medium text-sui-ink-body\">{{ title }}</DialogTitle>\n <Tooltip v-if=\"closable\" :text=\"closeLabel\">\n <Button :aria-label=\"closeLabel\" class=\"shrink-0\" icon-only size=\"sm\" variant=\"ghost\" @click=\"cancel\">\n <CloseIcon class=\"fill-sui-ink-muted stroke-sui-ink-muted\" />\n </Button>\n </Tooltip>\n </div>\n <div v-if=\"$slots.hero\" class=\"mt-0.5 flex flex-col items-center gap-1.5 pr-1.5\">\n <slot name=\"hero\" />\n </div>\n </div>\n <div\n v-else-if=\"!headerless\"\n class=\"flex shrink-0 items-center justify-between gap-3 rounded-t-xl border-b border-sui-line-subtle bg-sui-surface px-5 py-4 text-sui-ink\"\n data-modal-header\n >\n <DialogTitle class=\"min-w-0 truncate text-base font-semibold text-sui-ink\">\n <slot name=\"title\">{{ title }}</slot>\n </DialogTitle>\n <Tooltip v-if=\"closable\" :text=\"closeLabel\">\n <Button :aria-label=\"closeLabel\" class=\"shrink-0\" icon-only size=\"sm\" variant=\"ghost\" @click=\"cancel\">\n <CloseIcon class=\"fill-sui-ink-subtle stroke-sui-ink-subtle\" />\n </Button>\n </Tooltip>\n </div>\n <DialogTitle v-else class=\"sr-only\">{{ title }}</DialogTitle>\n\n <div\n class=\"overflow-y-auto bg-sui-surface text-sm text-sui-ink-emphasis\"\n :class=\"[banded ? 'px-5 pt-0.5 pb-4' : 'p-5', !hasFooter && (banded ? 'rounded-b-2xl' : 'rounded-b-xl')]\"\n data-modal-body\n >\n <slot />\n </div>\n\n <div\n v-if=\"hasFooter\"\n class=\"flex shrink-0 items-center border-t border-sui-line-subtle bg-sui-canvas text-sui-ink\"\n :class=\"banded ? 'gap-2 rounded-b-2xl py-3 pr-4 pl-5' : 'gap-3 rounded-b-xl px-5 py-4'\"\n data-modal-footer\n >\n <slot name=\"footer\">\n <slot name=\"footer-start\" />\n <span v-if=\"hint\" class=\"min-w-0 truncate text-xs text-sui-ink-muted\">{{ hint }}</span>\n <span class=\"flex-1\" />\n <Button type=\"button\" variant=\"ghost\" @click=\"cancel\">{{ cancelLabel }}</Button>\n <Button :disabled=\"confirmDisabled\" type=\"button\" :variant=\"confirmVariant\" @click=\"emit('confirm')\">\n <Icon v-if=\"confirmIcon\" :color=\"iconColours[confirmVariant]\" :name=\"confirmIcon\" />\n {{ confirmLabel }}\n </Button>\n </slot>\n </div>\n </DialogContent>\n </DialogOverlay>\n </DialogPortal>\n </DialogRoot>\n</template>\n"],"mappings":""}
|