@sparklingopinions/sparkling-ui 0.14.0 → 0.15.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 +4 -0
- package/dist/components/ActionBar/ActionBar.js.map +1 -1
- package/dist/components/ActionBar/ActionBar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/ActionBar/ActionBar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Alert/Alert.js.map +1 -1
- package/dist/components/Alert/Alert.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Alert/Alert.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/AlertDialog/AlertDialog.js.map +1 -1
- package/dist/components/AlertDialog/AlertDialog.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/components/AlertDialog/AlertDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Avatar/Avatar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Badge/Badge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Badge/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Board/Board.js.map +1 -1
- package/dist/components/Board/Board.vue_vue_type_script_setup_true_lang.js +5 -5
- package/dist/components/Board/Board.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_vue_type_script_setup_true_lang.js +23 -22
- package/dist/components/Button/Button.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Calendar/Calendar.js.map +1 -1
- package/dist/components/Calendar/Calendar.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/Calendar/Calendar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Checkbox/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/DataList/DataList.js.map +1 -1
- package/dist/components/DataList/DataList.vue_vue_type_script_setup_true_lang.js +103 -103
- package/dist/components/DataList/DataList.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Dialog/ContextSeparator.js +19 -0
- package/dist/components/Dialog/ContextSeparator.js.map +1 -0
- package/dist/components/Dialog/ContextSeparator.vue.d.ts +3 -0
- package/dist/components/Dialog/Dialog.js.map +1 -1
- package/dist/components/Dialog/Dialog.vue.d.ts +16 -11
- package/dist/components/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js +130 -119
- package/dist/components/Dialog/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Dialog/focus.d.ts +2 -0
- package/dist/components/Dialog/focus.js +2 -2
- package/dist/components/Dialog/focus.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.js.map +1 -1
- package/dist/components/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/EmptyState/EmptyState.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/FilterChip.js.map +1 -1
- package/dist/components/Filters/FilterChip.vue_vue_type_script_setup_true_lang.js +7 -7
- package/dist/components/Filters/FilterChip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Filters/FilterFrame.js.map +1 -1
- package/dist/components/Filters/FilterFrame.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Filters/FilterFrame.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_vue_type_script_setup_true_lang.js +3 -3
- 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_vue_type_script_setup_true_lang.js +7 -7
- package/dist/components/Filters/SortMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/Input/Input.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Input/Input.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Kbd/Kbd.js +1 -1
- package/dist/components/Kbd/Kbd.js.map +1 -1
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/Pagination/Pagination.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Pagination/Pagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/Popover.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Popover/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Popover/PopoverDialog.js.map +1 -1
- package/dist/components/Popover/PopoverDialog.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Popover/PopoverDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/PropertyList/Editable.js.map +1 -1
- package/dist/components/PropertyList/Editable.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/PropertyList/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/components/SegmentedControl/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.vue_vue_type_script_setup_true_lang.js +4 -4
- package/dist/components/Select/Select.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.js.map +1 -1
- package/dist/components/Skeleton/Skeleton.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/components/Skeleton/Skeleton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Table/Table.js.map +1 -1
- package/dist/components/Table/Table.vue_vue_type_script_setup_true_lang.js +3 -3
- 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_vue_type_script_setup_true_lang.js +3 -3
- package/dist/components/Tag/Tag.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/Textarea/Textarea.vue.d.ts +3 -0
- package/dist/components/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js +7 -2
- package/dist/components/Textarea/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/internal/menu.d.ts +2 -2
- package/dist/components/internal/menu.js +1 -1
- package/dist/components/internal/menu.js.map +1 -1
- package/dist/index.js +15 -15
- package/package.json +1 -1
- package/src/styles/tailwind.css +74 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Board.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_unref","_createElementVNode","_createBlock","_resolveDynamicComponent","_withCtx","Icon","_createCommentVNode","_createTextVNode","_toDisplayString","_renderSlot","_createVNode","Skeleton","_withModifiers","Icon_default","Skeleton_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","MOUSE_SLOP","TOUCH_SLOP","TOUCH_DELAY","EDGE","frame","Board_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","id","useId","idOf","item","root","ref","dragged","target","pending","pressTimer","ghost","grab","justDragged","onPointerDown","event","touch","start","onPointerMove","onPointerUp","stop","holdScroll","onEscape","rect","follow","moved","aim","scrollNear","x","y","under","body","column","card","index","c","board","from","to","clickCard","onCardKeydown","isTarget","openBlock","createElementBlock","normalizeClass","Fragment","renderList","unref","createElementVNode","createBlock","resolveDynamicComponent","withCtx","createCommentVNode","createTextVNode","toDisplayString","renderSlot","_ctx","i","createVNode","$event","_cache","withModifiers"],"sources":["../../../src/components/Board/Board.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { onBeforeUnmount, ref, useId } from 'vue'\n\nimport Icon from '../Icon/Icon.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { BoardColumn } from './types'\n\nexport interface BoardProps<T> {\n columns: BoardColumn<T>[]\n /** The item field that identifies it. */\n rowKey?: keyof T & string\n /** Cards also reorder within a column, for records that carry a position. */\n sortable?: boolean\n /** Columns run to the bottom of the parent and scroll inside it; give the parent a height. False sizes them to their cards. */\n fill?: boolean\n loading?: boolean\n /** The columns' heading level, to fit the page's outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n /** Draws each card's border, padding and surface. False when the `card` slot draws its own. */\n cardFrame?: boolean\n /** Makes a card a button that opens on click, Enter and Space (`@card-click`). Without it, the card slot carries its\n * own link or buttons. */\n onCardClick?: (item: T) => void\n /** Adds a dashed \"New\" button at the foot of each column, shown on hover (`@new`). */\n onNew?: (column: BoardColumn<T>) => void\n newLabel?: string\n emptyLabel?: string\n}\n\nconst props = withDefaults(defineProps<BoardProps<T>>(), {\n rowKey: 'id' as never,\n fill: true,\n headingLevel: 3,\n cardFrame: true,\n onCardClick: undefined,\n onNew: undefined,\n newLabel: 'New',\n emptyLabel: 'No cards'\n})\n\nconst emit = defineEmits<{\n /** A card dropped into a column, at its new index there. Show it moved at once; put it back if saving fails. */\n move: [id: unknown, column: string, index: number]\n}>()\n\ndefineSlots<{\n /** A card's content; `item.name` as a title by default. */\n card?: (props: { item: T; column: BoardColumn<T> }) => unknown\n /** Replaces a column's count, e.g. its summed deal value. */\n count?: (props: { column: BoardColumn<T> }) => unknown\n}>()\n\nconst id = useId()\nconst idOf = (item: T): unknown => item[props.rowKey ?? 'id']\nconst root = ref<HTMLElement>()\n\nconst dragged = ref<{ id: unknown; column: string; index: number } | null>(null)\nconst target = ref<{ column: string; index: number } | null>(null)\n\n// Dragging runs on pointer events, so it works with a mouse, a pen and a finger. A mouse drags once it moves a few\n// pixels, so a click still clicks; a finger after a short press, so a swipe still scrolls.\nconst MOUSE_SLOP = 4\nconst TOUCH_SLOP = 8\nconst TOUCH_DELAY = 250\nconst EDGE = 48\n\nlet pending: {\n item: T\n column: string\n index: number\n x: number\n y: number\n card: HTMLElement\n touch: boolean\n} | null = null\nlet pressTimer: number | undefined\nlet ghost: HTMLElement | null = null\nlet grab = { x: 0, y: 0 }\nlet justDragged = false\n\nfunction onPointerDown(event: PointerEvent, item: T, column: string, index: number) {\n if (event.button !== 0 || (event.target as HTMLElement).closest('input, textarea, select, [contenteditable]')) return\n const touch = event.pointerType === 'touch'\n pending = { item, column, index, x: event.clientX, y: event.clientY, card: event.currentTarget as HTMLElement, touch }\n if (touch) pressTimer = window.setTimeout(() => pending && start(), TOUCH_DELAY)\n window.addEventListener('pointermove', onPointerMove)\n window.addEventListener('pointerup', onPointerUp)\n window.addEventListener('pointercancel', stop)\n window.addEventListener('touchmove', holdScroll, { passive: false })\n window.addEventListener('keydown', onEscape)\n}\n\nfunction start() {\n if (!pending) return\n const rect = pending.card.getBoundingClientRect()\n grab = { x: pending.x - rect.left, y: pending.y - rect.top }\n ghost = pending.card.cloneNode(true) as HTMLElement\n ghost.removeAttribute('id')\n ghost.setAttribute('aria-hidden', 'true')\n ghost.classList.add('pointer-events-none', 'fixed', 'top-0', 'left-0', 'z-[1300]', 'shadow-lg', 'opacity-95')\n ghost.style.width = `${rect.width}px`\n document.body.appendChild(ghost)\n follow(pending.x, pending.y)\n dragged.value = { id: idOf(pending.item), column: pending.column, index: pending.index }\n}\n\nfunction onPointerMove(event: PointerEvent) {\n if (!pending) return\n if (!dragged.value) {\n const moved = Math.hypot(event.clientX - pending.x, event.clientY - pending.y)\n // A finger that moves before the press is long enough is scrolling.\n if (pending.touch) return moved > TOUCH_SLOP ? stop() : undefined\n if (moved < MOUSE_SLOP) return\n start()\n }\n event.preventDefault()\n follow(event.clientX, event.clientY)\n aim(event.clientX, event.clientY)\n scrollNear(event.clientX, event.clientY)\n}\n\nfunction follow(x: number, y: number) {\n if (ghost) ghost.style.transform = `translate(${x - grab.x}px, ${y - grab.y}px) rotate(1.5deg)`\n}\n\n// What's under the pointer: a card's top half drops before it, the bottom half after; an empty spot, at the end.\nfunction aim(x: number, y: number) {\n const under = document.elementFromPoint(x, y)\n const body = under?.closest<HTMLElement>('[data-board-column]')\n if (!body || !root.value?.contains(body)) {\n target.value = null\n return\n }\n const column = body.dataset.boardColumn!\n const card = under!.closest<HTMLElement>('[data-board-index]')\n let index = props.columns.find((c) => c.id === column)?.items.length ?? 0\n if (card && body.contains(card)) {\n const rect = card.getBoundingClientRect()\n index = Number(card.dataset.boardIndex) + (y < rect.top + rect.height / 2 ? 0 : 1)\n }\n if (target.value?.column !== column || target.value.index !== index) target.value = { column, index }\n}\n\n// Near a column's top or bottom, or the board's sides, it scrolls that way.\nfunction scrollNear(x: number, y: number) {\n const body = document.elementFromPoint(x, y)?.closest<HTMLElement>('[data-board-column]')\n if (body) {\n const rect = body.getBoundingClientRect()\n if (y < rect.top + EDGE) body.scrollTop -= 12\n else if (y > rect.bottom - EDGE) body.scrollTop += 12\n }\n const board = root.value?.getBoundingClientRect()\n if (board && root.value) {\n if (x < board.left + EDGE) root.value.scrollLeft -= 12\n else if (x > board.right - EDGE) root.value.scrollLeft += 12\n }\n}\n\nfunction onPointerUp() {\n const from = dragged.value\n const to = target.value\n if (from && to) {\n let index = to.index\n if (from.column === to.column) {\n // Within a column the dragged card leaves its old slot first.\n if (index > from.index) index--\n if (props.sortable && index !== from.index) emit('move', from.id, to.column, index)\n } else {\n emit('move', from.id, to.column, index)\n }\n }\n stop()\n}\n\nfunction holdScroll(event: TouchEvent) {\n if (dragged.value) event.preventDefault()\n}\n\nfunction onEscape(event: KeyboardEvent) {\n if (event.key === 'Escape' && dragged.value) {\n event.preventDefault()\n stop()\n }\n}\n\nfunction stop() {\n window.clearTimeout(pressTimer)\n window.removeEventListener('pointermove', onPointerMove)\n window.removeEventListener('pointerup', onPointerUp)\n window.removeEventListener('pointercancel', stop)\n window.removeEventListener('touchmove', holdScroll)\n window.removeEventListener('keydown', onEscape)\n ghost?.remove()\n ghost = null\n // The click that ends a drag isn't a click on the card.\n if (dragged.value) {\n justDragged = true\n window.setTimeout(() => (justDragged = false))\n }\n pending = null\n dragged.value = null\n target.value = null\n}\n\nonBeforeUnmount(stop)\n\nfunction clickCard(event: MouseEvent, item: T) {\n if (justDragged) {\n event.preventDefault()\n event.stopPropagation()\n return\n }\n props.onCardClick?.(item)\n}\n\nfunction onCardKeydown(event: KeyboardEvent, item: T) {\n if (!props.onCardClick || event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return\n event.preventDefault()\n props.onCardClick(item)\n}\n\nconst isTarget = (column: string, index: number, item: T) =>\n target.value?.column === column && target.value.index === index && dragged.value?.id !== idOf(item)\n\nconst frame =\n 'border border-sui-line-subtle bg-sui-surface p-4 transition-[border-color,opacity] duration-150 hover:border-sui-line'\n</script>\n\n<!-- A board: cards in columns by a field (a stage, a status), dragged between columns. -->\n<template>\n <div\n ref=\"root\"\n class=\"flex items-stretch gap-5 overflow-x-auto pb-2 after:w-1 after:shrink-0 after:content-['']\"\n :class=\"fill && 'h-full min-h-0 flex-1'\"\n >\n <section\n v-for=\"column in columns\"\n :key=\"column.id\"\n :aria-labelledby=\"`${id}-${column.id}`\"\n class=\"flex min-h-0 w-full max-w-xs min-w-[16rem] shrink-0 flex-col\"\n >\n <header class=\"flex items-center justify-between px-1 pb-2.5\">\n <component\n :is=\"`h${headingLevel}`\"\n :id=\"`${id}-${column.id}`\"\n class=\"m-0 flex items-center gap-2 text-sm font-semibold text-sui-ink\"\n >\n <Icon v-if=\"column.icon\" :color=\"column.iconColor ?? 'ink-subtle'\" :name=\"column.icon\" />\n {{ column.name }}\n </component>\n <span\n class=\"ml-2 inline-flex h-5 items-center justify-center rounded-full bg-sui-sunken px-2 text-xs text-sui-ink-muted tabular-nums\"\n >\n <slot :column=\"column\" name=\"count\">{{ column.items.length }}</slot>\n </span>\n </header>\n <div\n class=\"group/body flex min-h-[120px] flex-1 flex-col gap-2.5 overflow-y-auto rounded-xl bg-sui-sunken p-2.5 transition-shadow duration-150\"\n :class=\"target?.column === column.id && 'ring-2 ring-sui-accent-line ring-inset'\"\n :data-board-column=\"column.id\"\n >\n <template v-if=\"loading\">\n <div\n v-for=\"i in 3\"\n :key=\"i\"\n aria-hidden=\"true\"\n class=\"rounded-xl border border-sui-line-subtle bg-sui-surface p-4\"\n >\n <Skeleton class=\"mb-3\" />\n <Skeleton height=\"0.75rem\" width=\"70%\" />\n </div>\n </template>\n <p\n v-else-if=\"column.items.length === 0 && !dragged\"\n class=\"m-0 px-1 py-3 text-center text-13 text-sui-ink-muted\"\n >\n {{ emptyLabel }}\n </p>\n <ul v-else class=\"m-0 flex list-none flex-col gap-2.5 p-0\">\n <li v-for=\"(item, index) in column.items\" :key=\"idOf(item) as PropertyKey\">\n <div\n class=\"relative rounded-xl outline-hidden transition-opacity duration-150 select-none\"\n :class=\"[\n cardFrame && frame,\n onCardClick && 'cursor-pointer focus-visible:sui-ring',\n dragged?.id === idOf(item) && 'opacity-40',\n isTarget(column.id, index, item) && 'shadow-[0_-6px_0_-4px_rgb(var(--sui-accent-solid))]'\n ]\"\n :data-board-index=\"index\"\n :role=\"onCardClick ? 'button' : undefined\"\n :tabindex=\"onCardClick ? 0 : undefined\"\n @click.capture=\"clickCard($event, item)\"\n @contextmenu=\"dragged && $event.preventDefault()\"\n @dragstart.prevent\n @keydown=\"onCardKeydown($event, item)\"\n @pointerdown=\"onPointerDown($event, item, column.id, index)\"\n >\n <slot :column=\"column\" :item=\"item\" name=\"card\">\n <p class=\"m-0 text-sm font-semibold text-sui-ink\">{{ item.name }}</p>\n </slot>\n </div>\n </li>\n </ul>\n <button\n v-if=\"onNew && !loading\"\n class=\"flex h-9 w-full shrink-0 items-center justify-center gap-1.5 rounded-lg border border-dashed border-sui-line-strong text-xs font-medium text-sui-ink-muted opacity-0 outline-hidden transition-all duration-150 group-hover/body:opacity-100 hover:border-sui-ink-muted hover:bg-sui-surface/60 hover:text-sui-ink-body focus-visible:opacity-100 focus-visible:sui-ring\"\n type=\"button\"\n @click=\"onNew(column)\"\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 {{ newLabel }}\n </button>\n </div>\n </section>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Board.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_normalizeClass","_Fragment","_renderList","_unref","_createElementVNode","_createBlock","_resolveDynamicComponent","_withCtx","Icon","_createCommentVNode","_createTextVNode","_toDisplayString","_renderSlot","_createVNode","Skeleton","_withModifiers","Icon_default","Skeleton_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","MOUSE_SLOP","TOUCH_SLOP","TOUCH_DELAY","EDGE","frame","Board_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","id","useId","idOf","item","root","ref","dragged","target","pending","pressTimer","ghost","grab","justDragged","onPointerDown","event","touch","start","onPointerMove","onPointerUp","stop","holdScroll","onEscape","rect","follow","moved","aim","scrollNear","x","y","under","body","column","card","index","c","board","from","to","clickCard","onCardKeydown","isTarget","openBlock","createElementBlock","normalizeClass","Fragment","renderList","unref","createElementVNode","createBlock","resolveDynamicComponent","withCtx","createCommentVNode","createTextVNode","toDisplayString","renderSlot","_ctx","i","createVNode","$event","_cache","withModifiers"],"sources":["../../../src/components/Board/Board.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T extends Record<string, any>\">\nimport { onBeforeUnmount, ref, useId } from 'vue'\n\nimport Icon from '../Icon/Icon.vue'\nimport Skeleton from '../Skeleton/Skeleton.vue'\nimport type { BoardColumn } from './types'\n\nexport interface BoardProps<T> {\n columns: BoardColumn<T>[]\n /** The item field that identifies it. */\n rowKey?: keyof T & string\n /** Cards also reorder within a column, for records that carry a position. */\n sortable?: boolean\n /** Columns run to the bottom of the parent and scroll inside it; give the parent a height. False sizes them to their cards. */\n fill?: boolean\n loading?: boolean\n /** The columns' heading level, to fit the page's outline. */\n headingLevel?: 2 | 3 | 4 | 5 | 6\n /** Draws each card's border, padding and surface. False when the `card` slot draws its own. */\n cardFrame?: boolean\n /** Makes a card a button that opens on click, Enter and Space (`@card-click`). Without it, the card slot carries its\n * own link or buttons. */\n onCardClick?: (item: T) => void\n /** Adds a dashed \"New\" button at the foot of each column, shown on hover (`@new`). */\n onNew?: (column: BoardColumn<T>) => void\n newLabel?: string\n emptyLabel?: string\n}\n\nconst props = withDefaults(defineProps<BoardProps<T>>(), {\n rowKey: 'id' as never,\n fill: true,\n headingLevel: 3,\n cardFrame: true,\n onCardClick: undefined,\n onNew: undefined,\n newLabel: 'New',\n emptyLabel: 'No cards'\n})\n\nconst emit = defineEmits<{\n /** A card dropped into a column, at its new index there. Show it moved at once; put it back if saving fails. */\n move: [id: unknown, column: string, index: number]\n}>()\n\ndefineSlots<{\n /** A card's content; `item.name` as a title by default. */\n card?: (props: { item: T; column: BoardColumn<T> }) => unknown\n /** Replaces a column's count, e.g. its summed deal value. */\n count?: (props: { column: BoardColumn<T> }) => unknown\n}>()\n\nconst id = useId()\nconst idOf = (item: T): unknown => item[props.rowKey ?? 'id']\nconst root = ref<HTMLElement>()\n\nconst dragged = ref<{ id: unknown; column: string; index: number } | null>(null)\nconst target = ref<{ column: string; index: number } | null>(null)\n\n// Dragging runs on pointer events, so it works with a mouse, a pen and a finger. A mouse drags once it moves a few\n// pixels, so a click still clicks; a finger after a short press, so a swipe still scrolls.\nconst MOUSE_SLOP = 4\nconst TOUCH_SLOP = 8\nconst TOUCH_DELAY = 250\nconst EDGE = 48\n\nlet pending: {\n item: T\n column: string\n index: number\n x: number\n y: number\n card: HTMLElement\n touch: boolean\n} | null = null\nlet pressTimer: number | undefined\nlet ghost: HTMLElement | null = null\nlet grab = { x: 0, y: 0 }\nlet justDragged = false\n\nfunction onPointerDown(event: PointerEvent, item: T, column: string, index: number) {\n if (event.button !== 0 || (event.target as HTMLElement).closest('input, textarea, select, [contenteditable]')) return\n const touch = event.pointerType === 'touch'\n pending = { item, column, index, x: event.clientX, y: event.clientY, card: event.currentTarget as HTMLElement, touch }\n if (touch) pressTimer = window.setTimeout(() => pending && start(), TOUCH_DELAY)\n window.addEventListener('pointermove', onPointerMove)\n window.addEventListener('pointerup', onPointerUp)\n window.addEventListener('pointercancel', stop)\n window.addEventListener('touchmove', holdScroll, { passive: false })\n window.addEventListener('keydown', onEscape)\n}\n\nfunction start() {\n if (!pending) return\n const rect = pending.card.getBoundingClientRect()\n grab = { x: pending.x - rect.left, y: pending.y - rect.top }\n ghost = pending.card.cloneNode(true) as HTMLElement\n ghost.removeAttribute('id')\n ghost.setAttribute('aria-hidden', 'true')\n ghost.classList.add('pointer-events-none', 'fixed', 'top-0', 'left-0', 'z-[1300]', 'shadow-lg', 'opacity-95')\n ghost.style.width = `${rect.width}px`\n document.body.appendChild(ghost)\n follow(pending.x, pending.y)\n dragged.value = { id: idOf(pending.item), column: pending.column, index: pending.index }\n}\n\nfunction onPointerMove(event: PointerEvent) {\n if (!pending) return\n if (!dragged.value) {\n const moved = Math.hypot(event.clientX - pending.x, event.clientY - pending.y)\n // A finger that moves before the press is long enough is scrolling.\n if (pending.touch) return moved > TOUCH_SLOP ? stop() : undefined\n if (moved < MOUSE_SLOP) return\n start()\n }\n event.preventDefault()\n follow(event.clientX, event.clientY)\n aim(event.clientX, event.clientY)\n scrollNear(event.clientX, event.clientY)\n}\n\nfunction follow(x: number, y: number) {\n if (ghost) ghost.style.transform = `translate(${x - grab.x}px, ${y - grab.y}px) rotate(1.5deg)`\n}\n\n// What's under the pointer: a card's top half drops before it, the bottom half after; an empty spot, at the end.\nfunction aim(x: number, y: number) {\n const under = document.elementFromPoint(x, y)\n const body = under?.closest<HTMLElement>('[data-board-column]')\n if (!body || !root.value?.contains(body)) {\n target.value = null\n return\n }\n const column = body.dataset.boardColumn!\n const card = under!.closest<HTMLElement>('[data-board-index]')\n let index = props.columns.find((c) => c.id === column)?.items.length ?? 0\n if (card && body.contains(card)) {\n const rect = card.getBoundingClientRect()\n index = Number(card.dataset.boardIndex) + (y < rect.top + rect.height / 2 ? 0 : 1)\n }\n if (target.value?.column !== column || target.value.index !== index) target.value = { column, index }\n}\n\n// Near a column's top or bottom, or the board's sides, it scrolls that way.\nfunction scrollNear(x: number, y: number) {\n const body = document.elementFromPoint(x, y)?.closest<HTMLElement>('[data-board-column]')\n if (body) {\n const rect = body.getBoundingClientRect()\n if (y < rect.top + EDGE) body.scrollTop -= 12\n else if (y > rect.bottom - EDGE) body.scrollTop += 12\n }\n const board = root.value?.getBoundingClientRect()\n if (board && root.value) {\n if (x < board.left + EDGE) root.value.scrollLeft -= 12\n else if (x > board.right - EDGE) root.value.scrollLeft += 12\n }\n}\n\nfunction onPointerUp() {\n const from = dragged.value\n const to = target.value\n if (from && to) {\n let index = to.index\n if (from.column === to.column) {\n // Within a column the dragged card leaves its old slot first.\n if (index > from.index) index--\n if (props.sortable && index !== from.index) emit('move', from.id, to.column, index)\n } else {\n emit('move', from.id, to.column, index)\n }\n }\n stop()\n}\n\nfunction holdScroll(event: TouchEvent) {\n if (dragged.value) event.preventDefault()\n}\n\nfunction onEscape(event: KeyboardEvent) {\n if (event.key === 'Escape' && dragged.value) {\n event.preventDefault()\n stop()\n }\n}\n\nfunction stop() {\n window.clearTimeout(pressTimer)\n window.removeEventListener('pointermove', onPointerMove)\n window.removeEventListener('pointerup', onPointerUp)\n window.removeEventListener('pointercancel', stop)\n window.removeEventListener('touchmove', holdScroll)\n window.removeEventListener('keydown', onEscape)\n ghost?.remove()\n ghost = null\n // The click that ends a drag isn't a click on the card.\n if (dragged.value) {\n justDragged = true\n window.setTimeout(() => (justDragged = false))\n }\n pending = null\n dragged.value = null\n target.value = null\n}\n\nonBeforeUnmount(stop)\n\nfunction clickCard(event: MouseEvent, item: T) {\n if (justDragged) {\n event.preventDefault()\n event.stopPropagation()\n return\n }\n props.onCardClick?.(item)\n}\n\nfunction onCardKeydown(event: KeyboardEvent, item: T) {\n if (!props.onCardClick || event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return\n event.preventDefault()\n props.onCardClick(item)\n}\n\nconst isTarget = (column: string, index: number, item: T) =>\n target.value?.column === column && target.value.index === index && dragged.value?.id !== idOf(item)\n\nconst frame =\n 'border border-sui-edge bg-sui-raised p-4 transition-[border-color,opacity] duration-150 hover:border-sui-line-strong'\n</script>\n\n<!-- A board: cards in columns by a field (a stage, a status), dragged between columns. -->\n<template>\n <div\n ref=\"root\"\n class=\"flex items-stretch gap-5 overflow-x-auto pb-2 after:w-1 after:shrink-0 after:content-['']\"\n :class=\"fill && 'h-full min-h-0 flex-1'\"\n >\n <section\n v-for=\"column in columns\"\n :key=\"column.id\"\n :aria-labelledby=\"`${id}-${column.id}`\"\n class=\"flex min-h-0 w-full max-w-xs min-w-[16rem] shrink-0 flex-col\"\n >\n <header class=\"flex items-center justify-between px-1 pb-2.5\">\n <component\n :is=\"`h${headingLevel}`\"\n :id=\"`${id}-${column.id}`\"\n class=\"m-0 flex items-center gap-2 text-sm font-semibold text-sui-ink\"\n >\n <Icon v-if=\"column.icon\" :color=\"column.iconColor ?? 'ink-subtle'\" :name=\"column.icon\" />\n {{ column.name }}\n </component>\n <span\n class=\"ml-2 inline-flex h-5 items-center justify-center rounded-full bg-sui-fill px-2 text-xs text-sui-ink-muted tabular-nums\"\n >\n <slot :column=\"column\" name=\"count\">{{ column.items.length }}</slot>\n </span>\n </header>\n <div\n class=\"group/body flex min-h-[120px] flex-1 flex-col gap-2.5 overflow-y-auto rounded-xl bg-sui-fill p-2.5 transition-shadow duration-150\"\n :class=\"target?.column === column.id && 'ring-2 ring-sui-accent-line ring-inset'\"\n :data-board-column=\"column.id\"\n >\n <template v-if=\"loading\">\n <div v-for=\"i in 3\" :key=\"i\" aria-hidden=\"true\" class=\"rounded-xl border border-sui-edge bg-sui-raised p-4\">\n <Skeleton class=\"mb-3\" />\n <Skeleton height=\"0.75rem\" width=\"70%\" />\n </div>\n </template>\n <p\n v-else-if=\"column.items.length === 0 && !dragged\"\n class=\"m-0 px-1 py-3 text-center text-13 text-sui-ink-muted\"\n >\n {{ emptyLabel }}\n </p>\n <ul v-else class=\"m-0 flex list-none flex-col gap-2.5 p-0\">\n <li v-for=\"(item, index) in column.items\" :key=\"idOf(item) as PropertyKey\">\n <div\n class=\"relative rounded-xl outline-hidden transition-opacity duration-150 select-none\"\n :class=\"[\n cardFrame && frame,\n onCardClick && 'cursor-pointer focus-visible:sui-ring',\n dragged?.id === idOf(item) && 'opacity-40',\n isTarget(column.id, index, item) && 'shadow-[0_-6px_0_-4px_rgb(var(--sui-accent-solid))]'\n ]\"\n :data-board-index=\"index\"\n :role=\"onCardClick ? 'button' : undefined\"\n :tabindex=\"onCardClick ? 0 : undefined\"\n @click.capture=\"clickCard($event, item)\"\n @contextmenu=\"dragged && $event.preventDefault()\"\n @dragstart.prevent\n @keydown=\"onCardKeydown($event, item)\"\n @pointerdown=\"onPointerDown($event, item, column.id, index)\"\n >\n <slot :column=\"column\" :item=\"item\" name=\"card\">\n <p class=\"m-0 text-sm font-semibold text-sui-ink\">{{ item.name }}</p>\n </slot>\n </div>\n </li>\n </ul>\n <button\n v-if=\"onNew && !loading\"\n class=\"flex h-(--sui-control-height) w-full shrink-0 items-center justify-center gap-1.5 rounded-lg border border-dashed border-sui-line-strong text-xs font-medium text-sui-ink-muted opacity-0 outline-hidden transition-all duration-150 group-hover/body:opacity-100 hover:border-sui-ink-muted hover:bg-sui-surface/60 hover:text-sui-ink-body focus-visible:opacity-100 focus-visible:sui-ring\"\n type=\"button\"\n @click=\"onNew(column)\"\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 {{ newLabel }}\n </button>\n </div>\n </section>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -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 /** 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-
|
|
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-fill-line bg-sui-fill text-sui-ink-emphasis not-data-disabled:hover:bg-sui-fill-hover not-data-disabled:hover:text-sui-ink [&_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-fill-hover 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\n// Filled buttons turn neutral when off, rather than fading their colour; ghosts, with nothing to fill, fade.\nconst ghosts: ButtonVariant[] = ['ghost', 'ghost-inverse']\nconst offClass =\n 'data-disabled:border-sui-fill-line data-disabled:bg-sui-fill data-disabled:text-sui-ink-muted data-disabled:shadow-none data-disabled:[&_svg]:stroke-sui-ink-muted'\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',\n ghosts.includes(props.variant) ? 'data-disabled:opacity-50' : offClass,\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":""}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { computed as e, createBlock as t, defineComponent as n, normalizeClass as r, openBlock as i, renderSlot as a, unref as o, withCtx as s } from "vue";
|
|
2
2
|
import { Primitive as c } from "reka-ui";
|
|
3
3
|
//#region src/components/Button/Button.vue?vue&type=script&setup=true&lang.ts
|
|
4
|
-
var l = /*@__PURE__*/ n({
|
|
4
|
+
var l = "data-disabled:border-sui-fill-line data-disabled:bg-sui-fill data-disabled:text-sui-ink-muted data-disabled:shadow-none data-disabled:[&_svg]:stroke-sui-ink-muted", u = /*@__PURE__*/ n({
|
|
5
5
|
__name: "Button",
|
|
6
6
|
props: {
|
|
7
7
|
variant: { default: "secondary" },
|
|
@@ -18,52 +18,53 @@ var l = /*@__PURE__*/ n({
|
|
|
18
18
|
as: { default: "button" }
|
|
19
19
|
},
|
|
20
20
|
setup(n) {
|
|
21
|
-
let
|
|
21
|
+
let u = n, d = {
|
|
22
22
|
primary: "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",
|
|
23
|
-
secondary: "border border-sui-line bg-sui-
|
|
23
|
+
secondary: "border border-sui-fill-line bg-sui-fill text-sui-ink-emphasis not-data-disabled:hover:bg-sui-fill-hover not-data-disabled:hover:text-sui-ink [&_svg]:stroke-sui-ink-emphasis",
|
|
24
24
|
tinted: "border border-sui-accent-line bg-sui-accent-wash text-sui-accent not-data-disabled:hover:bg-sui-accent-soft [&_svg]:stroke-sui-accent",
|
|
25
|
-
ghost: "border border-transparent bg-transparent text-sui-ink-subtle not-data-disabled:hover:bg-sui-
|
|
25
|
+
ghost: "border border-transparent bg-transparent text-sui-ink-subtle not-data-disabled:hover:bg-sui-fill-hover not-data-disabled:hover:text-sui-ink-emphasis",
|
|
26
26
|
contrast: "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",
|
|
27
27
|
danger: "bg-sui-danger-solid text-sui-on-danger not-data-disabled:hover:bg-sui-danger-hover",
|
|
28
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",
|
|
29
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"
|
|
30
|
-
},
|
|
30
|
+
}, f = {
|
|
31
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
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
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
|
-
},
|
|
34
|
+
}, p = {
|
|
35
35
|
xs: "h-6",
|
|
36
36
|
sm: "h-7",
|
|
37
37
|
md: "h-(--sui-button-height)",
|
|
38
38
|
lg: "h-12"
|
|
39
|
-
},
|
|
39
|
+
}, m = {
|
|
40
40
|
xs: "px-2",
|
|
41
41
|
sm: "px-2.5",
|
|
42
42
|
md: "px-(--sui-button-padding-x)",
|
|
43
43
|
lg: "px-(--sui-button-padding-x)"
|
|
44
|
-
},
|
|
44
|
+
}, h = {
|
|
45
45
|
xs: "text-xs",
|
|
46
46
|
sm: "text-sm",
|
|
47
47
|
md: "text-(length:--sui-button-text) leading-5",
|
|
48
48
|
lg: "text-(length:--sui-button-text) leading-5"
|
|
49
|
-
},
|
|
50
|
-
"flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
f[
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
}, g = ["ghost", "ghost-inverse"], _ = e(() => [
|
|
50
|
+
"flex items-center justify-center font-medium text-nowrap no-underline transition-all duration-200 ease-in-out",
|
|
51
|
+
g.includes(u.variant) ? "data-disabled:opacity-50" : l,
|
|
52
|
+
u.variant === "ghost-inverse" ? "focus-visible:sui-ring-inverse" : "focus-visible:sui-ring",
|
|
53
|
+
u.pressed && f[u.variant] || d[u.variant],
|
|
54
|
+
p[u.size],
|
|
55
|
+
h[u.size],
|
|
56
|
+
u.iconOnly ? "aspect-square px-0" : m[u.size],
|
|
57
|
+
u.pill ? "rounded-full" : u.size === "xs" ? "rounded-md" : "rounded-(--sui-radius-button)",
|
|
58
|
+
u.iconOnly ? "" : u.iconPosition === "end" ? "[&_svg]:ml-2" : "[&_svg:first-child]:mr-2"
|
|
59
|
+
]), v = e(() => u.as === "button" && !u.asChild);
|
|
59
60
|
return (e, l) => (i(), t(o(c), {
|
|
60
|
-
"aria-disabled": n.disabled && !
|
|
61
|
+
"aria-disabled": n.disabled && !v.value ? "true" : void 0,
|
|
61
62
|
"aria-pressed": n.pressed === void 0 ? void 0 : String(n.pressed),
|
|
62
63
|
as: n.as,
|
|
63
64
|
"as-child": n.asChild,
|
|
64
|
-
class: r(
|
|
65
|
+
class: r(_.value),
|
|
65
66
|
"data-disabled": n.disabled ? "" : void 0,
|
|
66
|
-
disabled:
|
|
67
|
+
disabled: v.value ? n.disabled : void 0
|
|
67
68
|
}, {
|
|
68
69
|
default: s(() => [a(e.$slots, "default")]),
|
|
69
70
|
_: 3
|
|
@@ -79,6 +80,6 @@ var l = /*@__PURE__*/ n({
|
|
|
79
80
|
}
|
|
80
81
|
});
|
|
81
82
|
//#endregion
|
|
82
|
-
export {
|
|
83
|
+
export { u as default };
|
|
83
84
|
|
|
84
85
|
//# sourceMappingURL=Button.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -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","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-
|
|
1
|
+
{"version":3,"file":"Button.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_renderSlot","offClass","Button_vue_vue_type_script_setup_true_lang_default","props","__props","variants","pressedVariants","heights","paddings","texts","ghosts","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-fill-line bg-sui-fill text-sui-ink-emphasis not-data-disabled:hover:bg-sui-fill-hover not-data-disabled:hover:text-sui-ink [&_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-fill-hover 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\n// Filled buttons turn neutral when off, rather than fading their colour; ghosts, with nothing to fill, fade.\nconst ghosts: ButtonVariant[] = ['ghost', 'ghost-inverse']\nconst offClass =\n 'data-disabled:border-sui-fill-line data-disabled:bg-sui-fill data-disabled:text-sui-ink-muted data-disabled:shadow-none data-disabled:[&_svg]:stroke-sui-ink-muted'\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',\n ghosts.includes(props.variant) ? 'data-disabled:opacity-50' : offClass,\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":";;;AAqFA,IAAMM,IACJ,sK;;;;;;;;;;;;;;;;;EA9DF,IAAME,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,GAGMC,IAA0B,CAAC,SAAS,eAAe,GAInDC,IAAUC,QAAe;GAE7B;GACAF,EAAO,SAASP,EAAM,OAAO,IAAI,6BAA6BF;GAC9DE,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,GAGKU,IAAiBD,QAAeT,EAAM,OAAO,YAAY,CAACA,EAAM,OAAO;E,kBAI3ER,EAAA,GAAAC,EAUYC,EAAAoB,CAAA,GAAA;GATT,iBAAeb,EAAA,YAAQ,CAAKS,EAAA,QAAc,SAAY,KAAA;GACtD,gBAAcT,EAAA,YAAY,KAAA,IAAY,KAAA,IAAY,OAAOA,EAAA,OAAO;GAChE,IAAIA,EAAA;GACJ,YAAUA,EAAA;GACV,OAAKN,EAAEa,EAAA,KAAO;GACd,iBAAeP,EAAA,WAAQ,KAAQ,KAAA;GAC/B,UAAUS,EAAA,QAAiBT,EAAA,WAAW,KAAA;;GAEvC,SAAAL,QAAQ,CAARC,EAAQqB,EAAA,QAAA,SAAA,C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Calendar.js","names":["_sfc_main","Calendar_vue_vue_type_script_setup_true_lang_default","Calendar_default"],"sources":["../../../src/components/Calendar/Calendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport {\n addDays,\n addMonths,\n differenceInDays,\n isSameDay,\n isSameMonth,\n startOfDay,\n startOfMonth,\n startOfWeek\n} from '../../utils/dates'\nimport Tooltip from '../Tooltip/Tooltip.vue'\nimport ChevronIcon from '../internal/ChevronIcon.vue'\n\nexport interface CalendarProps {\n /** Selects a day; with `end`, a range. */\n start?: Date | null\n end?: Date | null\n /** For the month's name and the weekdays; the browser's language by default. */\n locale?: string\n /** Monday first; the locale's narrow names by default. */\n weekdays?: string[]\n previousLabel?: string\n nextLabel?: string\n}\n\nconst props = withDefaults(defineProps<CalendarProps>(), {\n start: null,\n end: null,\n locale: undefined,\n weekdays: undefined,\n previousLabel: 'Previous month',\n nextLabel: 'Next month'\n})\n\nconst emit = defineEmits<{ pick: [date: Date] }>()\n\nconst view = ref(startOfMonth(props.end ?? props.start ?? new Date()))\n\n// Follows the selection to its month, when it's picked elsewhere (a shortcut, say).\nwatch(\n () => props.end ?? props.start,\n (date) => {\n if (date) view.value = startOfMonth(date)\n }\n)\n\nconst title = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(view.value)\n)\n\n// 1 January 2024 was a Monday.\nconst dayNames = computed(\n () =>\n props.weekdays ??\n Array.from({ length: 7 }, (_, i) =>\n new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' }).format(new Date(2024, 0, 1 + i))\n )\n)\n\nconst shiftMonth = (months: number) => (view.value = addMonths(view.value, months))\n\nconst cells = computed(() => {\n const first = startOfWeek(view.value)\n const last = addDays(addMonths(view.value, 1), -1)\n const count = Math.ceil((differenceInDays(last, first) + 1) / 7) * 7\n const today = startOfDay(new Date())\n const [start, end] = [props.start, props.end].map((date) => (date ? startOfDay(date) : null))\n const ranged = !!start && !!end && !isSameDay(start, end)\n const [low, high] = ranged ? [start!, end!].sort((a, b) => a.getTime() - b.getTime()) : []\n\n return Array.from({ length: count }, (_, i) => {\n const date = addDays(first, i)\n const isStart = !!start && isSameDay(date, start)\n const isEnd = !!end && isSameDay(date, end)\n const selected = isStart || isEnd\n const inRange = ranged && date >= low! && date <= high!\n const isToday = isSameDay(date, today)\n const column = i % 7\n\n return {\n key: date.toISOString(),\n date,\n label: date.getDate(),\n selected,\n isToday,\n // The range is a band behind the days, rounded where it starts, ends or wraps.\n band: inRange\n ? ['bg-sui-accent-wash', (isStart || column === 0) && 'rounded-l-lg', (isEnd || column === 6) && 'rounded-r-lg']\n : '',\n classes: selected\n ? 'bg-sui-accent-solid font-semibold text-sui-on-accent'\n : [\n 'hover:bg-sui-
|
|
1
|
+
{"version":3,"file":"Calendar.js","names":["_sfc_main","Calendar_vue_vue_type_script_setup_true_lang_default","Calendar_default"],"sources":["../../../src/components/Calendar/Calendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport {\n addDays,\n addMonths,\n differenceInDays,\n isSameDay,\n isSameMonth,\n startOfDay,\n startOfMonth,\n startOfWeek\n} from '../../utils/dates'\nimport Tooltip from '../Tooltip/Tooltip.vue'\nimport ChevronIcon from '../internal/ChevronIcon.vue'\n\nexport interface CalendarProps {\n /** Selects a day; with `end`, a range. */\n start?: Date | null\n end?: Date | null\n /** For the month's name and the weekdays; the browser's language by default. */\n locale?: string\n /** Monday first; the locale's narrow names by default. */\n weekdays?: string[]\n previousLabel?: string\n nextLabel?: string\n}\n\nconst props = withDefaults(defineProps<CalendarProps>(), {\n start: null,\n end: null,\n locale: undefined,\n weekdays: undefined,\n previousLabel: 'Previous month',\n nextLabel: 'Next month'\n})\n\nconst emit = defineEmits<{ pick: [date: Date] }>()\n\nconst view = ref(startOfMonth(props.end ?? props.start ?? new Date()))\n\n// Follows the selection to its month, when it's picked elsewhere (a shortcut, say).\nwatch(\n () => props.end ?? props.start,\n (date) => {\n if (date) view.value = startOfMonth(date)\n }\n)\n\nconst title = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(view.value)\n)\n\n// 1 January 2024 was a Monday.\nconst dayNames = computed(\n () =>\n props.weekdays ??\n Array.from({ length: 7 }, (_, i) =>\n new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' }).format(new Date(2024, 0, 1 + i))\n )\n)\n\nconst shiftMonth = (months: number) => (view.value = addMonths(view.value, months))\n\nconst cells = computed(() => {\n const first = startOfWeek(view.value)\n const last = addDays(addMonths(view.value, 1), -1)\n const count = Math.ceil((differenceInDays(last, first) + 1) / 7) * 7\n const today = startOfDay(new Date())\n const [start, end] = [props.start, props.end].map((date) => (date ? startOfDay(date) : null))\n const ranged = !!start && !!end && !isSameDay(start, end)\n const [low, high] = ranged ? [start!, end!].sort((a, b) => a.getTime() - b.getTime()) : []\n\n return Array.from({ length: count }, (_, i) => {\n const date = addDays(first, i)\n const isStart = !!start && isSameDay(date, start)\n const isEnd = !!end && isSameDay(date, end)\n const selected = isStart || isEnd\n const inRange = ranged && date >= low! && date <= high!\n const isToday = isSameDay(date, today)\n const column = i % 7\n\n return {\n key: date.toISOString(),\n date,\n label: date.getDate(),\n selected,\n isToday,\n // The range is a band behind the days, rounded where it starts, ends or wraps.\n band: inRange\n ? ['bg-sui-accent-wash', (isStart || column === 0) && 'rounded-l-lg', (isEnd || column === 6) && 'rounded-r-lg']\n : '',\n classes: selected\n ? 'bg-sui-accent-solid font-semibold text-sui-on-accent'\n : [\n 'hover:bg-sui-fill-hover',\n isToday\n ? 'font-semibold text-sui-accent ring-1 ring-sui-accent-line ring-inset'\n : inRange\n ? 'font-medium text-sui-accent'\n : !isSameMonth(date, view.value) || date > today\n ? 'text-sui-ink-muted'\n : 'text-sui-ink'\n ]\n }\n })\n})\n</script>\n\n<!-- A month of days, weeks from Monday: today outlined with a dot, future days and other months muted. Picking a day\n emits it; the host decides what it selects. -->\n<template>\n <div>\n <div class=\"flex h-11 items-center justify-between\">\n <span class=\"pl-1.5 text-13 font-semibold text-sui-ink\">{{ title }}</span>\n <div class=\"flex gap-0.5\">\n <Tooltip :text=\"previousLabel\">\n <button\n :aria-label=\"previousLabel\"\n class=\"flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-fill-hover focus-visible:sui-ring\"\n type=\"button\"\n @click=\"shiftMonth(-1)\"\n >\n <ChevronIcon class=\"h-4 w-4 fill-sui-ink-body stroke-sui-ink-body\" direction=\"left\" />\n </button>\n </Tooltip>\n <Tooltip :text=\"nextLabel\">\n <button\n :aria-label=\"nextLabel\"\n class=\"flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-fill-hover focus-visible:sui-ring\"\n type=\"button\"\n @click=\"shiftMonth(1)\"\n >\n <ChevronIcon class=\"h-4 w-4 fill-sui-ink-body stroke-sui-ink-body\" direction=\"right\" />\n </button>\n </Tooltip>\n </div>\n </div>\n <div aria-hidden=\"true\" class=\"grid h-5.5 grid-cols-7 items-center\">\n <span v-for=\"(day, i) in dayNames\" :key=\"i\" class=\"text-center text-11 font-semibold text-sui-ink-muted\">\n {{ day }}\n </span>\n </div>\n <div class=\"mt-0.5 grid grid-cols-7 gap-y-0.5\">\n <button\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.date.toLocaleDateString(locale, { dateStyle: 'full' })\"\n :aria-pressed=\"cell.selected\"\n :class=\"['group flex h-9 items-center justify-center outline-hidden', cell.band]\"\n type=\"button\"\n @click=\"emit('pick', cell.date)\"\n >\n <span\n class=\"relative flex h-8.5 w-9 items-center justify-center rounded-lg text-13 tabular-nums transition-colors group-focus-visible:sui-ring\"\n :class=\"cell.classes\"\n >\n {{ cell.label }}\n <span\n v-if=\"cell.isToday\"\n class=\"absolute bottom-1 left-1/2 h-1 w-1 -translate-x-1/2 rounded-full\"\n :class=\"cell.selected ? 'bg-sui-on-accent' : 'bg-sui-accent-solid'\"\n />\n </span>\n </button>\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -45,7 +45,7 @@ var T = { class: "flex h-11 items-center justify-between" }, E = { class: "pl-1.
|
|
|
45
45
|
(s || v === 0) && "rounded-l-lg",
|
|
46
46
|
(c || v === 6) && "rounded-r-lg"
|
|
47
47
|
] : "",
|
|
48
|
-
classes: l ? "bg-sui-accent-solid font-semibold text-sui-on-accent" : ["hover:bg-sui-
|
|
48
|
+
classes: l ? "bg-sui-accent-solid font-semibold text-sui-on-accent" : ["hover:bg-sui-fill-hover", _ ? "font-semibold text-sui-accent ring-1 ring-sui-accent-line ring-inset" : g ? "font-medium text-sui-accent" : !a(o, I.value) || o > u ? "text-sui-ink-muted" : "text-sui-ink"]
|
|
49
49
|
};
|
|
50
50
|
});
|
|
51
51
|
});
|
|
@@ -53,7 +53,7 @@ var T = { class: "flex h-11 items-center justify-between" }, E = { class: "pl-1.
|
|
|
53
53
|
m("div", T, [m("span", E, S(L.value), 1), m("div", D, [g(e, { text: _.previousLabel }, {
|
|
54
54
|
default: w(() => [m("button", {
|
|
55
55
|
"aria-label": _.previousLabel,
|
|
56
|
-
class: "flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-
|
|
56
|
+
class: "flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-fill-hover focus-visible:sui-ring",
|
|
57
57
|
type: "button",
|
|
58
58
|
onClick: n[0] ||= (e) => z(-1)
|
|
59
59
|
}, [g(l, {
|
|
@@ -64,7 +64,7 @@ var T = { class: "flex h-11 items-center justify-between" }, E = { class: "pl-1.
|
|
|
64
64
|
}, 8, ["text"]), g(e, { text: _.nextLabel }, {
|
|
65
65
|
default: w(() => [m("button", {
|
|
66
66
|
"aria-label": _.nextLabel,
|
|
67
|
-
class: "flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-
|
|
67
|
+
class: "flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-fill-hover focus-visible:sui-ring",
|
|
68
68
|
type: "button",
|
|
69
69
|
onClick: n[1] ||= (e) => z(1)
|
|
70
70
|
}, [g(l, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Calendar.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","Tooltip","_withCtx","ChevronIcon","_Fragment","_renderList","_normalizeClass","_createTextVNode","_createCommentVNode","Tooltip_default","ChevronIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","Calendar_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","view","ref","startOfMonth","date","title","computed","dayNames","i","shiftMonth","addMonths","months","cells","first","startOfWeek","last","addDays","count","differenceInDays","today","startOfDay","start","end","ranged","isSameDay","low","high","a","b","isStart","isEnd","selected","inRange","isToday","column","isSameMonth","openBlock","createElementBlock","createElementVNode","toDisplayString","createVNode","withCtx","_cache","Fragment","renderList","day","cell","normalizeClass","createTextVNode","createCommentVNode"],"sources":["../../../src/components/Calendar/Calendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport {\n addDays,\n addMonths,\n differenceInDays,\n isSameDay,\n isSameMonth,\n startOfDay,\n startOfMonth,\n startOfWeek\n} from '../../utils/dates'\nimport Tooltip from '../Tooltip/Tooltip.vue'\nimport ChevronIcon from '../internal/ChevronIcon.vue'\n\nexport interface CalendarProps {\n /** Selects a day; with `end`, a range. */\n start?: Date | null\n end?: Date | null\n /** For the month's name and the weekdays; the browser's language by default. */\n locale?: string\n /** Monday first; the locale's narrow names by default. */\n weekdays?: string[]\n previousLabel?: string\n nextLabel?: string\n}\n\nconst props = withDefaults(defineProps<CalendarProps>(), {\n start: null,\n end: null,\n locale: undefined,\n weekdays: undefined,\n previousLabel: 'Previous month',\n nextLabel: 'Next month'\n})\n\nconst emit = defineEmits<{ pick: [date: Date] }>()\n\nconst view = ref(startOfMonth(props.end ?? props.start ?? new Date()))\n\n// Follows the selection to its month, when it's picked elsewhere (a shortcut, say).\nwatch(\n () => props.end ?? props.start,\n (date) => {\n if (date) view.value = startOfMonth(date)\n }\n)\n\nconst title = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(view.value)\n)\n\n// 1 January 2024 was a Monday.\nconst dayNames = computed(\n () =>\n props.weekdays ??\n Array.from({ length: 7 }, (_, i) =>\n new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' }).format(new Date(2024, 0, 1 + i))\n )\n)\n\nconst shiftMonth = (months: number) => (view.value = addMonths(view.value, months))\n\nconst cells = computed(() => {\n const first = startOfWeek(view.value)\n const last = addDays(addMonths(view.value, 1), -1)\n const count = Math.ceil((differenceInDays(last, first) + 1) / 7) * 7\n const today = startOfDay(new Date())\n const [start, end] = [props.start, props.end].map((date) => (date ? startOfDay(date) : null))\n const ranged = !!start && !!end && !isSameDay(start, end)\n const [low, high] = ranged ? [start!, end!].sort((a, b) => a.getTime() - b.getTime()) : []\n\n return Array.from({ length: count }, (_, i) => {\n const date = addDays(first, i)\n const isStart = !!start && isSameDay(date, start)\n const isEnd = !!end && isSameDay(date, end)\n const selected = isStart || isEnd\n const inRange = ranged && date >= low! && date <= high!\n const isToday = isSameDay(date, today)\n const column = i % 7\n\n return {\n key: date.toISOString(),\n date,\n label: date.getDate(),\n selected,\n isToday,\n // The range is a band behind the days, rounded where it starts, ends or wraps.\n band: inRange\n ? ['bg-sui-accent-wash', (isStart || column === 0) && 'rounded-l-lg', (isEnd || column === 6) && 'rounded-r-lg']\n : '',\n classes: selected\n ? 'bg-sui-accent-solid font-semibold text-sui-on-accent'\n : [\n 'hover:bg-sui-sunken',\n isToday\n ? 'font-semibold text-sui-accent ring-1 ring-sui-accent-line ring-inset'\n : inRange\n ? 'font-medium text-sui-accent'\n : !isSameMonth(date, view.value) || date > today\n ? 'text-sui-ink-muted'\n : 'text-sui-ink'\n ]\n }\n })\n})\n</script>\n\n<!-- A month of days, weeks from Monday: today outlined with a dot, future days and other months muted. Picking a day\n emits it; the host decides what it selects. -->\n<template>\n <div>\n <div class=\"flex h-11 items-center justify-between\">\n <span class=\"pl-1.5 text-13 font-semibold text-sui-ink\">{{ title }}</span>\n <div class=\"flex gap-0.5\">\n <Tooltip :text=\"previousLabel\">\n <button\n :aria-label=\"previousLabel\"\n class=\"flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-sunken focus-visible:sui-ring\"\n type=\"button\"\n @click=\"shiftMonth(-1)\"\n >\n <ChevronIcon class=\"h-4 w-4 fill-sui-ink-body stroke-sui-ink-body\" direction=\"left\" />\n </button>\n </Tooltip>\n <Tooltip :text=\"nextLabel\">\n <button\n :aria-label=\"nextLabel\"\n class=\"flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-sunken focus-visible:sui-ring\"\n type=\"button\"\n @click=\"shiftMonth(1)\"\n >\n <ChevronIcon class=\"h-4 w-4 fill-sui-ink-body stroke-sui-ink-body\" direction=\"right\" />\n </button>\n </Tooltip>\n </div>\n </div>\n <div aria-hidden=\"true\" class=\"grid h-5.5 grid-cols-7 items-center\">\n <span v-for=\"(day, i) in dayNames\" :key=\"i\" class=\"text-center text-11 font-semibold text-sui-ink-muted\">\n {{ day }}\n </span>\n </div>\n <div class=\"mt-0.5 grid grid-cols-7 gap-y-0.5\">\n <button\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.date.toLocaleDateString(locale, { dateStyle: 'full' })\"\n :aria-pressed=\"cell.selected\"\n :class=\"['group flex h-9 items-center justify-center outline-hidden', cell.band]\"\n type=\"button\"\n @click=\"emit('pick', cell.date)\"\n >\n <span\n class=\"relative flex h-8.5 w-9 items-center justify-center rounded-lg text-13 tabular-nums transition-colors group-focus-visible:sui-ring\"\n :class=\"cell.classes\"\n >\n {{ cell.label }}\n <span\n v-if=\"cell.isToday\"\n class=\"absolute bottom-1 left-1/2 h-1 w-1 -translate-x-1/2 rounded-full\"\n :class=\"cell.selected ? 'bg-sui-on-accent' : 'bg-sui-accent-solid'\"\n />\n </span>\n </button>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Calendar.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_createElementVNode","_toDisplayString","_createVNode","Tooltip","_withCtx","ChevronIcon","_Fragment","_renderList","_normalizeClass","_createTextVNode","_createCommentVNode","Tooltip_default","ChevronIcon_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","Calendar_vue_vue_type_script_setup_true_lang_default","__emit","props","__props","emit","view","ref","startOfMonth","date","title","computed","dayNames","i","shiftMonth","addMonths","months","cells","first","startOfWeek","last","addDays","count","differenceInDays","today","startOfDay","start","end","ranged","isSameDay","low","high","a","b","isStart","isEnd","selected","inRange","isToday","column","isSameMonth","openBlock","createElementBlock","createElementVNode","toDisplayString","createVNode","withCtx","_cache","Fragment","renderList","day","cell","normalizeClass","createTextVNode","createCommentVNode"],"sources":["../../../src/components/Calendar/Calendar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport {\n addDays,\n addMonths,\n differenceInDays,\n isSameDay,\n isSameMonth,\n startOfDay,\n startOfMonth,\n startOfWeek\n} from '../../utils/dates'\nimport Tooltip from '../Tooltip/Tooltip.vue'\nimport ChevronIcon from '../internal/ChevronIcon.vue'\n\nexport interface CalendarProps {\n /** Selects a day; with `end`, a range. */\n start?: Date | null\n end?: Date | null\n /** For the month's name and the weekdays; the browser's language by default. */\n locale?: string\n /** Monday first; the locale's narrow names by default. */\n weekdays?: string[]\n previousLabel?: string\n nextLabel?: string\n}\n\nconst props = withDefaults(defineProps<CalendarProps>(), {\n start: null,\n end: null,\n locale: undefined,\n weekdays: undefined,\n previousLabel: 'Previous month',\n nextLabel: 'Next month'\n})\n\nconst emit = defineEmits<{ pick: [date: Date] }>()\n\nconst view = ref(startOfMonth(props.end ?? props.start ?? new Date()))\n\n// Follows the selection to its month, when it's picked elsewhere (a shortcut, say).\nwatch(\n () => props.end ?? props.start,\n (date) => {\n if (date) view.value = startOfMonth(date)\n }\n)\n\nconst title = computed(() =>\n new Intl.DateTimeFormat(props.locale, { month: 'long', year: 'numeric' }).format(view.value)\n)\n\n// 1 January 2024 was a Monday.\nconst dayNames = computed(\n () =>\n props.weekdays ??\n Array.from({ length: 7 }, (_, i) =>\n new Intl.DateTimeFormat(props.locale, { weekday: 'narrow' }).format(new Date(2024, 0, 1 + i))\n )\n)\n\nconst shiftMonth = (months: number) => (view.value = addMonths(view.value, months))\n\nconst cells = computed(() => {\n const first = startOfWeek(view.value)\n const last = addDays(addMonths(view.value, 1), -1)\n const count = Math.ceil((differenceInDays(last, first) + 1) / 7) * 7\n const today = startOfDay(new Date())\n const [start, end] = [props.start, props.end].map((date) => (date ? startOfDay(date) : null))\n const ranged = !!start && !!end && !isSameDay(start, end)\n const [low, high] = ranged ? [start!, end!].sort((a, b) => a.getTime() - b.getTime()) : []\n\n return Array.from({ length: count }, (_, i) => {\n const date = addDays(first, i)\n const isStart = !!start && isSameDay(date, start)\n const isEnd = !!end && isSameDay(date, end)\n const selected = isStart || isEnd\n const inRange = ranged && date >= low! && date <= high!\n const isToday = isSameDay(date, today)\n const column = i % 7\n\n return {\n key: date.toISOString(),\n date,\n label: date.getDate(),\n selected,\n isToday,\n // The range is a band behind the days, rounded where it starts, ends or wraps.\n band: inRange\n ? ['bg-sui-accent-wash', (isStart || column === 0) && 'rounded-l-lg', (isEnd || column === 6) && 'rounded-r-lg']\n : '',\n classes: selected\n ? 'bg-sui-accent-solid font-semibold text-sui-on-accent'\n : [\n 'hover:bg-sui-fill-hover',\n isToday\n ? 'font-semibold text-sui-accent ring-1 ring-sui-accent-line ring-inset'\n : inRange\n ? 'font-medium text-sui-accent'\n : !isSameMonth(date, view.value) || date > today\n ? 'text-sui-ink-muted'\n : 'text-sui-ink'\n ]\n }\n })\n})\n</script>\n\n<!-- A month of days, weeks from Monday: today outlined with a dot, future days and other months muted. Picking a day\n emits it; the host decides what it selects. -->\n<template>\n <div>\n <div class=\"flex h-11 items-center justify-between\">\n <span class=\"pl-1.5 text-13 font-semibold text-sui-ink\">{{ title }}</span>\n <div class=\"flex gap-0.5\">\n <Tooltip :text=\"previousLabel\">\n <button\n :aria-label=\"previousLabel\"\n class=\"flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-fill-hover focus-visible:sui-ring\"\n type=\"button\"\n @click=\"shiftMonth(-1)\"\n >\n <ChevronIcon class=\"h-4 w-4 fill-sui-ink-body stroke-sui-ink-body\" direction=\"left\" />\n </button>\n </Tooltip>\n <Tooltip :text=\"nextLabel\">\n <button\n :aria-label=\"nextLabel\"\n class=\"flex h-7 w-7 items-center justify-center rounded-lg transition-colors hover:bg-sui-fill-hover focus-visible:sui-ring\"\n type=\"button\"\n @click=\"shiftMonth(1)\"\n >\n <ChevronIcon class=\"h-4 w-4 fill-sui-ink-body stroke-sui-ink-body\" direction=\"right\" />\n </button>\n </Tooltip>\n </div>\n </div>\n <div aria-hidden=\"true\" class=\"grid h-5.5 grid-cols-7 items-center\">\n <span v-for=\"(day, i) in dayNames\" :key=\"i\" class=\"text-center text-11 font-semibold text-sui-ink-muted\">\n {{ day }}\n </span>\n </div>\n <div class=\"mt-0.5 grid grid-cols-7 gap-y-0.5\">\n <button\n v-for=\"cell in cells\"\n :key=\"cell.key\"\n :aria-current=\"cell.isToday ? 'date' : undefined\"\n :aria-label=\"cell.date.toLocaleDateString(locale, { dateStyle: 'full' })\"\n :aria-pressed=\"cell.selected\"\n :class=\"['group flex h-9 items-center justify-center outline-hidden', cell.band]\"\n type=\"button\"\n @click=\"emit('pick', cell.date)\"\n >\n <span\n class=\"relative flex h-8.5 w-9 items-center justify-center rounded-lg text-13 tabular-nums transition-colors group-focus-visible:sui-ring\"\n :class=\"cell.classes\"\n >\n {{ cell.label }}\n <span\n v-if=\"cell.isToday\"\n class=\"absolute bottom-1 left-1/2 h-1 w-1 -translate-x-1/2 rounded-full\"\n :class=\"cell.selected ? 'bg-sui-on-accent' : 'bg-sui-accent-solid'\"\n />\n </span>\n </button>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","names":["_sfc_main","Checkbox_vue_vue_type_script_setup_true_lang_default","Checkbox_default"],"sources":["../../../src/components/Checkbox/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nexport interface CheckboxProps {\n invalid?: boolean\n disabled?: boolean\n /** Some but not all of a group are checked, e.g. a table's select-all. Shows a dash; checking clears it. */\n indeterminate?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<CheckboxProps>()\n\nconst model = defineModel<boolean>({ default: false })\n\n// Class and style size the control; everything else (id, name, aria-label) belongs on the input.\nconst attrs = useAttrs()\nconst inputAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs\n return rest\n})\n\nconst filled = computed(() => model.value || props.indeterminate)\n\nconst box = computed(() => [\n filled.value\n ? 'border-sui-accent-solid bg-sui-accent-solid'\n : props.invalid\n ? 'border-sui-danger-mid'\n : 'border-sui-line-control bg-
|
|
1
|
+
{"version":3,"file":"Checkbox.js","names":["_sfc_main","Checkbox_vue_vue_type_script_setup_true_lang_default","Checkbox_default"],"sources":["../../../src/components/Checkbox/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nexport interface CheckboxProps {\n invalid?: boolean\n disabled?: boolean\n /** Some but not all of a group are checked, e.g. a table's select-all. Shows a dash; checking clears it. */\n indeterminate?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<CheckboxProps>()\n\nconst model = defineModel<boolean>({ default: false })\n\n// Class and style size the control; everything else (id, name, aria-label) belongs on the input.\nconst attrs = useAttrs()\nconst inputAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs\n return rest\n})\n\nconst filled = computed(() => model.value || props.indeterminate)\n\nconst box = computed(() => [\n filled.value\n ? 'border-sui-accent-solid bg-sui-accent-solid'\n : props.invalid\n ? 'border-sui-danger-mid'\n : 'border-sui-line-control bg-transparent',\n props.disabled\n ? 'cursor-default opacity-60'\n : [!filled.value && !props.invalid && 'peer-hover:border-sui-accent-solid', 'peer-focus-visible:sui-ring']\n])\n</script>\n\n<template>\n <div\n class=\"relative inline-flex h-4 w-4 cursor-pointer align-bottom text-sui-on-accent select-none\"\n :class=\"attrs.class\"\n :style=\"attrs.style as string\"\n >\n <input\n v-bind=\"inputAttrs\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n :checked=\"model\"\n class=\"peer absolute top-0 left-0 z-10 m-0 h-full w-full cursor-pointer appearance-none p-0 opacity-0 outline-hidden\"\n :disabled=\"disabled\"\n :indeterminate=\"indeterminate && !model\"\n type=\"checkbox\"\n @change=\"model = ($event.target as HTMLInputElement).checked\"\n />\n <div class=\"flex h-4 w-4 items-center justify-center rounded border transition-colors duration-200\" :class=\"box\">\n <svg\n v-if=\"indeterminate && !model\"\n aria-hidden=\"true\"\n class=\"size-2.5 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-width=\"2\"\n viewBox=\"0 0 10 10\"\n >\n <path d=\"M2 5h6\" />\n </svg>\n <svg\n v-else-if=\"model\"\n aria-hidden=\"true\"\n class=\"h-4 w-4 text-base leading-none text-sui-on-accent transition-all duration-200\"\n fill=\"none\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n width=\"14\"\n >\n <path\n d=\"M4.86199 11.5948C4.78717 11.5923 4.71366 11.5745 4.64596 11.5426C4.57826 11.5107 4.51779 11.4652 4.46827 11.4091L0.753985 7.69483C0.683167 7.64891 0.623706 7.58751 0.580092 7.51525C0.536478 7.44299 0.509851 7.36177 0.502221 7.27771C0.49459 7.19366 0.506156 7.10897 0.536046 7.03004C0.565935 6.95111 0.613367 6.88 0.674759 6.82208C0.736151 6.76416 0.8099 6.72095 0.890436 6.69571C0.970973 6.67046 1.05619 6.66385 1.13966 6.67635C1.22313 6.68886 1.30266 6.72017 1.37226 6.76792C1.44186 6.81567 1.4997 6.8786 1.54141 6.95197L4.86199 10.2503L12.6397 2.49483C12.7444 2.42694 12.8689 2.39617 12.9932 2.40745C13.1174 2.41873 13.2343 2.47141 13.3251 2.55705C13.4159 2.64268 13.4753 2.75632 13.4938 2.87973C13.5123 3.00315 13.4888 3.1292 13.4271 3.23768L5.2557 11.4091C5.20618 11.4652 5.14571 11.5107 5.07801 11.5426C5.01031 11.5745 4.9368 11.5923 4.86199 11.5948Z\"\n fill=\"currentColor\"\n />\n </svg>\n </div>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -39,7 +39,7 @@ var p = [
|
|
|
39
39
|
let a = i, g = f(i, "modelValue"), _ = d(), v = e(() => {
|
|
40
40
|
let { class: e, style: t, ...n } = _;
|
|
41
41
|
return n;
|
|
42
|
-
}), y = e(() => g.value || a.indeterminate), b = e(() => [y.value ? "border-sui-accent-solid bg-sui-accent-solid" : a.invalid ? "border-sui-danger-mid" : "border-sui-line-control bg-
|
|
42
|
+
}), y = e(() => g.value || a.indeterminate), b = e(() => [y.value ? "border-sui-accent-solid bg-sui-accent-solid" : a.invalid ? "border-sui-danger-mid" : "border-sui-line-control bg-transparent", a.disabled ? "cursor-default opacity-60" : [!y.value && !a.invalid && "peer-hover:border-sui-accent-solid", "peer-focus-visible:sui-ring"]]);
|
|
43
43
|
return (e, a) => (l(), n("div", {
|
|
44
44
|
class: s(["relative inline-flex h-4 w-4 cursor-pointer align-bottom text-sui-on-accent select-none", u(_).class]),
|
|
45
45
|
style: c(u(_).style)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createElementVNode","_mergeProps","_createCommentVNode","_hoisted_1","_hoisted_2","_hoisted_3","Checkbox_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","props","__props","model","useModel","attrs","useAttrs","inputAttrs","computed","_class","_style","rest","filled","box","openBlock","createElementBlock","normalizeClass","unref","normalizeStyle","createElementVNode","mergeProps","_cache","$event","createCommentVNode"],"sources":["../../../src/components/Checkbox/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nexport interface CheckboxProps {\n invalid?: boolean\n disabled?: boolean\n /** Some but not all of a group are checked, e.g. a table's select-all. Shows a dash; checking clears it. */\n indeterminate?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<CheckboxProps>()\n\nconst model = defineModel<boolean>({ default: false })\n\n// Class and style size the control; everything else (id, name, aria-label) belongs on the input.\nconst attrs = useAttrs()\nconst inputAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs\n return rest\n})\n\nconst filled = computed(() => model.value || props.indeterminate)\n\nconst box = computed(() => [\n filled.value\n ? 'border-sui-accent-solid bg-sui-accent-solid'\n : props.invalid\n ? 'border-sui-danger-mid'\n : 'border-sui-line-control bg-
|
|
1
|
+
{"version":3,"file":"Checkbox.vue_vue_type_script_setup_true_lang.js","names":["_useModel","_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle","_createElementVNode","_mergeProps","_createCommentVNode","_hoisted_1","_hoisted_2","_hoisted_3","Checkbox_vue_vue_type_script_setup_true_lang_default","\"modelValue\"","\"modelModifiers\"","props","__props","model","useModel","attrs","useAttrs","inputAttrs","computed","_class","_style","rest","filled","box","openBlock","createElementBlock","normalizeClass","unref","normalizeStyle","createElementVNode","mergeProps","_cache","$event","createCommentVNode"],"sources":["../../../src/components/Checkbox/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useAttrs } from 'vue'\n\nexport interface CheckboxProps {\n invalid?: boolean\n disabled?: boolean\n /** Some but not all of a group are checked, e.g. a table's select-all. Shows a dash; checking clears it. */\n indeterminate?: boolean\n}\n\ndefineOptions({ inheritAttrs: false })\n\nconst props = defineProps<CheckboxProps>()\n\nconst model = defineModel<boolean>({ default: false })\n\n// Class and style size the control; everything else (id, name, aria-label) belongs on the input.\nconst attrs = useAttrs()\nconst inputAttrs = computed(() => {\n const { class: _class, style: _style, ...rest } = attrs\n return rest\n})\n\nconst filled = computed(() => model.value || props.indeterminate)\n\nconst box = computed(() => [\n filled.value\n ? 'border-sui-accent-solid bg-sui-accent-solid'\n : props.invalid\n ? 'border-sui-danger-mid'\n : 'border-sui-line-control bg-transparent',\n props.disabled\n ? 'cursor-default opacity-60'\n : [!filled.value && !props.invalid && 'peer-hover:border-sui-accent-solid', 'peer-focus-visible:sui-ring']\n])\n</script>\n\n<template>\n <div\n class=\"relative inline-flex h-4 w-4 cursor-pointer align-bottom text-sui-on-accent select-none\"\n :class=\"attrs.class\"\n :style=\"attrs.style as string\"\n >\n <input\n v-bind=\"inputAttrs\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n :checked=\"model\"\n class=\"peer absolute top-0 left-0 z-10 m-0 h-full w-full cursor-pointer appearance-none p-0 opacity-0 outline-hidden\"\n :disabled=\"disabled\"\n :indeterminate=\"indeterminate && !model\"\n type=\"checkbox\"\n @change=\"model = ($event.target as HTMLInputElement).checked\"\n />\n <div class=\"flex h-4 w-4 items-center justify-center rounded border transition-colors duration-200\" :class=\"box\">\n <svg\n v-if=\"indeterminate && !model\"\n aria-hidden=\"true\"\n class=\"size-2.5 fill-none stroke-current\"\n stroke-linecap=\"round\"\n stroke-width=\"2\"\n viewBox=\"0 0 10 10\"\n >\n <path d=\"M2 5h6\" />\n </svg>\n <svg\n v-else-if=\"model\"\n aria-hidden=\"true\"\n class=\"h-4 w-4 text-base leading-none text-sui-on-accent transition-all duration-200\"\n fill=\"none\"\n height=\"14\"\n viewBox=\"0 0 14 14\"\n width=\"14\"\n >\n <path\n d=\"M4.86199 11.5948C4.78717 11.5923 4.71366 11.5745 4.64596 11.5426C4.57826 11.5107 4.51779 11.4652 4.46827 11.4091L0.753985 7.69483C0.683167 7.64891 0.623706 7.58751 0.580092 7.51525C0.536478 7.44299 0.509851 7.36177 0.502221 7.27771C0.49459 7.19366 0.506156 7.10897 0.536046 7.03004C0.565935 6.95111 0.613367 6.88 0.674759 6.82208C0.736151 6.76416 0.8099 6.72095 0.890436 6.69571C0.970973 6.67046 1.05619 6.66385 1.13966 6.67635C1.22313 6.68886 1.30266 6.72017 1.37226 6.76792C1.44186 6.81567 1.4997 6.8786 1.54141 6.95197L4.86199 10.2503L12.6397 2.49483C12.7444 2.42694 12.8689 2.39617 12.9932 2.40745C13.1174 2.41873 13.2343 2.47141 13.3251 2.55705C13.4159 2.64268 13.4753 2.75632 13.4938 2.87973C13.5123 3.00315 13.4888 3.1292 13.4271 3.23768L5.2557 11.4091C5.20618 11.4652 5.14571 11.5107 5.07801 11.5426C5.01031 11.5745 4.9368 11.5923 4.86199 11.5948Z\"\n fill=\"currentColor\"\n />\n </svg>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYA,IAAMe,IAAQC,GAERC,IAAQjB,EAAoBgB,GAAA,YAAmB,GAG/CG,IAAQC,EAAS,GACjBC,IAAaC,QAAe;GAChC,IAAM,EAAE,OAAOC,GAAQ,OAAOC,GAAQ,GAAGC,MAASN;GAClD,OAAOM;EACT,CAAC,GAEKC,IAASJ,QAAeL,EAAM,SAASF,EAAM,aAAa,GAE1DY,IAAML,QAAe,CACzBI,EAAO,QACH,gDACAX,EAAM,UACJ,0BACA,0CACNA,EAAM,WACF,8BACA,CAAC,CAACW,EAAO,SAAS,CAACX,EAAM,WAAW,sCAAsC,6BAA6B,CAC7G,CAAC;E,kBAICd,EAAA,GAAAC,EAyCM,OAAA;GAxCJ,OAAKC,EAAA,CAAC,2FACEC,EAAAe,CAAA,CAAK,CAAC,KAAK,CAAA;GAClB,OAAKd,EAAED,EAAAe,CAAA,CAAK,CAAC,KAAK;E,IAEnBb,EASE,SATFC,EACUc,EAQR,OARkB;GACjB,gBAAcL,EAAA,UAAO,SAAY,KAAA;GACjC,SAASC,EAAA;GACV,OAAM;GACL,UAAUD,EAAA;GACV,eAAeA,EAAA,iBAAa,CAAKC,EAAA;GAClC,MAAK;GACJ,UAAM,AAAAkB,EAAA,QAAA,MAAE,EAAA,QAASC,EAAO,OAA4B;E,kBAEvD9B,EAyBM,OAAA,EAzBD,OAAKH,EAAA,CAAC,0FAAiGwB,EAAA,KAAG,CAAA,E,IAErGX,EAAA,iBAAa,CAAKC,EAAA,SAD1BhB,EAAA,GAAAC,EASM,OATNQ,GASM,CAAA,GAAA,AAAAyB,EAAA,OAAA,CADJ7B,EAAmB,QAAA,EAAb,GAAE,SAAQ,GAAA,MAAA,EAAA,C,OAGLW,EAAA,SADbhB,EAAA,GAAAC,EAaM,OAbNS,GAaM,CAAA,GAAA,AAAAwB,EAAA,OAAA,CAJJ7B,EAGE,QAAA;GAFA,GAAE;GACF,MAAK"}
|
|
@@ -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 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 /** While loading, the placeholder groups drawn, as each one's number of rows. */\n loadingGroups?: number[]\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 loadingGroups: () => [4, 2],\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%', '38%']\nconst skeletonGroupWidths = ['5.5rem', '4rem', '4.75rem']\n// A Skeleton is sunken, so on a sunken header band its bars go a step darker.\nconst headerBar = computed(() => [\n 'animate-pulse',\n headerTone.value === tones.sunken ? 'bg-sui-line-subtle' : 'bg-sui-sunken'\n])\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 <!-- Placeholder groups in the list's own shape: its header, rows, leading marks and meta. -->\n <template v-else-if=\"loading\">\n <section\n v-for=\"(rows, g) in loadingGroups\"\n :key=\"g\"\n aria-busy=\"true\"\n :class=\"cards ? card : 'border-t border-sui-line-subtle first:border-t-0'\"\n >\n <div\n aria-hidden=\"true\"\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, collapsible ? 'pl-1.5' : 'pl-4']\"\n >\n <span v-if=\"collapsible\" class=\"size-6 shrink-0\" />\n <span\n v-if=\"$slots['group-leading'] || $slots['group-header']\"\n class=\"size-3.5 rounded-full\"\n :class=\"headerBar\"\n />\n <span class=\"h-2.5 rounded-md\" :class=\"headerBar\" :style=\"{ width: skeletonGroupWidths[g % 3] }\" />\n <span class=\"h-2.5 w-3.5 rounded-md\" :class=\"headerBar\" />\n </div>\n <div\n v-for=\"r in rows\"\n :key=\"r\"\n aria-hidden=\"true\"\n class=\"flex h-[var(--sui-list-row,2.5rem)] items-center gap-2.5 pr-4\"\n :class=\"[gutter ? 'pl-9.5' : 'pl-4', dividers && '[&+&]:border-t [&+&]:border-sui-line-subtle']\"\n >\n <Skeleton v-if=\"$slots.leading || leading\" shape=\"circle\" size=\"1.25rem\" />\n <Skeleton height=\"0.75rem\" :width=\"skeletonWidths[(g * 3 + r) % skeletonWidths.length]\" />\n <span class=\"flex-1\" />\n <template v-if=\"$slots.meta\">\n <Skeleton height=\"0.625rem\" width=\"6rem\" />\n <Skeleton height=\"0.625rem\" width=\"3.5rem\" />\n </template>\n </div>\n </section>\n </template>\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
|
+
{"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 /** While loading, the placeholder groups drawn, as each one's number of rows. */\n loadingGroups?: number[]\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 loadingGroups: () => [4, 2],\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: 'sui-band', 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%', '38%']\nconst skeletonGroupWidths = ['5.5rem', '4rem', '4.75rem']\nconst headerBar = 'animate-pulse bg-sui-fill-hover'\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-fill-hover 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 <!-- Placeholder groups in the list's own shape: its header, rows, leading marks and meta. -->\n <template v-else-if=\"loading\">\n <section\n v-for=\"(rows, g) in loadingGroups\"\n :key=\"g\"\n aria-busy=\"true\"\n :class=\"cards ? card : 'border-t border-sui-line-subtle first:border-t-0'\"\n >\n <div\n aria-hidden=\"true\"\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, collapsible ? 'pl-1.5' : 'pl-4']\"\n >\n <span v-if=\"collapsible\" class=\"size-6 shrink-0\" />\n <span\n v-if=\"$slots['group-leading'] || $slots['group-header']\"\n class=\"size-3.5 rounded-full\"\n :class=\"headerBar\"\n />\n <span class=\"h-2.5 rounded-md\" :class=\"headerBar\" :style=\"{ width: skeletonGroupWidths[g % 3] }\" />\n <span class=\"h-2.5 w-3.5 rounded-md\" :class=\"headerBar\" />\n </div>\n <div\n v-for=\"r in rows\"\n :key=\"r\"\n aria-hidden=\"true\"\n class=\"flex h-[var(--sui-list-row,2.5rem)] items-center gap-2.5 pr-4\"\n :class=\"[gutter ? 'pl-9.5' : 'pl-4', dividers && '[&+&]:border-t [&+&]:border-sui-line-subtle']\"\n >\n <Skeleton v-if=\"$slots.leading || leading\" shape=\"circle\" size=\"1.25rem\" />\n <Skeleton height=\"0.75rem\" :width=\"skeletonWidths[(g * 3 + r) % skeletonWidths.length]\" />\n <span class=\"flex-1\" />\n <template v-if=\"$slots.meta\">\n <Skeleton height=\"0.625rem\" width=\"6rem\" />\n <Skeleton height=\"0.625rem\" width=\"3.5rem\" />\n </template>\n </div>\n </section>\n </template>\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-fill-hover has-focus-visible:bg-sui-fill-hover'\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":""}
|