@sparklingopinions/sparkling-ui 0.7.0 → 0.8.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.
Files changed (54) hide show
  1. package/README.md +2 -2
  2. package/dist/aliases.d.ts +23 -2
  3. package/dist/components/Button/Button.js.map +1 -1
  4. package/dist/components/Button/Button.vue.d.ts +4 -0
  5. package/dist/components/Button/Button.vue_vue_type_script_setup_true_lang.js +21 -11
  6. package/dist/components/Button/Button.vue_vue_type_script_setup_true_lang.js.map +1 -1
  7. package/dist/components/ContextMenu/ContextMenu.js.map +1 -1
  8. package/dist/components/ContextMenu/ContextMenu.vue_vue_type_script_setup_true_lang.js +29 -20
  9. package/dist/components/ContextMenu/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  10. package/dist/components/DataList/DataList.js.map +1 -1
  11. package/dist/components/DataList/DataList.vue.d.ts +30 -3
  12. package/dist/components/DataList/DataList.vue_vue_type_script_setup_true_lang.js +188 -100
  13. package/dist/components/DataList/DataList.vue_vue_type_script_setup_true_lang.js.map +1 -1
  14. package/dist/components/DataList/types.d.ts +2 -0
  15. package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
  16. package/dist/components/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js +32 -23
  17. package/dist/components/DropdownMenu/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  18. package/dist/components/Icon/iconSet.js +1 -0
  19. package/dist/components/Icon/iconSet.js.map +1 -1
  20. package/dist/components/Popover/PopoverDialog.js +7 -0
  21. package/dist/components/Popover/PopoverDialog.js.map +1 -0
  22. package/dist/components/Popover/PopoverDialog.vue.d.ts +56 -0
  23. package/dist/components/Popover/PopoverDialog.vue_vue_type_script_setup_true_lang.js +158 -0
  24. package/dist/components/Popover/PopoverDialog.vue_vue_type_script_setup_true_lang.js.map +1 -0
  25. package/dist/components/Popover/index.d.ts +2 -0
  26. package/dist/components/Table/Table.js.map +1 -1
  27. package/dist/components/Table/Table.vue.d.ts +2 -0
  28. package/dist/components/Table/Table.vue_vue_type_script_setup_true_lang.js +5 -1
  29. package/dist/components/Table/Table.vue_vue_type_script_setup_true_lang.js.map +1 -1
  30. package/dist/components/Tag/Tag.js.map +1 -1
  31. package/dist/components/Tag/Tag.vue.d.ts +3 -0
  32. package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js +3 -2
  33. package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js.map +1 -1
  34. package/dist/components/Toast/Toaster.js.map +1 -1
  35. package/dist/components/Toast/Toaster.vue.d.ts +4 -0
  36. package/dist/components/Toast/Toaster.vue_vue_type_script_setup_true_lang.js +49 -32
  37. package/dist/components/Toast/Toaster.vue_vue_type_script_setup_true_lang.js.map +1 -1
  38. package/dist/components/Toolbar/Toolbar.js +7 -0
  39. package/dist/components/Toolbar/Toolbar.js.map +1 -0
  40. package/dist/components/Toolbar/Toolbar.vue.d.ts +23 -0
  41. package/dist/components/Toolbar/Toolbar.vue_vue_type_script_setup_true_lang.js +52 -0
  42. package/dist/components/Toolbar/Toolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  43. package/dist/components/Toolbar/index.d.ts +2 -0
  44. package/dist/components/internal/MenuItemContent.js.map +1 -1
  45. package/dist/components/internal/MenuItemContent.vue.d.ts +1 -0
  46. package/dist/components/internal/MenuItemContent.vue_vue_type_script_setup_true_lang.js +30 -14
  47. package/dist/components/internal/MenuItemContent.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/internal/menu.d.ts +5 -1
  49. package/dist/components/internal/menu.js +17 -14
  50. package/dist/components/internal/menu.js.map +1 -1
  51. package/dist/index.d.ts +1 -0
  52. package/dist/index.js +18 -16
  53. package/package.json +1 -1
  54. package/src/styles/tailwind.css +22 -0
package/README.md CHANGED
@@ -67,7 +67,7 @@ The package is published to npm as `@sparklingopinions/sparkling-ui`. Apps insta
67
67
  import { Button } from 'sparkling-ui'
68
68
  ```
69
69
 
70
- 5. `Icon` draws the library's set of 134 icons by name: `<Icon name="users" color="ink-muted" />`. Older names (`people`, `companies`, `dot-vertical`...) resolve as aliases, and `iconNames` lists the set. To add icons or replace some of the set, register a resolver once, before mounting; names it returns a component for win. With SVG files imported as components (e.g. `vite-svg-loader`):
70
+ 5. `Icon` draws the library's set of 135 icons by name: `<Icon name="users" color="ink-muted" />`. Older names (`people`, `companies`, `dot-vertical`...) resolve as aliases, and `iconNames` lists the set. To add icons or replace some of the set, register a resolver once, before mounting; names it returns a component for win. With SVG files imported as components (e.g. `vite-svg-loader`):
71
71
 
72
72
  ```ts
73
73
  import { type Component, defineAsyncComponent } from 'vue'
@@ -83,7 +83,7 @@ The package is published to npm as `@sparklingopinions/sparkling-ui`. Apps insta
83
83
 
84
84
  6. For light and dark, call `initColorMode()` once before mounting (it sets `data-mode` on `<html>` from the saved choice or the system's); then `useColorMode()` gives `mode`, `resolved`, `isDark`, `setMode` and `toggle`, and `useForceLightMode()` keeps a page light while it's mounted.
85
85
 
86
- 7. For toasts, mount one `<Toaster />` near the app root, then call `toast.info('Draft saved')`, `toast.success(...)` or `toast.error(...)` anywhere, with `{ detail, life, closable }` as options.
86
+ 7. For toasts, mount one `<Toaster />` near the app root, then call `toast.info('Draft saved')`, `toast.success(...)` or `toast.error(...)` anywhere, with `{ detail, life, closable }` as options. `position` and the `--sui-toaster-inset-*` tokens move them clear of your own sidebar or bottom bar.
87
87
 
88
88
  8. To use `useConfirm`, mount one `<AlertDialog />` near the app root (it takes default `confirm-label` and `cancel-label` for translation). Then anywhere:
89
89
 
package/dist/aliases.d.ts CHANGED
@@ -189,6 +189,9 @@ export declare const List: <T extends Record<string, any>>(__VLS_props: NonNulla
189
189
  }) => unknown;
190
190
  leading?: (props: {
191
191
  item: T;
192
+ selected: boolean;
193
+ selecting: boolean;
194
+ toggle: () => void;
192
195
  }) => unknown;
193
196
  title?: (props: {
194
197
  item: T;
@@ -196,17 +199,28 @@ export declare const List: <T extends Record<string, any>>(__VLS_props: NonNulla
196
199
  meta?: (props: {
197
200
  item: T;
198
201
  }) => unknown;
202
+ 'group-actions'?: (props: {
203
+ group: DataListGroup<T>;
204
+ }) => unknown;
199
205
  empty?: () => unknown;
200
206
  };
201
- emit: ((event: "update:selected", value: unknown[]) => void) & ((event: "update:collapsed", value: string[]) => void);
207
+ emit: ((evt: "context-item", action: DropdownMenuAction, item: T) => void) & (((event: "update:selected", value: unknown[]) => void) & ((event: "update:collapsed", value: string[]) => void));
202
208
  }, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
203
209
  props: import("vue").PublicProps & {
204
210
  groups: DataListGroup<T>[];
205
211
  rowKey?: (keyof T & string) | undefined;
206
212
  selectable?: boolean | undefined;
213
+ checkboxPlacement?: "gutter" | "leading" | undefined;
207
214
  loading?: boolean | undefined;
208
215
  variant?: "joined" | "cards" | undefined;
209
216
  headingLevel?: 2 | 3 | 4 | 5 | 6 | undefined;
217
+ groupHeaderTone?: "sunken" | "surface" | "canvas" | undefined;
218
+ groupHeaderHeight?: number | undefined;
219
+ collapsible?: boolean | undefined;
220
+ rowHeight?: number | undefined;
221
+ dividers?: boolean | undefined;
222
+ contextItems?: ((item: T) => DropdownMenuEntry[]) | undefined;
223
+ contextLabel?: string | undefined;
210
224
  onRowClick?: ((item: T) => void) | undefined;
211
225
  onNew?: ((group: DataListGroup<T>) => void) | undefined;
212
226
  newLabel?: string | undefined;
@@ -215,6 +229,7 @@ export declare const List: <T extends Record<string, any>>(__VLS_props: NonNulla
215
229
  selectLabel?: string | undefined;
216
230
  selected?: unknown[] | undefined;
217
231
  collapsed?: string[] | undefined;
232
+ "onContext-item"?: ((action: DropdownMenuAction, item: T) => any) | undefined;
218
233
  "onUpdate:selected"?: ((value: unknown[]) => any) | undefined;
219
234
  "onUpdate:collapsed"?: ((value: string[]) => any) | undefined;
220
235
  } & (typeof globalThis extends {
@@ -233,6 +248,9 @@ export declare const List: <T extends Record<string, any>>(__VLS_props: NonNulla
233
248
  }) => unknown;
234
249
  leading?: (props: {
235
250
  item: T;
251
+ selected: boolean;
252
+ selecting: boolean;
253
+ toggle: () => void;
236
254
  }) => unknown;
237
255
  title?: (props: {
238
256
  item: T;
@@ -240,9 +258,12 @@ export declare const List: <T extends Record<string, any>>(__VLS_props: NonNulla
240
258
  meta?: (props: {
241
259
  item: T;
242
260
  }) => unknown;
261
+ 'group-actions'?: (props: {
262
+ group: DataListGroup<T>;
263
+ }) => unknown;
243
264
  empty?: () => unknown;
244
265
  };
245
- emit: ((event: "update:selected", value: unknown[]) => void) & ((event: "update:collapsed", value: string[]) => void);
266
+ emit: ((evt: "context-item", action: DropdownMenuAction, item: T) => void) & (((event: "update:selected", value: unknown[]) => void) & ((event: "update:collapsed", value: string[]) => void));
246
267
  }>) => import("vue").VNode & {
247
268
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
248
269
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Button.js","names":["_sfc_main","Button_vue_vue_type_script_setup_true_lang_default","Button_default"],"sources":["../../../src/components/Button/Button.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { Primitive, type PrimitiveProps } from 'reka-ui'\n\nexport type ButtonVariant =\n 'primary' | 'secondary' | 'tinted' | 'ghost' | 'contrast' | 'danger' | 'danger-outline' | 'ghost-inverse'\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps extends PrimitiveProps {\n variant?: ButtonVariant\n size?: ButtonSize\n /** Fully rounded ends. */\n pill?: boolean\n /** Square, for a single icon; give it an `aria-label`. */\n iconOnly?: boolean\n /** Where the icon sits next to the label; it's spaced from the text on the other side. */\n iconPosition?: 'start' | 'end'\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n as: 'button',\n variant: 'secondary',\n size: 'md',\n iconPosition: 'start'\n})\n\nconst variants: Record<ButtonVariant, string> = {\n primary:\n 'border border-sui-accent-solid bg-sui-accent-solid text-sui-on-accent not-data-disabled:hover:border-sui-accent-hover not-data-disabled:hover:bg-sui-accent-hover [&_svg]:stroke-sui-on-accent',\n secondary:\n 'border border-sui-line bg-sui-surface text-sui-ink-emphasis shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-line-strong not-data-disabled:hover:bg-sui-canvas [&_svg]:stroke-sui-ink-emphasis',\n tinted:\n 'border border-sui-accent-line bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n ghost:\n 'border border-transparent bg-transparent text-sui-ink-subtle not-data-disabled:hover:bg-sui-sunken not-data-disabled:hover:text-sui-ink-emphasis',\n contrast:\n 'border border-sui-contrast bg-sui-contrast text-sui-on-contrast not-data-disabled:hover:border-sui-contrast-hover not-data-disabled:hover:bg-sui-contrast-hover',\n // No border: it's 2px narrower than the other variants.\n danger: 'bg-sui-danger-solid text-sui-on-danger not-data-disabled:hover:bg-sui-danger-hover',\n 'danger-outline':\n 'border border-sui-danger-line bg-sui-surface text-sui-danger not-data-disabled:hover:bg-sui-danger-soft [&_svg]:stroke-sui-danger',\n // For inverse grounds: toasts, dark bands.\n 'ghost-inverse':\n 'border border-transparent bg-transparent text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.12] not-data-disabled:active:bg-sui-on-inverse/[0.2] [&_svg]:stroke-sui-on-inverse'\n}\n\nconst heights: Record<ButtonSize, string> = {\n xs: 'h-6',\n sm: 'h-7',\n md: 'h-(--sui-button-height)',\n lg: 'h-12'\n}\n\nconst paddings: Record<ButtonSize, string> = {\n xs: 'px-2',\n sm: 'px-2.5',\n md: 'px-(--sui-button-padding-x)',\n lg: 'px-(--sui-button-padding-x)'\n}\n\nconst texts: Record<ButtonSize, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-(length:--sui-button-text) leading-5',\n lg: 'text-(length:--sui-button-text) leading-5'\n}\n\nconst classes = computed(() => [\n // no-underline: as a link, it shouldn't pick up the app's link styles.\n 'flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out data-disabled:opacity-50',\n props.variant === 'ghost-inverse' ? 'focus-visible:sui-ring-inverse' : 'focus-visible:sui-ring',\n variants[props.variant],\n heights[props.size],\n texts[props.size],\n props.iconOnly ? 'aspect-square px-0' : paddings[props.size],\n // xs uses rounded-md: rounded-lg reads as a pill at that height.\n props.pill ? 'rounded-full' : props.size === 'xs' ? 'rounded-md' : 'rounded-(--sui-radius-button)',\n props.iconOnly ? '' : props.iconPosition === 'end' ? '[&_svg]:ml-2' : '[&_svg:first-child]:mr-2'\n])\n\n// A link or custom element can't be natively disabled: it says so to assistive tech instead.\nconst isNativeButton = computed(() => props.as === 'button' && !props.asChild)\n</script>\n\n<template>\n <Primitive\n :aria-disabled=\"disabled && !isNativeButton ? 'true' : undefined\"\n :as=\"as\"\n :as-child=\"asChild\"\n :class=\"classes\"\n :data-disabled=\"disabled ? '' : undefined\"\n :disabled=\"isNativeButton ? disabled : undefined\"\n >\n <slot />\n </Primitive>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"Button.js","names":["_sfc_main","Button_vue_vue_type_script_setup_true_lang_default","Button_default"],"sources":["../../../src/components/Button/Button.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { Primitive, type PrimitiveProps } from 'reka-ui'\n\nexport type ButtonVariant =\n 'primary' | 'secondary' | 'tinted' | 'ghost' | 'contrast' | 'danger' | 'danger-outline' | 'ghost-inverse'\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps extends PrimitiveProps {\n variant?: ButtonVariant\n size?: ButtonSize\n /** Fully rounded ends. */\n pill?: boolean\n /** Square, for a single icon; give it an `aria-label`. */\n iconOnly?: boolean\n /** Where the icon sits next to the label; it's spaced from the text on the other side. */\n iconPosition?: 'start' | 'end'\n disabled?: boolean\n /** A toggle's state, as `aria-pressed`. Drawn on for ghost, ghost-inverse and secondary; leave it unset on a plain\n * button. */\n pressed?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n as: 'button',\n variant: 'secondary',\n size: 'md',\n iconPosition: 'start',\n pressed: undefined\n})\n\nconst variants: Record<ButtonVariant, string> = {\n primary:\n 'border border-sui-accent-solid bg-sui-accent-solid text-sui-on-accent not-data-disabled:hover:border-sui-accent-hover not-data-disabled:hover:bg-sui-accent-hover [&_svg]:stroke-sui-on-accent',\n secondary:\n 'border border-sui-line bg-sui-surface text-sui-ink-emphasis shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-line-strong not-data-disabled:hover:bg-sui-canvas [&_svg]:stroke-sui-ink-emphasis',\n tinted:\n 'border border-sui-accent-line bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n ghost:\n 'border border-transparent bg-transparent text-sui-ink-subtle not-data-disabled:hover:bg-sui-sunken not-data-disabled:hover:text-sui-ink-emphasis',\n contrast:\n 'border border-sui-contrast bg-sui-contrast text-sui-on-contrast not-data-disabled:hover:border-sui-contrast-hover not-data-disabled:hover:bg-sui-contrast-hover',\n // No border: it's 2px narrower than the other variants.\n danger: 'bg-sui-danger-solid text-sui-on-danger not-data-disabled:hover:bg-sui-danger-hover',\n 'danger-outline':\n 'border border-sui-danger-line bg-sui-surface text-sui-danger not-data-disabled:hover:bg-sui-danger-soft [&_svg]:stroke-sui-danger',\n // For inverse grounds: toasts, dark bands.\n 'ghost-inverse':\n 'border border-transparent bg-transparent text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.12] not-data-disabled:active:bg-sui-on-inverse/[0.2] [&_svg]:stroke-sui-on-inverse'\n}\n\n// On is a step past hover, and hovering it deepens it again. On inverse, 22% is as deep as white text keeps 4.5:1.\nconst pressedVariants: Partial<Record<ButtonVariant, string>> = {\n secondary:\n 'border border-sui-accent-line bg-sui-accent-wash text-sui-accent shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-accent-bright not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n ghost:\n 'border border-transparent bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n 'ghost-inverse':\n 'border border-transparent bg-sui-on-inverse/[0.18] text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.22] [&_svg]:stroke-sui-on-inverse'\n}\n\nconst heights: Record<ButtonSize, string> = {\n xs: 'h-6',\n sm: 'h-7',\n md: 'h-(--sui-button-height)',\n lg: 'h-12'\n}\n\nconst paddings: Record<ButtonSize, string> = {\n xs: 'px-2',\n sm: 'px-2.5',\n md: 'px-(--sui-button-padding-x)',\n lg: 'px-(--sui-button-padding-x)'\n}\n\nconst texts: Record<ButtonSize, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-(length:--sui-button-text) leading-5',\n lg: 'text-(length:--sui-button-text) leading-5'\n}\n\nconst classes = computed(() => [\n // no-underline: as a link, it shouldn't pick up the app's link styles.\n 'flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out data-disabled:opacity-50',\n props.variant === 'ghost-inverse' ? 'focus-visible:sui-ring-inverse' : 'focus-visible:sui-ring',\n (props.pressed && pressedVariants[props.variant]) || variants[props.variant],\n heights[props.size],\n texts[props.size],\n props.iconOnly ? 'aspect-square px-0' : paddings[props.size],\n // xs uses rounded-md: rounded-lg reads as a pill at that height.\n props.pill ? 'rounded-full' : props.size === 'xs' ? 'rounded-md' : 'rounded-(--sui-radius-button)',\n props.iconOnly ? '' : props.iconPosition === 'end' ? '[&_svg]:ml-2' : '[&_svg:first-child]:mr-2'\n])\n\n// A link or custom element can't be natively disabled: it says so to assistive tech instead.\nconst isNativeButton = computed(() => props.as === 'button' && !props.asChild)\n</script>\n\n<template>\n <Primitive\n :aria-disabled=\"disabled && !isNativeButton ? 'true' : undefined\"\n :aria-pressed=\"pressed === undefined ? undefined : String(pressed)\"\n :as=\"as\"\n :as-child=\"asChild\"\n :class=\"classes\"\n :data-disabled=\"disabled ? '' : undefined\"\n :disabled=\"isNativeButton ? disabled : undefined\"\n >\n <slot />\n </Primitive>\n</template>\n"],"mappings":""}
@@ -11,6 +11,9 @@ export interface ButtonProps extends PrimitiveProps {
11
11
  /** Where the icon sits next to the label; it's spaced from the text on the other side. */
12
12
  iconPosition?: 'start' | 'end';
13
13
  disabled?: boolean;
14
+ /** A toggle's state, as `aria-pressed`. Drawn on for ghost, ghost-inverse and secondary; leave it unset on a plain
15
+ * button. */
16
+ pressed?: boolean;
14
17
  }
15
18
  declare var __VLS_7: {};
16
19
  type __VLS_Slots = {} & {
@@ -20,6 +23,7 @@ declare const __VLS_base: import("vue").DefineComponent<ButtonProps, {}, {}, {},
20
23
  variant: ButtonVariant;
21
24
  size: ButtonSize;
22
25
  iconPosition: "start" | "end";
26
+ pressed: boolean;
23
27
  as: import("reka-ui").AsTag | import("vue").Component;
24
28
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
25
29
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -10,6 +10,10 @@ var l = /*@__PURE__*/ n({
10
10
  iconOnly: { type: Boolean },
11
11
  iconPosition: { default: "start" },
12
12
  disabled: { type: Boolean },
13
+ pressed: {
14
+ type: Boolean,
15
+ default: void 0
16
+ },
13
17
  asChild: { type: Boolean },
14
18
  as: { default: "button" }
15
19
  },
@@ -24,42 +28,48 @@ var l = /*@__PURE__*/ n({
24
28
  "danger-outline": "border border-sui-danger-line bg-sui-surface text-sui-danger not-data-disabled:hover:bg-sui-danger-soft [&_svg]:stroke-sui-danger",
25
29
  "ghost-inverse": "border border-transparent bg-transparent text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.12] not-data-disabled:active:bg-sui-on-inverse/[0.2] [&_svg]:stroke-sui-on-inverse"
26
30
  }, d = {
31
+ secondary: "border border-sui-accent-line bg-sui-accent-wash text-sui-accent shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-accent-bright not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent",
32
+ ghost: "border border-transparent bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent",
33
+ "ghost-inverse": "border border-transparent bg-sui-on-inverse/[0.18] text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.22] [&_svg]:stroke-sui-on-inverse"
34
+ }, f = {
27
35
  xs: "h-6",
28
36
  sm: "h-7",
29
37
  md: "h-(--sui-button-height)",
30
38
  lg: "h-12"
31
- }, f = {
39
+ }, p = {
32
40
  xs: "px-2",
33
41
  sm: "px-2.5",
34
42
  md: "px-(--sui-button-padding-x)",
35
43
  lg: "px-(--sui-button-padding-x)"
36
- }, p = {
44
+ }, m = {
37
45
  xs: "text-xs",
38
46
  sm: "text-sm",
39
47
  md: "text-(length:--sui-button-text) leading-5",
40
48
  lg: "text-(length:--sui-button-text) leading-5"
41
- }, m = e(() => [
49
+ }, h = e(() => [
42
50
  "flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out data-disabled:opacity-50",
43
51
  l.variant === "ghost-inverse" ? "focus-visible:sui-ring-inverse" : "focus-visible:sui-ring",
44
- u[l.variant],
45
- d[l.size],
46
- p[l.size],
47
- l.iconOnly ? "aspect-square px-0" : f[l.size],
52
+ l.pressed && d[l.variant] || u[l.variant],
53
+ f[l.size],
54
+ m[l.size],
55
+ l.iconOnly ? "aspect-square px-0" : p[l.size],
48
56
  l.pill ? "rounded-full" : l.size === "xs" ? "rounded-md" : "rounded-(--sui-radius-button)",
49
57
  l.iconOnly ? "" : l.iconPosition === "end" ? "[&_svg]:ml-2" : "[&_svg:first-child]:mr-2"
50
- ]), h = e(() => l.as === "button" && !l.asChild);
58
+ ]), g = e(() => l.as === "button" && !l.asChild);
51
59
  return (e, l) => (i(), t(o(c), {
52
- "aria-disabled": n.disabled && !h.value ? "true" : void 0,
60
+ "aria-disabled": n.disabled && !g.value ? "true" : void 0,
61
+ "aria-pressed": n.pressed === void 0 ? void 0 : String(n.pressed),
53
62
  as: n.as,
54
63
  "as-child": n.asChild,
55
- class: r(m.value),
64
+ class: r(h.value),
56
65
  "data-disabled": n.disabled ? "" : void 0,
57
- disabled: h.value ? n.disabled : void 0
66
+ disabled: g.value ? n.disabled : void 0
58
67
  }, {
59
68
  default: s(() => [a(e.$slots, "default")]),
60
69
  _: 3
61
70
  }, 8, [
62
71
  "aria-disabled",
72
+ "aria-pressed",
63
73
  "as",
64
74
  "as-child",
65
75
  "class",
@@ -1 +1 @@
1
- {"version":3,"file":"Button.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_renderSlot","Button_vue_vue_type_script_setup_true_lang_default","props","__props","variants","heights","paddings","texts","classes","computed","isNativeButton","openBlock","createBlock","unref","Primitive","normalizeClass","withCtx","renderSlot","_ctx"],"sources":["../../../src/components/Button/Button.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { Primitive, type PrimitiveProps } from 'reka-ui'\n\nexport type ButtonVariant =\n 'primary' | 'secondary' | 'tinted' | 'ghost' | 'contrast' | 'danger' | 'danger-outline' | 'ghost-inverse'\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps extends PrimitiveProps {\n variant?: ButtonVariant\n size?: ButtonSize\n /** Fully rounded ends. */\n pill?: boolean\n /** Square, for a single icon; give it an `aria-label`. */\n iconOnly?: boolean\n /** Where the icon sits next to the label; it's spaced from the text on the other side. */\n iconPosition?: 'start' | 'end'\n disabled?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n as: 'button',\n variant: 'secondary',\n size: 'md',\n iconPosition: 'start'\n})\n\nconst variants: Record<ButtonVariant, string> = {\n primary:\n 'border border-sui-accent-solid bg-sui-accent-solid text-sui-on-accent not-data-disabled:hover:border-sui-accent-hover not-data-disabled:hover:bg-sui-accent-hover [&_svg]:stroke-sui-on-accent',\n secondary:\n 'border border-sui-line bg-sui-surface text-sui-ink-emphasis shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-line-strong not-data-disabled:hover:bg-sui-canvas [&_svg]:stroke-sui-ink-emphasis',\n tinted:\n 'border border-sui-accent-line bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n ghost:\n 'border border-transparent bg-transparent text-sui-ink-subtle not-data-disabled:hover:bg-sui-sunken not-data-disabled:hover:text-sui-ink-emphasis',\n contrast:\n 'border border-sui-contrast bg-sui-contrast text-sui-on-contrast not-data-disabled:hover:border-sui-contrast-hover not-data-disabled:hover:bg-sui-contrast-hover',\n // No border: it's 2px narrower than the other variants.\n danger: 'bg-sui-danger-solid text-sui-on-danger not-data-disabled:hover:bg-sui-danger-hover',\n 'danger-outline':\n 'border border-sui-danger-line bg-sui-surface text-sui-danger not-data-disabled:hover:bg-sui-danger-soft [&_svg]:stroke-sui-danger',\n // For inverse grounds: toasts, dark bands.\n 'ghost-inverse':\n 'border border-transparent bg-transparent text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.12] not-data-disabled:active:bg-sui-on-inverse/[0.2] [&_svg]:stroke-sui-on-inverse'\n}\n\nconst heights: Record<ButtonSize, string> = {\n xs: 'h-6',\n sm: 'h-7',\n md: 'h-(--sui-button-height)',\n lg: 'h-12'\n}\n\nconst paddings: Record<ButtonSize, string> = {\n xs: 'px-2',\n sm: 'px-2.5',\n md: 'px-(--sui-button-padding-x)',\n lg: 'px-(--sui-button-padding-x)'\n}\n\nconst texts: Record<ButtonSize, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-(length:--sui-button-text) leading-5',\n lg: 'text-(length:--sui-button-text) leading-5'\n}\n\nconst classes = computed(() => [\n // no-underline: as a link, it shouldn't pick up the app's link styles.\n 'flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out data-disabled:opacity-50',\n props.variant === 'ghost-inverse' ? 'focus-visible:sui-ring-inverse' : 'focus-visible:sui-ring',\n variants[props.variant],\n heights[props.size],\n texts[props.size],\n props.iconOnly ? 'aspect-square px-0' : paddings[props.size],\n // xs uses rounded-md: rounded-lg reads as a pill at that height.\n props.pill ? 'rounded-full' : props.size === 'xs' ? 'rounded-md' : 'rounded-(--sui-radius-button)',\n props.iconOnly ? '' : props.iconPosition === 'end' ? '[&_svg]:ml-2' : '[&_svg:first-child]:mr-2'\n])\n\n// A link or custom element can't be natively disabled: it says so to assistive tech instead.\nconst isNativeButton = computed(() => props.as === 'button' && !props.asChild)\n</script>\n\n<template>\n <Primitive\n :aria-disabled=\"disabled && !isNativeButton ? 'true' : undefined\"\n :as=\"as\"\n :as-child=\"asChild\"\n :class=\"classes\"\n :data-disabled=\"disabled ? '' : undefined\"\n :disabled=\"isNativeButton ? disabled : undefined\"\n >\n <slot />\n </Primitive>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAqBA,IAAMO,IAAQC,GAORC,IAA0C;GAC9C,SACE;GACF,WACE;GACF,QACE;GACF,OACE;GACF,UACE;GAEF,QAAQ;GACR,kBACE;GAEF,iBACE;EACJ,GAEMC,IAAsC;GAC1C,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN,GAEMC,IAAuC;GAC3C,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN,GAEMC,IAAoC;GACxC,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN,GAEMC,IAAUC,QAAe;GAE7B;GACAP,EAAM,YAAY,kBAAkB,mCAAmC;GACvEE,EAASF,EAAM;GACfG,EAAQH,EAAM;GACdK,EAAML,EAAM;GACZA,EAAM,WAAW,uBAAuBI,EAASJ,EAAM;GAEvDA,EAAM,OAAO,iBAAiBA,EAAM,SAAS,OAAO,eAAe;GACnEA,EAAM,WAAW,KAAKA,EAAM,iBAAiB,QAAQ,iBAAiB;EACxE,CAAC,GAGKQ,IAAiBD,QAAeP,EAAM,OAAO,YAAY,CAACA,EAAM,OAAO;E,kBAI3EP,EAAA,GAAAC,EASYC,EAAAiB,CAAA,GAAA;GART,iBAAeX,EAAA,YAAQ,CAAKO,EAAA,QAAc,SAAY,KAAA;GACtD,IAAIP,EAAA;GACJ,YAAUA,EAAA;GACV,OAAKL,EAAEU,EAAA,KAAO;GACd,iBAAeL,EAAA,WAAQ,KAAQ,KAAA;GAC/B,UAAUO,EAAA,QAAiBP,EAAA,WAAW,KAAA;;GAEvC,SAAAJ,QAAQ,CAARC,EAAQkB,EAAA,QAAA,SAAA,C"}
1
+ {"version":3,"file":"Button.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_renderSlot","Button_vue_vue_type_script_setup_true_lang_default","props","__props","variants","pressedVariants","heights","paddings","texts","classes","computed","isNativeButton","openBlock","createBlock","unref","Primitive","normalizeClass","withCtx","renderSlot","_ctx"],"sources":["../../../src/components/Button/Button.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { Primitive, type PrimitiveProps } from 'reka-ui'\n\nexport type ButtonVariant =\n 'primary' | 'secondary' | 'tinted' | 'ghost' | 'contrast' | 'danger' | 'danger-outline' | 'ghost-inverse'\nexport type ButtonSize = 'xs' | 'sm' | 'md' | 'lg'\n\nexport interface ButtonProps extends PrimitiveProps {\n variant?: ButtonVariant\n size?: ButtonSize\n /** Fully rounded ends. */\n pill?: boolean\n /** Square, for a single icon; give it an `aria-label`. */\n iconOnly?: boolean\n /** Where the icon sits next to the label; it's spaced from the text on the other side. */\n iconPosition?: 'start' | 'end'\n disabled?: boolean\n /** A toggle's state, as `aria-pressed`. Drawn on for ghost, ghost-inverse and secondary; leave it unset on a plain\n * button. */\n pressed?: boolean\n}\n\nconst props = withDefaults(defineProps<ButtonProps>(), {\n as: 'button',\n variant: 'secondary',\n size: 'md',\n iconPosition: 'start',\n pressed: undefined\n})\n\nconst variants: Record<ButtonVariant, string> = {\n primary:\n 'border border-sui-accent-solid bg-sui-accent-solid text-sui-on-accent not-data-disabled:hover:border-sui-accent-hover not-data-disabled:hover:bg-sui-accent-hover [&_svg]:stroke-sui-on-accent',\n secondary:\n 'border border-sui-line bg-sui-surface text-sui-ink-emphasis shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-line-strong not-data-disabled:hover:bg-sui-canvas [&_svg]:stroke-sui-ink-emphasis',\n tinted:\n 'border border-sui-accent-line bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n ghost:\n 'border border-transparent bg-transparent text-sui-ink-subtle not-data-disabled:hover:bg-sui-sunken not-data-disabled:hover:text-sui-ink-emphasis',\n contrast:\n 'border border-sui-contrast bg-sui-contrast text-sui-on-contrast not-data-disabled:hover:border-sui-contrast-hover not-data-disabled:hover:bg-sui-contrast-hover',\n // No border: it's 2px narrower than the other variants.\n danger: 'bg-sui-danger-solid text-sui-on-danger not-data-disabled:hover:bg-sui-danger-hover',\n 'danger-outline':\n 'border border-sui-danger-line bg-sui-surface text-sui-danger not-data-disabled:hover:bg-sui-danger-soft [&_svg]:stroke-sui-danger',\n // For inverse grounds: toasts, dark bands.\n 'ghost-inverse':\n 'border border-transparent bg-transparent text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.12] not-data-disabled:active:bg-sui-on-inverse/[0.2] [&_svg]:stroke-sui-on-inverse'\n}\n\n// On is a step past hover, and hovering it deepens it again. On inverse, 22% is as deep as white text keeps 4.5:1.\nconst pressedVariants: Partial<Record<ButtonVariant, string>> = {\n secondary:\n 'border border-sui-accent-line bg-sui-accent-wash text-sui-accent shadow-(--sui-shadow-raised) not-data-disabled:hover:border-sui-accent-bright not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n ghost:\n 'border border-transparent bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent',\n 'ghost-inverse':\n 'border border-transparent bg-sui-on-inverse/[0.18] text-sui-on-inverse not-data-disabled:hover:bg-sui-on-inverse/[0.22] [&_svg]:stroke-sui-on-inverse'\n}\n\nconst heights: Record<ButtonSize, string> = {\n xs: 'h-6',\n sm: 'h-7',\n md: 'h-(--sui-button-height)',\n lg: 'h-12'\n}\n\nconst paddings: Record<ButtonSize, string> = {\n xs: 'px-2',\n sm: 'px-2.5',\n md: 'px-(--sui-button-padding-x)',\n lg: 'px-(--sui-button-padding-x)'\n}\n\nconst texts: Record<ButtonSize, string> = {\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-(length:--sui-button-text) leading-5',\n lg: 'text-(length:--sui-button-text) leading-5'\n}\n\nconst classes = computed(() => [\n // no-underline: as a link, it shouldn't pick up the app's link styles.\n 'flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out data-disabled:opacity-50',\n props.variant === 'ghost-inverse' ? 'focus-visible:sui-ring-inverse' : 'focus-visible:sui-ring',\n (props.pressed && pressedVariants[props.variant]) || variants[props.variant],\n heights[props.size],\n texts[props.size],\n props.iconOnly ? 'aspect-square px-0' : paddings[props.size],\n // xs uses rounded-md: rounded-lg reads as a pill at that height.\n props.pill ? 'rounded-full' : props.size === 'xs' ? 'rounded-md' : 'rounded-(--sui-radius-button)',\n props.iconOnly ? '' : props.iconPosition === 'end' ? '[&_svg]:ml-2' : '[&_svg:first-child]:mr-2'\n])\n\n// A link or custom element can't be natively disabled: it says so to assistive tech instead.\nconst isNativeButton = computed(() => props.as === 'button' && !props.asChild)\n</script>\n\n<template>\n <Primitive\n :aria-disabled=\"disabled && !isNativeButton ? 'true' : undefined\"\n :aria-pressed=\"pressed === undefined ? undefined : String(pressed)\"\n :as=\"as\"\n :as-child=\"asChild\"\n :class=\"classes\"\n :data-disabled=\"disabled ? '' : undefined\"\n :disabled=\"isNativeButton ? disabled : undefined\"\n >\n <slot />\n </Primitive>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAwBA,IAAMO,IAAQC,GAQRC,IAA0C;GAC9C,SACE;GACF,WACE;GACF,QACE;GACF,OACE;GACF,UACE;GAEF,QAAQ;GACR,kBACE;GAEF,iBACE;EACJ,GAGMC,IAA0D;GAC9D,WACE;GACF,OACE;GACF,iBACE;EACJ,GAEMC,IAAsC;GAC1C,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN,GAEMC,IAAuC;GAC3C,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN,GAEMC,IAAoC;GACxC,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;EACN,GAEMC,IAAUC,QAAe;GAE7B;GACAR,EAAM,YAAY,kBAAkB,mCAAmC;GACtEA,EAAM,WAAWG,EAAgBH,EAAM,YAAaE,EAASF,EAAM;GACpEI,EAAQJ,EAAM;GACdM,EAAMN,EAAM;GACZA,EAAM,WAAW,uBAAuBK,EAASL,EAAM;GAEvDA,EAAM,OAAO,iBAAiBA,EAAM,SAAS,OAAO,eAAe;GACnEA,EAAM,WAAW,KAAKA,EAAM,iBAAiB,QAAQ,iBAAiB;EACxE,CAAC,GAGKS,IAAiBD,QAAeR,EAAM,OAAO,YAAY,CAACA,EAAM,OAAO;E,kBAI3EP,EAAA,GAAAC,EAUYC,EAAAkB,CAAA,GAAA;GATT,iBAAeZ,EAAA,YAAQ,CAAKQ,EAAA,QAAc,SAAY,KAAA;GACtD,gBAAcR,EAAA,YAAY,KAAA,IAAY,KAAA,IAAY,OAAOA,EAAA,OAAO;GAChE,IAAIA,EAAA;GACJ,YAAUA,EAAA;GACV,OAAKL,EAAEW,EAAA,KAAO;GACd,iBAAeN,EAAA,WAAQ,KAAQ,KAAA;GAC/B,UAAUQ,EAAA,QAAiBR,EAAA,WAAW,KAAA;;GAEvC,SAAAJ,QAAQ,CAARC,EAAQmB,EAAA,QAAA,SAAA,C"}
@@ -1 +1 @@
1
- {"version":3,"file":"ContextMenu.js","names":["_sfc_main","ContextMenu_vue_vue_type_script_setup_true_lang_default","ContextMenu_default"],"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":""}
1
+ {"version":3,"file":"ContextMenu.js","names":["_sfc_main","ContextMenu_vue_vue_type_script_setup_true_lang_default","ContextMenu_default"],"sources":["../../../src/components/ContextMenu/ContextMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\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 uid = useId()\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\n v-else\n as-child\n :class=\"menuItemClass\"\n :text-value=\"item.disabled ? '' : undefined\"\n @select=\"select(item, $event)\"\n >\n <MenuItemContent :item=\"item\" :reason-id=\"`${uid}-${index}`\" />\n </ContextMenuItem>\n </template>\n </ContextMenuContent>\n </ContextMenuPortal>\n </ContextMenuRoot>\n</template>\n"],"mappings":""}
@@ -1,9 +1,9 @@
1
1
  import e from "../internal/MenuItemContent.js";
2
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";
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, useId as _, withCtx as v } from "vue";
4
+ import { ContextMenuContent as y, ContextMenuItem as b, ContextMenuPortal as x, ContextMenuRoot as S, ContextMenuSeparator as C, ContextMenuTrigger as w } from "reka-ui";
5
5
  //#region src/components/ContextMenu/ContextMenu.vue?vue&type=script&setup=true&lang.ts
6
- var w = /*@__PURE__*/ u({
6
+ var T = /*@__PURE__*/ u({
7
7
  __name: "ContextMenu",
8
8
  props: /*@__PURE__*/ d({
9
9
  items: {},
@@ -27,11 +27,11 @@ var w = /*@__PURE__*/ u({
27
27
  "hide"
28
28
  ],
29
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");
30
+ let d = t, T = u, E = _(), D = a(d, (e) => T("click-item", e));
31
+ function O(e) {
32
+ T("update:open", e), T(e ? "show" : "hide");
33
33
  }
34
- function D(e) {
34
+ function k(e) {
35
35
  if (d.disabled || !(e.key === "F10" && e.shiftKey || e.key === "ContextMenu")) return;
36
36
  e.preventDefault();
37
37
  let t = e.currentTarget, n = e.target.getBoundingClientRect();
@@ -42,34 +42,43 @@ var w = /*@__PURE__*/ u({
42
42
  clientY: n.top + Math.min(n.height, 32)
43
43
  }));
44
44
  }
45
- return (a, u) => (p(), s(g(x), {
45
+ return (a, u) => (p(), s(g(S), {
46
46
  modal: !1,
47
- "onUpdate:open": E
47
+ "onUpdate:open": O
48
48
  }, {
49
- default: _(() => [l(g(C), {
49
+ default: v(() => [l(g(w), {
50
50
  "as-child": "",
51
51
  disabled: t.disabled,
52
- onKeydown: D
52
+ onKeydown: k
53
53
  }, {
54
- default: _(() => [h(a.$slots, "default")]),
54
+ default: v(() => [h(a.$slots, "default")]),
55
55
  _: 3
56
- }, 8, ["disabled"]), l(g(b), null, {
57
- default: _(() => [l(g(v), {
56
+ }, 8, ["disabled"]), l(g(x), null, {
57
+ default: v(() => [l(g(y), {
58
58
  "aria-label": t.label,
59
59
  class: f([g(n), "min-w-48"]),
60
60
  "collision-padding": 8
61
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), {
62
+ default: v(() => [(p(!0), c(o, null, m(t.items, (t, n) => (p(), c(o, { key: n }, [t.type === "separator" ? (p(), s(g(C), {
63
63
  key: 0,
64
64
  class: f(g(i))
65
- }, null, 8, ["class"])) : (p(), s(g(y), {
65
+ }, null, 8, ["class"])) : (p(), s(g(b), {
66
66
  key: 1,
67
+ "as-child": "",
67
68
  class: f(g(r)),
68
- onSelect: (e) => g(T)(t)
69
+ "text-value": t.disabled ? "" : void 0,
70
+ onSelect: (e) => g(D)(t, e)
69
71
  }, {
70
- default: _(() => [l(e, { item: t }, null, 8, ["item"])]),
72
+ default: v(() => [l(e, {
73
+ item: t,
74
+ "reason-id": `${g(E)}-${n}`
75
+ }, null, 8, ["item", "reason-id"])]),
71
76
  _: 2
72
- }, 1032, ["class", "onSelect"]))], 64))), 128))]),
77
+ }, 1032, [
78
+ "class",
79
+ "text-value",
80
+ "onSelect"
81
+ ]))], 64))), 128))]),
73
82
  _: 1
74
83
  }, 8, ["aria-label", "class"])]),
75
84
  _: 1
@@ -79,6 +88,6 @@ var w = /*@__PURE__*/ u({
79
88
  }
80
89
  });
81
90
  //#endregion
82
- export { w as default };
91
+ export { T as default };
83
92
 
84
93
  //# sourceMappingURL=ContextMenu.vue_vue_type_script_setup_true_lang.js.map
@@ -1 +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
+ {"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","uid","useId","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","$event"],"sources":["../../../src/components/ContextMenu/ContextMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\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 uid = useId()\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\n v-else\n as-child\n :class=\"menuItemClass\"\n :text-value=\"item.disabled ? '' : undefined\"\n @select=\"select(item, $event)\"\n >\n <MenuItemContent :item=\"item\" :reason-id=\"`${uid}-${index}`\" />\n </ContextMenuItem>\n </template>\n </ContextMenuContent>\n </ContextMenuPortal>\n </ContextMenuRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgCA,IAAMe,IAAQC,GAMRC,IAAOH,GAOPI,IAAMC,EAAM,GACZC,IAASC,EAAcN,IAAQ,MAASE,EAAK,cAAcK,CAAI,CAAC;EAEtE,SAASC,EAAa,GAAe;GAEnC,AADAN,EAAK,eAAeO,CAAI,GACdP,EAANO,IAAW,SACL,MAAM;EAClB;EAIA,SAASC,EAAU,GAAsB;GACvC,IAAIV,EAAM,YAAY,EAAGW,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,kBAKE5B,EAAA,GAAAC,EAoBkBC,EAAA8B,CAAA,GAAA;GApBA,OAAO;GAAQ,iBAAaT;;GAC5C,SAAApB,QAEqB,CAFrBC,EAEqBF,EAAAiC,CAAA,GAAA;IAFD,YAAA;IAAU,UAAUnB,EAAA;IAAoB;;IAC1D,SAAAb,QAAQ,CAARE,EAAQgC,EAAA,QAAA,SAAA,C;;G,qBAEVjC,EAeoBF,EAAAoC,CAAA,GAAA,MAAA;IAdlB,SAAAnC,QAaqB,CAbrBC,EAaqBF,EAAAqC,CAAA,GAAA;KAbA,cAAYvB,EAAA;KAAQ,OAAKV,EAAA,CAAGJ,EAAAuC,CAAA,GAAgB,UAAA,CAAA;KAAgB,qBAAmB;;KACxF,SAAAtC,QAA8B,EAAxCH,EAAA,EAAA,GAAAO,EAWWC,GAAA,MAAAC,EAXuBO,EAAA,QAAhB,GAAM,OAAuBhB,EAAA,GAAAO,EAAAC,GAAA,EAAA,KAAAqC,EAAK,GAAA,CACtBvB,EAAK,SAAI,eAArCtB,EAAA,GAAAC,EAAqFC,EAAA4C,CAAA,GAAA;;MAA7B,OAAKxC,EAAEJ,EAAA6C,CAAA,CAAkB;K,2BACjF/C,EAAA,GAAAC,EAQkBC,EAAA8C,CAAA,GAAA;;MANhB,YAAA;MACC,OAAK1C,EAAEJ,EAAA+C,CAAA,CAAa;MACpB,cAAY3B,EAAK,WAAQ,KAAQ,KAAA;MACjC,WAAM,MAAEpB,EAAAkB,CAAA,CAAM,CAACE,GAAM4B,CAAM;;MAE5B,SAAA/C,QAA+D,CAA/DC,EAA+DM,GAAA;OAAvC;OAAO,aAAS,GAAKR,EAAAgB,CAAA,EAAG,GAAI2B"}
@@ -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 /** 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":""}
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 ContextMenu from '../ContextMenu/ContextMenu.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { DropdownMenuAction, DropdownMenuEntry } from '../internal/menu'\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 bound to `v-model:selected`: it shows on hover and focus, and on every row once anything is selected.\n * Shift-click (or Shift+Space) selects a range, Cmd or Ctrl-click toggles one row, Shift+↑ ↓ extends. */\n selectable?: boolean\n /** gutter: the checkbox in its own column. leading: it takes the place of the `leading` slot's first mark. */\n checkboxPlacement?: 'gutter' | 'leading'\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 /** The group headers' ground; by default sunken when joined, surface as cards. */\n groupHeaderTone?: 'sunken' | 'surface' | 'canvas'\n /** The group headers' height in px (36 by default); sets `--sui-list-header`. */\n groupHeaderHeight?: number\n /** Groups fold away under a chevron; a group's own `collapsible` wins. */\n collapsible?: boolean\n /** The rows' height in px, such as 44 (40 by default); sets `--sui-list-row`. */\n rowHeight?: number\n /** A hairline between rows. */\n dividers?: boolean\n /** Wraps each row in a ContextMenu of these items (`@context-item`); return [] for none. */\n contextItems?: (item: T) => DropdownMenuEntry[]\n /** The context menu's accessible name. */\n contextLabel?: string\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 checkboxPlacement: 'gutter',\n headingLevel: 3,\n groupHeaderTone: undefined,\n groupHeaderHeight: undefined,\n collapsible: true,\n rowHeight: undefined,\n dividers: false,\n contextItems: undefined,\n contextLabel: undefined,\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 emit = defineEmits<{ 'context-item': [action: DropdownMenuAction, item: T] }>()\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. With `checkboxPlacement=\"leading\"`, the\n * first mark gives way to the checkbox. */\n leading?: (props: { item: T; selected: boolean; selecting: boolean; toggle: () => void }) => 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 /** Controls at the end of a group's header, e.g. Show and Hide. */\n 'group-actions'?: (props: { group: DataListGroup<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\nconst collapsibleGroup = (group: DataListGroup<T>) => group.collapsible ?? props.collapsible\nconst isCollapsed = (group: DataListGroup<T>) => collapsibleGroup(group) && collapsed.value.includes(group.id)\nconst visibleItems = computed(() => props.groups.filter((group) => !isCollapsed(group)).flatMap((group) => group.items))\n\n// Where a range starts: the row last toggled.\nlet anchorKey: unknown = undefined\n\nfunction toggle(item: T) {\n const key = idOf(item)\n anchorKey = key\n selected.value = selected.value.includes(key) ? selected.value.filter((x) => x !== key) : [...selected.value, key]\n}\n\nfunction selectRange(item: T) {\n const keys = visibleItems.value.map(idOf)\n const to = keys.indexOf(idOf(item))\n const from = keys.indexOf(anchorKey)\n const span = from < 0 ? [keys[to]] : keys.slice(Math.min(from, to), Math.max(from, to) + 1)\n if (from < 0) anchorKey = idOf(item)\n selected.value = [...selected.value, ...span.filter((key) => !selected.value.includes(key))]\n}\n\nfunction select(...items: T[]) {\n const keys = items.map(idOf).filter((key) => !selected.value.includes(key))\n if (keys.length) selected.value = [...selected.value, ...new Set(keys)]\n}\n\nfunction clickRow(event: MouseEvent, item: T) {\n if (props.selectable && event.shiftKey) selectRange(item)\n else if (props.selectable && (event.metaKey || event.ctrlKey)) toggle(item)\n // Leading checkboxes stand in for the row's mark, so while selecting a click selects, as in a mail list.\n else if (props.selectable && leading.value && selected.value.length) toggle(item)\n else props.onRowClick?.(item)\n}\n\n// A shift-click selects rather than highlighting the text between.\nfunction onRowMousedown(event: MouseEvent) {\n if (props.selectable && event.shiftKey) event.preventDefault()\n}\n\n// ↑ ↓ or j k move between rows, x selects, Enter and Space open; with Shift, ↑ ↓ extend the selection and Space\n// selects a range.\nfunction onKeydown(event: KeyboardEvent, item: T) {\n if (event.target !== event.currentTarget || event.metaKey || event.ctrlKey || event.altKey) return\n // A collapsed group's rows are still in the DOM, hidden.\n const rows = [...(root.value?.querySelectorAll<HTMLElement>('[data-list-row]') ?? [])].filter(\n (row) => row.closest('ul')?.style.display !== 'none'\n )\n const i = rows.indexOf(event.currentTarget as HTMLElement)\n const down = event.key === 'ArrowDown' || event.key === 'j'\n const up = event.key === 'ArrowUp' || event.key === 'k'\n if ((down || up) && event.shiftKey && props.selectable) {\n const j = down ? Math.min(rows.length - 1, i + 1) : Math.max(0, i - 1)\n const next = visibleItems.value[j]\n select(item, ...(next ? [next] : []))\n rows[j]?.focus()\n } else if (down) rows[Math.min(rows.length - 1, i + 1)]?.focus()\n else if (up) rows[Math.max(0, i - 1)]?.focus()\n else if (event.key === ' ' && event.shiftKey && props.selectable) selectRange(item)\n else if (event.key.toLowerCase() === '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 leading = computed(() => props.selectable && props.checkboxPlacement === 'leading')\nconst gutter = computed(() => props.selectable && props.checkboxPlacement === 'gutter')\nconst tones = { sunken: 'bg-sui-sunken', surface: 'bg-sui-surface', canvas: 'bg-sui-canvas' }\nconst headerTone = computed(() => tones[props.groupHeaderTone ?? (cards.value ? 'surface' : 'sunken')])\nconst sizes = computed(() => {\n const style: Record<string, string> = {}\n if (props.rowHeight !== undefined) style['--sui-list-row'] = `${props.rowHeight}px`\n if (props.groupHeaderHeight !== undefined) style['--sui-list-header'] = `${props.groupHeaderHeight}px`\n return Object.keys(style).length ? style : undefined\n})\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\" :style=\"sizes\">\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-[var(--sui-list-row,2.5rem)] 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-[var(--sui-list-header,2.25rem)] items-center gap-2 border-b border-sui-line-subtle pr-2\"\n :class=\"[headerTone, !cards && 'sticky top-0 z-10', collapsibleGroup(group) ? 'pl-1.5' : 'pl-4']\"\n >\n <button\n v-if=\"collapsibleGroup(group)\"\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=\"isCollapsed(group) && '-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 <span class=\"ml-auto flex items-center gap-1\">\n <slot :group=\"group\" name=\"group-actions\" />\n </span>\n <button\n v-if=\"onNew\"\n :aria-label=\"newLabel.replace('{group}', groupName(group))\"\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=\"!isCollapsed(group)\" :id=\"`${id}-${group.id}`\" class=\"m-0 list-none p-0\">\n <ContextMenu\n v-for=\"item in group.items\"\n :key=\"idOf(item) as PropertyKey\"\n :disabled=\"!contextItems\"\n :items=\"contextItems?.(item)\"\n :label=\"contextLabel\"\n @click-item=\"emit('context-item', $event, item)\"\n >\n <li\n class=\"group/row relative flex h-[var(--sui-list-row,2.5rem)] items-center transition-colors duration-100 has-focus-visible:sui-ring-inset\"\n :class=\"[\n dividers && '[&+&]:border-t [&+&]:border-sui-line-subtle',\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 <!-- Outside the row, which is a button, over the leading slot's first mark: 16px in, or more for a child. -->\n <span\n v-if=\"leading\"\n class=\"absolute top-1/2 z-[1] flex size-4 -translate-y-1/2 items-center justify-center\"\n :class=\"!selected.length && 'hidden group-hover/row:flex group-has-focus-visible/row:flex'\"\n data-list-check\n :style=\"{ left: `${16 + (item.depth ?? 0) * 22}px` }\"\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 <span\n v-if=\"gutter\"\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=\"[gutter ? 'pl-2.5' : 'pl-4', (onRowClick || selectable) && 'cursor-pointer']\"\n data-list-row\n :role=\"onRowClick ? 'button' : undefined\"\n :tabindex=\"interactive ? 0 : undefined\"\n @click=\"clickRow($event, item)\"\n @keydown=\"onKeydown($event, item)\"\n @mousedown=\"onRowMousedown\"\n >\n <span\n v-if=\"$slots.leading\"\n class=\"flex shrink-0 items-center gap-2 text-sui-ink-muted tabular-nums\"\n :class=\"\n leading &&\n (selected.length\n ? '[&>*:first-child]:invisible'\n : 'group-hover/row:[&>*:first-child]:invisible group-has-focus-visible/row:[&>*:first-child]:invisible')\n \"\n >\n <slot\n :item=\"item\"\n name=\"leading\"\n :selected=\"selected.includes(idOf(item))\"\n :selecting=\"selected.length > 0\"\n :toggle=\"() => toggle(item)\"\n />\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\n v-if=\"$slots.meta\"\n class=\"flex shrink-0 items-center gap-2 text-xs text-sui-ink-muted tabular-nums\"\n >\n <slot :item=\"item\" name=\"meta\" />\n </span>\n </div>\n </li>\n </ContextMenu>\n </ul>\n </section>\n </template>\n </div>\n</template>\n"],"mappings":""}
@@ -1,15 +1,33 @@
1
+ import type { DropdownMenuAction, DropdownMenuEntry } from '../internal/menu';
1
2
  import type { DataListGroup } from './types';
2
3
  export interface DataListProps<T> {
3
4
  groups: DataListGroup<T>[];
4
5
  /** The item field that identifies it. An item's `depth` indents it as a child. */
5
6
  rowKey?: keyof T & string;
6
- /** A checkbox gutter bound to `v-model:selected`: it shows on hover, and on every row once anything is selected. */
7
+ /** A checkbox bound to `v-model:selected`: it shows on hover and focus, and on every row once anything is selected.
8
+ * Shift-click (or Shift+Space) selects a range, Cmd or Ctrl-click toggles one row, Shift+↑ ↓ extends. */
7
9
  selectable?: boolean;
10
+ /** gutter: the checkbox in its own column. leading: it takes the place of the `leading` slot's first mark. */
11
+ checkboxPlacement?: 'gutter' | 'leading';
8
12
  loading?: boolean;
9
13
  /** joined: one frame, sticky sunken group headers. cards: each group its own card, 12px apart, headers not sticky. */
10
14
  variant?: 'joined' | 'cards';
11
15
  /** The group headings' level, to fit the page's outline. */
12
16
  headingLevel?: 2 | 3 | 4 | 5 | 6;
17
+ /** The group headers' ground; by default sunken when joined, surface as cards. */
18
+ groupHeaderTone?: 'sunken' | 'surface' | 'canvas';
19
+ /** The group headers' height in px (36 by default); sets `--sui-list-header`. */
20
+ groupHeaderHeight?: number;
21
+ /** Groups fold away under a chevron; a group's own `collapsible` wins. */
22
+ collapsible?: boolean;
23
+ /** The rows' height in px, such as 44 (40 by default); sets `--sui-list-row`. */
24
+ rowHeight?: number;
25
+ /** A hairline between rows. */
26
+ dividers?: boolean;
27
+ /** Wraps each row in a ContextMenu of these items (`@context-item`); return [] for none. */
28
+ contextItems?: (item: T) => DropdownMenuEntry[];
29
+ /** The context menu's accessible name. */
30
+ contextLabel?: string;
13
31
  /** Makes rows open on click and Enter (`@row-click`). */
14
32
  onRowClick?: (item: T) => void;
15
33
  /** Adds a + to each group's header (`@new`). */
@@ -25,6 +43,7 @@ declare const __VLS_export: <T extends Record<string, any>>(__VLS_props: NonNull
25
43
  selected?: unknown[];
26
44
  collapsed?: string[];
27
45
  }) & {
46
+ "onContext-item"?: ((action: DropdownMenuAction, item: T) => any) | undefined;
28
47
  "onUpdate:selected"?: ((value: unknown[]) => any) | undefined;
29
48
  "onUpdate:collapsed"?: ((value: string[]) => any) | undefined;
30
49
  }> & (typeof globalThis extends {
@@ -43,9 +62,13 @@ declare const __VLS_export: <T extends Record<string, any>>(__VLS_props: NonNull
43
62
  count: number;
44
63
  collapsed: boolean;
45
64
  }) => unknown;
46
- /** Small marks before an item's title: a status icon, an id, an avatar. */
65
+ /** Small marks before an item's title: a status icon, an id, an avatar. With `checkboxPlacement="leading"`, the
66
+ * first mark gives way to the checkbox. */
47
67
  leading?: (props: {
48
68
  item: T;
69
+ selected: boolean;
70
+ selecting: boolean;
71
+ toggle: () => void;
49
72
  }) => unknown;
50
73
  /** The item's title; `item.name` by default. */
51
74
  title?: (props: {
@@ -55,10 +78,14 @@ declare const __VLS_export: <T extends Record<string, any>>(__VLS_props: NonNull
55
78
  meta?: (props: {
56
79
  item: T;
57
80
  }) => unknown;
81
+ /** Controls at the end of a group's header, e.g. Show and Hide. */
82
+ 'group-actions'?: (props: {
83
+ group: DataListGroup<T>;
84
+ }) => unknown;
58
85
  /** Shown when every group is empty, e.g. an EmptyState. */
59
86
  empty?: () => unknown;
60
87
  };
61
- emit: ((event: "update:selected", value: unknown[]) => void) & ((event: "update:collapsed", value: string[]) => void);
88
+ emit: ((evt: "context-item", action: DropdownMenuAction, item: T) => void) & (((event: "update:selected", value: unknown[]) => void) & ((event: "update:collapsed", value: string[]) => void));
62
89
  }>) => import("vue").VNode & {
63
90
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
64
91
  };