@vielzeug/refine 2.3.0 → 3.0.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 +5 -18
- package/dist/accordion-item.cjs +1 -1
- package/dist/accordion-item.cjs.map +1 -1
- package/dist/accordion-item.js +1 -1
- package/dist/accordion-item.js.map +1 -1
- package/dist/accordion.cjs.map +1 -1
- package/dist/accordion.js.map +1 -1
- package/dist/async.cjs +1 -1
- package/dist/async.cjs.map +1 -1
- package/dist/async.js +3 -3
- package/dist/async.js.map +1 -1
- package/dist/box.cjs.map +1 -1
- package/dist/box.js.map +1 -1
- package/dist/card.cjs +1 -1
- package/dist/card.cjs.map +1 -1
- package/dist/card.js +3 -3
- package/dist/card.js.map +1 -1
- package/dist/chat-message.cjs +1 -1
- package/dist/chat-message.cjs.map +1 -1
- package/dist/chat-message.js +1 -1
- package/dist/chat-message.js.map +1 -1
- package/dist/checkbox-group.cjs +1 -1
- package/dist/checkbox-group.cjs.map +1 -1
- package/dist/checkbox-group.js +1 -1
- package/dist/checkbox-group.js.map +1 -1
- package/dist/combobox.cjs +10 -8
- package/dist/combobox.cjs.map +1 -1
- package/dist/combobox.js +62 -51
- package/dist/combobox.js.map +1 -1
- package/dist/command-palette.cjs +1 -1
- package/dist/command-palette.cjs.map +1 -1
- package/dist/command-palette.js +6 -2
- package/dist/command-palette.js.map +1 -1
- package/dist/content/card/card.css.cjs +1 -1
- package/dist/content/card/card.css.cjs.map +1 -1
- package/dist/content/card/card.css.js +1 -1
- package/dist/content/card/card.css.js.map +1 -1
- package/dist/content/card/card.d.ts +2 -2
- package/dist/content/card/card.d.ts.map +1 -1
- package/dist/content/index.d.ts +2 -0
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/list/list-item.css.cjs +1 -1
- package/dist/content/list/list-item.css.cjs.map +1 -1
- package/dist/content/list/list-item.css.js +1 -1
- package/dist/content/list/list-item.css.js.map +1 -1
- package/dist/content/list/list-item.d.ts +9 -0
- package/dist/content/list/list-item.d.ts.map +1 -1
- package/dist/content/pagination/pagination.css.cjs +1 -1
- package/dist/content/pagination/pagination.css.cjs.map +1 -1
- package/dist/content/pagination/pagination.css.js +1 -1
- package/dist/content/pagination/pagination.css.js.map +1 -1
- package/dist/content/stats/stats.css.cjs +2 -0
- package/dist/content/stats/stats.css.cjs.map +1 -0
- package/dist/content/stats/stats.css.js +6 -0
- package/dist/content/stats/stats.css.js.map +1 -0
- package/dist/content/stats/stats.d.ts +72 -0
- package/dist/content/stats/stats.d.ts.map +1 -0
- package/dist/core/auto-resize.cjs +1 -1
- package/dist/core/auto-resize.cjs.map +1 -1
- package/dist/core/auto-resize.js +1 -1
- package/dist/core/auto-resize.js.map +1 -1
- package/dist/core/field-base.cjs +1 -1
- package/dist/core/field-base.cjs.map +1 -1
- package/dist/core/field-base.d.ts +1 -1
- package/dist/core/field-base.js +1 -1
- package/dist/core/field-base.js.map +1 -1
- package/dist/core/nav.cjs +1 -1
- package/dist/core/nav.cjs.map +1 -1
- package/dist/core/nav.d.ts +4 -1
- package/dist/core/nav.d.ts.map +1 -1
- package/dist/core/nav.js +41 -28
- package/dist/core/nav.js.map +1 -1
- package/dist/core/option-list.cjs +1 -1
- package/dist/core/option-list.cjs.map +1 -1
- package/dist/core/option-list.d.ts.map +1 -1
- package/dist/core/option-list.js +30 -28
- package/dist/core/option-list.js.map +1 -1
- package/dist/core/slider.cjs +1 -1
- package/dist/core/slider.cjs.map +1 -1
- package/dist/core/slider.js +1 -1
- package/dist/core/slider.js.map +1 -1
- package/dist/core/spinner.cjs +1 -1
- package/dist/core/spinner.cjs.map +1 -1
- package/dist/core/spinner.js +1 -1
- package/dist/core/spinner.js.map +1 -1
- package/dist/custom-elements.json +5471 -4977
- package/dist/datagrid.cjs +219 -199
- package/dist/datagrid.cjs.map +1 -1
- package/dist/datagrid.js +415 -302
- package/dist/datagrid.js.map +1 -1
- package/dist/disclosure/accordion/accordion.css.cjs +1 -1
- package/dist/disclosure/accordion/accordion.css.cjs.map +1 -1
- package/dist/disclosure/accordion/accordion.css.js +1 -1
- package/dist/disclosure/accordion/accordion.css.js.map +1 -1
- package/dist/disclosure/accordion/accordion.d.ts +5 -5
- package/dist/disclosure/accordion/accordion.d.ts.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.cjs.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js +1 -1
- package/dist/disclosure/accordion-item/accordion-item.css.js.map +1 -1
- package/dist/disclosure/accordion-item/accordion-item.d.ts +3 -3
- package/dist/disclosure/accordion-item/accordion-item.d.ts.map +1 -1
- package/dist/disclosure/tab-item/tab-item.css.cjs +1 -1
- package/dist/disclosure/tab-item/tab-item.css.cjs.map +1 -1
- package/dist/disclosure/tab-item/tab-item.css.js +1 -1
- package/dist/disclosure/tab-item/tab-item.css.js.map +1 -1
- package/dist/disclosure/tab-item/tab-item.d.ts +6 -3
- package/dist/disclosure/tab-item/tab-item.d.ts.map +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs +1 -1
- package/dist/disclosure/tabs/tabs.css.cjs.map +1 -1
- package/dist/disclosure/tabs/tabs.css.js +1 -1
- package/dist/disclosure/tabs/tabs.css.js.map +1 -1
- package/dist/disclosure/tabs/tabs.d.ts +9 -5
- package/dist/disclosure/tabs/tabs.d.ts.map +1 -1
- package/dist/drawer.cjs +1 -1
- package/dist/drawer.cjs.map +1 -1
- package/dist/drawer.js +1 -1
- package/dist/drawer.js.map +1 -1
- package/dist/feedback/toast/toast.d.ts.map +1 -1
- package/dist/file-input.cjs +1 -1
- package/dist/file-input.cjs.map +1 -1
- package/dist/file-input.js +2 -2
- package/dist/file-input.js.map +1 -1
- package/dist/frameworks/elements.d.ts +4 -85
- package/dist/frameworks/react.d.ts +5 -4
- package/dist/frameworks/vue.d.ts +4 -4
- package/dist/grid.cjs +2 -2
- package/dist/grid.cjs.map +1 -1
- package/dist/grid.js +21 -18
- package/dist/grid.js.map +1 -1
- package/dist/icon.cjs +1 -1
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +1 -1
- package/dist/icon.js.map +1 -1
- package/dist/input.cjs +5 -4
- package/dist/input.cjs.map +1 -1
- package/dist/input.js +14 -8
- package/dist/input.js.map +1 -1
- package/dist/inputs/combobox/combobox-options.cjs +1 -1
- package/dist/inputs/combobox/combobox-options.cjs.map +1 -1
- package/dist/inputs/combobox/combobox-options.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox-options.js +1 -0
- package/dist/inputs/combobox/combobox-options.js.map +1 -1
- package/dist/inputs/combobox/combobox.css.cjs +1 -1
- package/dist/inputs/combobox/combobox.css.cjs.map +1 -1
- package/dist/inputs/combobox/combobox.css.js +1 -1
- package/dist/inputs/combobox/combobox.css.js.map +1 -1
- package/dist/inputs/combobox/combobox.d.ts +13 -1
- package/dist/inputs/combobox/combobox.d.ts.map +1 -1
- package/dist/inputs/combobox/combobox.types.d.ts +7 -0
- package/dist/inputs/combobox/combobox.types.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs +1 -1
- package/dist/inputs/datagrid/datagrid-model.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.d.ts +17 -3
- package/dist/inputs/datagrid/datagrid-model.d.ts.map +1 -1
- package/dist/inputs/datagrid/datagrid-model.js +93 -70
- package/dist/inputs/datagrid/datagrid-model.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.css.cjs +1 -1
- package/dist/inputs/datagrid/datagrid.css.cjs.map +1 -1
- package/dist/inputs/datagrid/datagrid.css.js +1 -1
- package/dist/inputs/datagrid/datagrid.css.js.map +1 -1
- package/dist/inputs/datagrid/datagrid.d.ts +78 -36
- package/dist/inputs/datagrid/datagrid.d.ts.map +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs +1 -1
- package/dist/inputs/file-input/file-input-upload.cjs.map +1 -1
- package/dist/inputs/file-input/file-input-upload.js +1 -1
- package/dist/inputs/file-input/file-input-upload.js.map +1 -1
- package/dist/inputs/index.d.ts +3 -4
- package/dist/inputs/index.d.ts.map +1 -1
- package/dist/inputs/input/input.css.cjs +1 -1
- package/dist/inputs/input/input.css.cjs.map +1 -1
- package/dist/inputs/input/input.css.js +1 -1
- package/dist/inputs/input/input.css.js.map +1 -1
- package/dist/inputs/input/input.d.ts +3 -0
- package/dist/inputs/input/input.d.ts.map +1 -1
- package/dist/inputs/select/select.css.cjs +1 -1
- package/dist/inputs/select/select.css.cjs.map +1 -1
- package/dist/inputs/select/select.css.js +1 -1
- package/dist/inputs/select/select.css.js.map +1 -1
- package/dist/inputs/select/select.d.ts +5 -0
- package/dist/inputs/select/select.d.ts.map +1 -1
- package/dist/inputs/shared/field-binding.cjs +1 -1
- package/dist/inputs/shared/field-binding.cjs.map +1 -1
- package/dist/inputs/shared/field-binding.js +6 -6
- package/dist/inputs/shared/field-binding.js.map +1 -1
- package/dist/layout/box/box.css.cjs +1 -1
- package/dist/layout/box/box.css.cjs.map +1 -1
- package/dist/layout/box/box.css.js +1 -1
- package/dist/layout/box/box.css.js.map +1 -1
- package/dist/layout/box/box.d.ts +3 -3
- package/dist/layout/box/box.d.ts.map +1 -1
- package/dist/layout/grid/grid.d.ts.map +1 -1
- package/dist/layout/navbar/navbar.css.cjs +1 -1
- package/dist/layout/navbar/navbar.css.cjs.map +1 -1
- package/dist/layout/navbar/navbar.css.js +1 -1
- package/dist/layout/navbar/navbar.css.js.map +1 -1
- package/dist/layout/navbar/navbar.d.ts +3 -1
- package/dist/layout/navbar/navbar.d.ts.map +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs +1 -1
- package/dist/layout/sidebar/sidebar.css.cjs.map +1 -1
- package/dist/layout/sidebar/sidebar.css.js +1 -1
- package/dist/layout/sidebar/sidebar.css.js.map +1 -1
- package/dist/layout/sidebar/sidebar.d.ts +8 -0
- package/dist/layout/sidebar/sidebar.d.ts.map +1 -1
- package/dist/list-item.cjs +6 -5
- package/dist/list-item.cjs.map +1 -1
- package/dist/list-item.js +23 -17
- package/dist/list-item.js.map +1 -1
- package/dist/list.cjs +1 -1
- package/dist/list.cjs.map +1 -1
- package/dist/list.js +1 -1
- package/dist/list.js.map +1 -1
- package/dist/menu.cjs +1 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.js +7 -7
- package/dist/menu.js.map +1 -1
- package/dist/navbar.cjs +2 -2
- package/dist/navbar.cjs.map +1 -1
- package/dist/navbar.js +91 -87
- package/dist/navbar.js.map +1 -1
- package/dist/navigation-menu.cjs +1 -1
- package/dist/navigation-menu.cjs.map +1 -1
- package/dist/navigation-menu.js +2 -2
- package/dist/navigation-menu.js.map +1 -1
- package/dist/number-input.cjs +1 -1
- package/dist/number-input.cjs.map +1 -1
- package/dist/number-input.js +1 -1
- package/dist/number-input.js.map +1 -1
- package/dist/overlay/popover/popover.css.cjs +1 -1
- package/dist/overlay/popover/popover.css.cjs.map +1 -1
- package/dist/overlay/popover/popover.css.js +1 -1
- package/dist/overlay/popover/popover.css.js.map +1 -1
- package/dist/overlay/popover/popover.d.ts +1 -0
- package/dist/overlay/popover/popover.d.ts.map +1 -1
- package/dist/overlay/shared/use-dialog.cjs +1 -1
- package/dist/overlay/shared/use-dialog.cjs.map +1 -1
- package/dist/overlay/shared/use-dialog.js +1 -1
- package/dist/overlay/shared/use-dialog.js.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs +1 -1
- package/dist/overlay/shared/use-floating-trigger.cjs.map +1 -1
- package/dist/overlay/shared/use-floating-trigger.js +1 -1
- package/dist/overlay/shared/use-floating-trigger.js.map +1 -1
- package/dist/overlay/tooltip/tooltip.d.ts.map +1 -1
- package/dist/popover.cjs +1 -1
- package/dist/popover.cjs.map +1 -1
- package/dist/popover.js +6 -6
- package/dist/popover.js.map +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.cjs.map +1 -1
- package/dist/radio-group.js +1 -1
- package/dist/radio-group.js.map +1 -1
- package/dist/radio.cjs +1 -1
- package/dist/radio.cjs.map +1 -1
- package/dist/radio.js +15 -13
- package/dist/radio.js.map +1 -1
- package/dist/rating.cjs +1 -1
- package/dist/rating.cjs.map +1 -1
- package/dist/rating.js +2 -2
- package/dist/rating.js.map +1 -1
- package/dist/refine.cjs +373 -324
- package/dist/refine.cjs.map +1 -1
- package/dist/refine.iife.js +382 -333
- package/dist/refine.iife.js.map +1 -1
- package/dist/refine.js +417 -368
- package/dist/refine.js.map +1 -1
- package/dist/select.cjs +16 -14
- package/dist/select.cjs.map +1 -1
- package/dist/select.js +118 -112
- package/dist/select.js.map +1 -1
- package/dist/shared/types.d.ts +0 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/sidebar.cjs +28 -23
- package/dist/sidebar.cjs.map +1 -1
- package/dist/sidebar.js +143 -108
- package/dist/sidebar.js.map +1 -1
- package/dist/skeleton.cjs +1 -1
- package/dist/skeleton.cjs.map +1 -1
- package/dist/skeleton.js +11 -9
- package/dist/skeleton.js.map +1 -1
- package/dist/slider.cjs +1 -1
- package/dist/slider.cjs.map +1 -1
- package/dist/slider.js +21 -21
- package/dist/slider.js.map +1 -1
- package/dist/stats.cjs +18 -0
- package/dist/stats.cjs.map +1 -0
- package/dist/stats.js +88 -0
- package/dist/stats.js.map +1 -0
- package/dist/stepper.cjs +1 -1
- package/dist/stepper.cjs.map +1 -1
- package/dist/stepper.js +1 -1
- package/dist/stepper.js.map +1 -1
- package/dist/styles/mixins/animation.css.cjs +2 -0
- package/dist/styles/mixins/animation.css.cjs.map +1 -1
- package/dist/styles/mixins/animation.css.d.ts.map +1 -1
- package/dist/styles/mixins/animation.css.js +2 -0
- package/dist/styles/mixins/animation.css.js.map +1 -1
- package/dist/tab-item.cjs +1 -1
- package/dist/tab-item.cjs.map +1 -1
- package/dist/tab-item.js +3 -2
- package/dist/tab-item.js.map +1 -1
- package/dist/tabs.cjs +1 -1
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs.js +3 -1
- package/dist/tabs.js.map +1 -1
- package/dist/toast.cjs +8 -8
- package/dist/toast.cjs.map +1 -1
- package/dist/toast.js +32 -33
- package/dist/toast.js.map +1 -1
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.cjs.map +1 -1
- package/dist/tooltip.js +2 -2
- package/dist/tooltip.js.map +1 -1
- package/dist/types/elements.d.ts +207 -0
- package/package.json +70 -62
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datagrid.css.js","names":[],"sources":["../../../src/inputs/datagrid/datagrid.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n /* ── Datagrid-specific token overrides on top of tableBaseMixin ─────── */\n --_row-hover-bg: var(--datagrid-row-hover-bg, color-mix(in oklch, var(--color-contrast-500) 5%, transparent));\n --_row-selected-bg: var(--datagrid-row-selected-bg, color-mix(in oklch, var(--_accent) 10%, transparent));\n --_row-selected-border: var(--datagrid-row-selected-border, var(--_accent));\n }\n\n /* ── Scroll wrapper ───────────────────────────────────────────────────────── */\n\n .dg-scroll {\n max-height: var(--datagrid-max-height, none);\n overflow: auto;\n }\n\n /* ── Table ────────────────────────────────────────────────────────────────── */\n\n .dg-table {\n width: 100%;\n font-size: var(--_font-size);\n table-layout: auto;\n border-collapse: collapse;\n }\n\n /* ── Head ─────────────────────────────────────────────────────────────────── */\n\n .dg-head {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--_header-bg);\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-th {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n\n &:has(.dg-sort-btn) {\n padding: 0;\n }\n }\n\n .dg-sort-btn {\n --button-padding: var(--_cell-padding-y) var(--_cell-padding-x);\n --button-font-size: var(--text-xs);\n --button-font-weight: var(--font-semibold);\n --button-color: var(--color-contrast-500);\n --button-justify: flex-start;\n\n height: 100%;\n }\n\n .dg-th[data-align='center'] .dg-sort-btn {\n --button-justify: center;\n }\n\n .dg-th[data-align='right'] .dg-sort-btn {\n --button-justify: flex-end;\n }\n\n :host(:dir(rtl)) .dg-th,\n :dir(rtl) .dg-th {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-th:last-child,\n .dg-th.dg-th-check,\n .dg-th.dg-th-expand,\n .dg-th.dg-th-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-th:last-child,\n :host(:dir(rtl)) .dg-th.dg-th-check,\n :host(:dir(rtl)) .dg-th.dg-th-expand,\n :host(:dir(rtl)) .dg-th.dg-th-last,\n :dir(rtl) .dg-th:last-child,\n :dir(rtl) .dg-th.dg-th-check,\n :dir(rtl) .dg-th.dg-th-expand,\n :dir(rtl) .dg-th.dg-th-last {\n border-left: none;\n }\n\n .dg-th-inner {\n position: relative;\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-th[aria-sort]:has(.dg-sort-btn:hover):not(:has(.dg-sort-btn[aria-disabled='true'])) {\n background: color-mix(in oklch, var(--color-contrast-900) 5%, transparent);\n }\n\n .dg-th[aria-sort='ascending'] .dg-sort-icon,\n .dg-th[aria-sort='descending'] .dg-sort-icon {\n color: var(--_accent);\n }\n\n /* Select-all checkbox cell */\n .dg-th-check {\n width: var(--size-5);\n min-width: var(--size-5);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n }\n\n .dg-th-check,\n .dg-td-check {\n vertical-align: middle;\n text-align: center;\n }\n\n .dg-td.dg-td-check {\n padding: var(--size-1) 0 0;\n }\n\n /* ore-checkbox used for select-all and row checkboxes — label-less, compact */\n .dg-check {\n display: inline-flex;\n gap: 0;\n align-items: center;\n justify-content: center;\n min-height: 0;\n cursor: pointer;\n }\n\n /* ── Body rows ────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n .dg-body .dg-tr:hover:not([data-disabled], [aria-selected='true']) {\n background: var(--_row-hover-bg);\n }\n\n /* ── Selection ─────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n .dg-body .dg-tr[data-selectable] {\n cursor: pointer;\n outline: none;\n }\n\n .dg-body .dg-tr[data-selectable]:focus-visible {\n box-shadow: inset 0 0 0 2px var(--_row-selected-border);\n }\n\n .dg-body .dg-tr[data-disabled] {\n pointer-events: none;\n opacity: 0.45;\n }\n\n /* Striped rows */\n :host([striped]) .dg-body .dg-tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n :host([striped]) .dg-body .dg-tr:nth-child(even)[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n /* ── Body cells ────────────────────────────────────────────────────────────── */\n\n .dg-td {\n max-width: var(--datagrid-cell-max-width, 32ch);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n color: var(--color-contrast-800);\n white-space: nowrap;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n }\n\n :host(:dir(rtl)) .dg-td,\n :dir(rtl) .dg-td {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-td:last-child,\n .dg-td.dg-td-check,\n .dg-td.dg-td-expand,\n .dg-td.dg-td-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-td:last-child,\n :host(:dir(rtl)) .dg-td.dg-td-check,\n :host(:dir(rtl)) .dg-td.dg-td-expand,\n :host(:dir(rtl)) .dg-td.dg-td-last,\n :dir(rtl) .dg-td:last-child,\n :dir(rtl) .dg-td.dg-td-check,\n :dir(rtl) .dg-td.dg-td-expand,\n :dir(rtl) .dg-td.dg-td-last {\n border-left: none;\n }\n\n /* ── Empty state ──────────────────────────────────────────────────────────── */\n\n .dg-empty {\n padding: var(--size-12) var(--_cell-padding-x);\n font-size: var(--text-sm);\n color: var(--color-contrast-400);\n text-align: center;\n }\n\n .dg-empty-content {\n display: flex;\n flex-direction: column;\n gap: var(--size-4);\n align-items: center;\n justify-content: center;\n }\n\n .dg-empty-actions {\n margin-top: var(--size-2);\n }\n\n /* ── Controls bar (tabs + action row) ───────────────────────────────────── */\n\n .dg-controls {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n height: var(--size-8);\n padding: var(--size-1-5) var(--_cell-padding-x);\n background: var(--_header-bg);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n /* ── Tab system (left) ───────────────────────────────────────────────────── */\n\n .dg-tabs {\n display: flex;\n flex: 1;\n gap: var(--size-1);\n align-items: center;\n min-width: 0;\n overflow: hidden;\n }\n\n .dg-tab {\n --button-padding: var(--size-0-5) var(--size-2-5);\n --button-font-size: var(--text-xs);\n --button-color: var(--color-contrast-500);\n\n flex: 1;\n min-width: 0;\n }\n\n .dg-tab--active {\n --button-color: var(--color-contrast-900);\n --button-bg: color-mix(in oklch, var(--color-contrast-500) 10%, transparent);\n --button-border-color: var(--_border-color);\n --button-border: var(--border);\n\n font-weight: var(--font-semibold);\n }\n\n /* ── Action bar (right) ──────────────────────────────────────────────────── */\n\n .dg-actions {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-0-5);\n align-items: center;\n }\n\n /* ── Shared icon button — used by action bar, search-expand clear, popover header icons ── */\n\n .dg-action-divider {\n display: inline-block;\n flex-shrink: 0;\n width: var(--border);\n height: var(--size-4);\n margin-inline: var(--size-1);\n background: var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-filter {\n display: block;\n width: 100%;\n }\n\n /* ── Search toggle (rightmost action) ───────────────────────────────────── */\n\n .dg-search {\n display: flex;\n flex-direction: row-reverse;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-search-input {\n --input-height: var(--size-7);\n display: block;\n flex-shrink: 0;\n width: 0;\n overflow: hidden;\n opacity: 0;\n transition:\n width var(--transition-fast),\n opacity var(--transition-fast);\n }\n\n .dg-search--open .dg-search-input {\n width: 12rem;\n overflow: visible;\n opacity: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .dg-search-input {\n transition: none;\n }\n }\n\n /* ── ore-popover: action popover wrapper ─────────────────────────────────── */\n\n .dg-action-popover {\n display: inline-flex;\n }\n\n /* ── Popover content: shared header row ─────────────────────────────────── */\n\n .dg-pop-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: var(--size-3) var(--size-3) var(--size-2);\n }\n\n .dg-pop-title {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-600);\n letter-spacing: 0.02em;\n }\n\n /* ── Sort / Group by popover content ────────────────────────────────────── */\n\n .dg-pop-sort {\n display: flex;\n flex-direction: column;\n padding-block-end: var(--size-2);\n }\n\n .dg-pop-sort-row {\n display: flex;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-1) var(--size-3);\n }\n\n .dg-pop-select {\n --select-min-width: var(--size-20);\n\n flex: 2;\n }\n\n .dg-pop-dir-select {\n --select-min-width: var(--size-10);\n\n flex: 1;\n }\n\n /* ── Filter popover content ──────────────────────────────────────────────── */\n\n .dg-pop-filter {\n display: flex;\n flex-direction: column;\n }\n\n /* Field picker: select columns to add filter rules. */\n .dg-pop-filter-fields {\n padding: 0 var(--size-3) var(--size-3);\n }\n\n /* ── Filter rules section ────────────────────────────────────────────────── */\n\n .dg-pop-filter-rules {\n padding-block-start: var(--size-1);\n border-block-start: var(--border) solid var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-pop-filter-rule {\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-filter-rule-header {\n display: flex;\n gap: var(--size-1-5);\n align-items: center;\n }\n\n .dg-pop-filter-field {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-800);\n white-space: nowrap;\n }\n\n .dg-pop-filter-op-select {\n --select-min-width: 0;\n --select-height: var(--size-6);\n --select-font-size: var(--text-2xs);\n --select-padding-x: var(--size-1-5);\n\n flex: 1;\n min-width: 6rem;\n }\n\n .dg-pop-col-item {\n --button-radius: var(--rounded-md);\n\n justify-content: flex-start;\n }\n\n .dg-pop-col-list {\n display: flex;\n flex-wrap: wrap;\n gap: var(--size-1);\n max-width: 25rem;\n padding: var(--size-3);\n }\n\n /* ── Footer / pagination bar ─────────────────────────────────────────────── */\n\n .dg-footer {\n display: flex;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-2) var(--_cell-padding-x);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n background: var(--_header-bg);\n }\n\n .dg-footer-info {\n white-space: nowrap;\n }\n\n .dg-footer-end {\n display: flex;\n gap: var(--size-2);\n align-items: center;\n width: fit-content;\n margin-inline-start: auto;\n }\n\n .dg-page-size-wrap {\n flex-shrink: 0;\n width: 5rem;\n }\n\n .dg-page-size-select {\n --select-min-width: 0;\n --select-height: var(--size-7);\n --select-font-size: var(--text-xs);\n --select-padding-x: var(--size-2);\n }\n\n .dg-pagination {\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n :host(:dir(rtl)) .dg-page-btn,\n :dir(rtl) .dg-page-btn {\n transform: scaleX(-1);\n }\n\n .dg-page-btn {\n --button-color: var(--color-contrast-600);\n --button-radius: var(--rounded-md);\n\n width: var(--size-7);\n height: var(--size-7);\n }\n\n .dg-page-btn:hover {\n --button-color: var(--color-contrast-900);\n --button-bg: var(--color-contrast-300);\n }\n\n .dg-page-btn:disabled {\n cursor: not-allowed;\n opacity: 0.35;\n }\n\n .dg-page-label {\n padding-inline: var(--size-1);\n white-space: nowrap;\n }\n\n /* ── Column resize handle (F4) ───────────────────────────────────────────── */\n\n .dg-col-resize {\n position: absolute;\n inset-block: calc(var(--_cell-padding-y) * -1);\n inset-inline-end: calc((var(--_cell-padding-x) + var(--border) + 2px) * -1);\n width: var(--size-1);\n cursor: col-resize;\n user-select: none;\n background: transparent;\n border-radius: var(--rounded);\n transition: background var(--transition-fast);\n }\n\n .dg-col-resize:hover,\n .dg-col-resize:active {\n background: var(--_accent, var(--color-contrast-300));\n }\n\n /* ── Cell keyboard focus (ARIA grid roving tabindex) ─────────────────────── */\n\n .dg-td:focus-visible,\n .dg-th:focus-visible {\n outline: 2px solid var(--_row-selected-border);\n outline-offset: -2px;\n }\n\n /* ── Row expansion ────────────────────────────────────────────────────────── */\n\n .dg-tr-expanded .dg-td-expanded {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n border: none;\n\n /* Paint background + bottom separator via box-shadow so it is\n direction-agnostic and covers the host outer border on both sides. */\n box-shadow:\n inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-600) 4%, var(--_bg, transparent)),\n inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-tr-expanded:last-child .dg-td-expanded {\n box-shadow: inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-900) 4%, var(--_bg, transparent));\n }\n\n .dg-tr:has(+ .dg-tr-expanded) {\n border-block-end: none;\n }\n\n .dg-th-expand,\n .dg-td-expand {\n width: var(--size-8);\n min-width: var(--size-8);\n padding-block: var(--size-1) 0;\n padding-inline: 0 var(--size-2);\n text-align: center;\n white-space: nowrap;\n border-right: none;\n border-left: none;\n }\n\n .dg-expand-btn {\n --button-color: var(--color-contrast-500);\n --button-radius: var(--rounded-sm);\n\n width: var(--size-5);\n height: var(--size-5);\n }\n\n .dg-expand-btn:hover {\n --button-color: var(--color-contrast-900);\n }\n\n .dg-expand-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n /* ── Loading overlay ──────────────────────────────────────────────────────── */\n\n .dg-loading-overlay {\n position: absolute;\n inset: 0;\n z-index: 3;\n display: none;\n align-items: center;\n justify-content: center;\n background: color-mix(in oklch, var(--_bg, white) 40%, transparent);\n backdrop-filter: blur(1px);\n }\n\n :host([loading]) .dg-loading-overlay {\n display: flex;\n }\n\n :host([loading]) .dg-scroll table {\n pointer-events: none;\n opacity: 0.5;\n }\n\n .dg-spin {\n animation: dg-spin 1s linear infinite;\n }\n\n @keyframes dg-spin {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n }\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"datagrid.css.js","names":[],"sources":["../../../src/inputs/datagrid/datagrid.css?inline"],"sourcesContent":["@layer refine.base {\n :host {\n /* ── Datagrid-specific token overrides on top of tableBaseMixin ─────── */\n --_row-hover-bg: var(--datagrid-row-hover-bg, color-mix(in oklch, var(--color-contrast-500) 5%, transparent));\n --_row-selected-bg: var(--datagrid-row-selected-bg, color-mix(in oklch, var(--_accent) 10%, transparent));\n --_row-selected-border: var(--datagrid-row-selected-border, var(--_accent));\n }\n\n /* ── Scroll wrapper ───────────────────────────────────────────────────────── */\n\n .dg-scroll {\n max-height: var(--datagrid-max-height, none);\n overflow: auto;\n }\n\n /* ── Table ────────────────────────────────────────────────────────────────── */\n\n .dg-table {\n width: 100%;\n font-size: var(--_font-size);\n table-layout: auto;\n border-collapse: collapse;\n }\n\n /* ── Head ─────────────────────────────────────────────────────────────────── */\n\n .dg-head {\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--_header-bg);\n box-shadow: inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-th {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-500);\n text-align: start;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n white-space: nowrap;\n user-select: none;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n\n &:has(.dg-sort-btn) {\n padding: 0;\n }\n }\n\n .dg-sort-btn {\n --button-padding: var(--_cell-padding-y) var(--_cell-padding-x);\n --button-font-size: var(--text-xs);\n --button-font-weight: var(--font-semibold);\n --button-color: var(--color-contrast-500);\n --button-justify: flex-start;\n\n height: 100%;\n }\n\n .dg-th[data-align='center'] .dg-sort-btn {\n --button-justify: center;\n }\n\n .dg-th[data-align='right'] .dg-sort-btn {\n --button-justify: flex-end;\n }\n\n :host(:dir(rtl)) .dg-th,\n :dir(rtl) .dg-th {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-th:last-child,\n .dg-th.dg-th-check,\n .dg-th.dg-th-expand,\n .dg-th.dg-th-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-th:last-child,\n :host(:dir(rtl)) .dg-th.dg-th-check,\n :host(:dir(rtl)) .dg-th.dg-th-expand,\n :host(:dir(rtl)) .dg-th.dg-th-last,\n :dir(rtl) .dg-th:last-child,\n :dir(rtl) .dg-th.dg-th-check,\n :dir(rtl) .dg-th.dg-th-expand,\n :dir(rtl) .dg-th.dg-th-last {\n border-left: none;\n }\n\n .dg-th-inner {\n position: relative;\n display: flex;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-th[aria-sort]:has(.dg-sort-btn:hover):not(:has(.dg-sort-btn[aria-disabled='true'])) {\n background: color-mix(in oklch, var(--color-contrast-900) 5%, transparent);\n }\n\n .dg-th[aria-sort='ascending'] .dg-sort-icon,\n .dg-th[aria-sort='descending'] .dg-sort-icon {\n color: var(--_accent);\n }\n\n /* Select-all checkbox cell */\n .dg-th-check {\n width: var(--size-5);\n min-width: var(--size-5);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n }\n\n .dg-th-check,\n .dg-td-check {\n vertical-align: middle;\n text-align: center;\n }\n\n .dg-td.dg-td-check {\n padding: var(--size-1) 0 0;\n }\n\n /* ore-checkbox used for select-all and row checkboxes — label-less, compact */\n .dg-check {\n display: inline-flex;\n gap: 0;\n align-items: center;\n justify-content: center;\n min-height: 0;\n cursor: pointer;\n }\n\n /* ── Body rows ────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr {\n border-block-end: var(--border) solid var(--_border-color);\n transition: background var(--transition-fast);\n }\n\n .dg-body .dg-tr:hover:not([data-disabled], [aria-selected='true']) {\n background: var(--_row-hover-bg);\n }\n\n /* ── Selection ─────────────────────────────────────────────────────────────── */\n\n .dg-body .dg-tr[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n .dg-body .dg-tr[data-selectable] {\n cursor: pointer;\n outline: none;\n }\n\n .dg-body .dg-tr[data-selectable]:focus-visible {\n box-shadow: inset 0 0 0 2px var(--_row-selected-border);\n }\n\n .dg-body .dg-tr[data-disabled] {\n pointer-events: none;\n opacity: 0.45;\n }\n\n /* Striped rows */\n :host([striped]) .dg-body .dg-tr:nth-child(even) {\n background: var(--_stripe-bg);\n }\n\n :host([striped]) .dg-body .dg-tr:nth-child(even)[aria-selected='true'] {\n background: var(--_row-selected-bg);\n }\n\n /* ── Body cells ────────────────────────────────────────────────────────────── */\n\n .dg-td {\n max-width: var(--datagrid-cell-max-width, 32ch);\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n overflow: hidden;\n text-overflow: ellipsis;\n vertical-align: middle;\n color: var(--color-contrast-800);\n white-space: nowrap;\n border-right: var(--border) solid var(--_border-color);\n\n &[data-align='center'] {\n text-align: center;\n }\n\n &[data-align='right'] {\n text-align: right;\n }\n }\n\n :host(:dir(rtl)) .dg-td,\n :dir(rtl) .dg-td {\n border-right: none;\n border-left: var(--border) solid var(--_border-color);\n }\n\n .dg-td:last-child,\n .dg-td.dg-td-check,\n .dg-td.dg-td-expand,\n .dg-td.dg-td-last {\n border-right: none;\n }\n\n :host(:dir(rtl)) .dg-td:last-child,\n :host(:dir(rtl)) .dg-td.dg-td-check,\n :host(:dir(rtl)) .dg-td.dg-td-expand,\n :host(:dir(rtl)) .dg-td.dg-td-last,\n :dir(rtl) .dg-td:last-child,\n :dir(rtl) .dg-td.dg-td-check,\n :dir(rtl) .dg-td.dg-td-expand,\n :dir(rtl) .dg-td.dg-td-last {\n border-left: none;\n }\n\n /* ── Empty state ──────────────────────────────────────────────────────────── */\n\n .dg-empty {\n padding: var(--size-12) var(--_cell-padding-x);\n font-size: var(--text-sm);\n color: var(--color-contrast-400);\n text-align: center;\n }\n\n .dg-empty-content {\n display: flex;\n flex-direction: column;\n gap: var(--size-4);\n align-items: center;\n justify-content: center;\n }\n\n .dg-empty-actions {\n margin-top: var(--size-2);\n }\n\n /* ── Controls bar (tabs + action row) ───────────────────────────────────── */\n\n .dg-controls {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n height: var(--size-8);\n padding: var(--size-1-5) var(--_cell-padding-x);\n background: var(--_header-bg);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n /* ── Tab system (left) ───────────────────────────────────────────────────── */\n\n .dg-tabs {\n display: flex;\n flex: 1;\n gap: var(--size-1);\n align-items: center;\n min-width: 0;\n overflow: hidden;\n }\n\n .dg-tab {\n --button-padding: var(--size-0-5) var(--size-2-5);\n --button-font-size: var(--text-xs);\n --button-color: var(--color-contrast-500);\n\n flex: 1;\n min-width: 0;\n }\n\n @media (max-width: 40rem) {\n .dg-tabs {\n overflow-x: auto;\n overscroll-behavior-inline: contain;\n scrollbar-width: none;\n }\n\n .dg-tabs::-webkit-scrollbar {\n display: none;\n }\n\n .dg-tab {\n flex: 0 0 auto;\n min-width: max-content;\n }\n }\n\n .dg-tab--active {\n --button-color: var(--color-contrast-900);\n --button-bg: color-mix(in oklch, var(--color-contrast-500) 10%, transparent);\n --button-border-color: var(--_border-color);\n --button-border: var(--border);\n\n font-weight: var(--font-semibold);\n }\n\n /* ── Action bar (right) ──────────────────────────────────────────────────── */\n\n .dg-actions {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-0-5);\n align-items: center;\n }\n\n /* ── Shared icon button — used by action bar, search-expand clear, popover header icons ── */\n\n .dg-action-divider {\n display: inline-block;\n flex-shrink: 0;\n width: var(--border);\n height: var(--size-4);\n margin-inline: var(--size-1);\n background: var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-filter {\n display: block;\n width: 100%;\n }\n\n /* ── Search toggle (rightmost action) ───────────────────────────────────── */\n\n .dg-search {\n display: flex;\n flex-direction: row-reverse;\n gap: var(--size-1);\n align-items: center;\n }\n\n .dg-search-input {\n --input-height: var(--size-7);\n display: block;\n flex-shrink: 0;\n width: 0;\n overflow: hidden;\n opacity: 0;\n transition:\n width var(--transition-fast),\n opacity var(--transition-fast);\n }\n\n .dg-search--open .dg-search-input {\n width: 12rem;\n overflow: visible;\n opacity: 1;\n }\n\n @media (prefers-reduced-motion: reduce) {\n .dg-search-input {\n transition: none;\n }\n }\n\n /* ── ore-popover: action popover wrapper ─────────────────────────────────── */\n\n .dg-action-popover {\n display: inline-flex;\n }\n\n /* ── Popover content: shared header row ─────────────────────────────────── */\n\n .dg-pop-header {\n display: flex;\n gap: var(--size-3);\n align-items: center;\n justify-content: space-between;\n min-height: var(--size-10);\n padding: var(--size-2) var(--size-3);\n border-block-end: var(--border) solid var(--_border-color);\n }\n\n .dg-pop-title {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-600);\n letter-spacing: 0.02em;\n }\n\n .dg-pop-heading,\n .dg-pop-heading > * {\n display: block;\n min-width: 0;\n }\n\n .dg-pop-heading small {\n margin-block-start: var(--size-0-5);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n }\n\n /* ── Sort / Group by popover content ────────────────────────────────────── */\n\n .dg-pop-sort {\n display: flex;\n flex-direction: column;\n }\n\n .dg-pop-sort-row {\n display: flex;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-select,\n .dg-pop-dir-select {\n --input-min-width: 0;\n --select-min-width: 0;\n width: 0;\n min-width: 0;\n }\n\n .dg-pop-select {\n flex: 2 1 0;\n }\n\n .dg-pop-dir-select {\n flex: 0 0 var(--size-28);\n width: var(--size-28);\n }\n\n /* ── Filter popover content ──────────────────────────────────────────────── */\n\n .dg-pop-filter {\n display: flex;\n flex-direction: column;\n }\n\n /* Field picker: select columns to add filter rules. */\n .dg-pop-filter-fields {\n padding: var(--size-2) var(--size-3);\n }\n\n /* ── Filter rules section ────────────────────────────────────────────────── */\n\n .dg-pop-filter-rules {\n padding-block-start: var(--size-1);\n border-block-start: var(--border) solid var(--_border-color);\n\n &[hidden] {\n display: none;\n }\n }\n\n .dg-pop-filter-rule {\n display: flex;\n flex-direction: column;\n gap: var(--size-2);\n padding: var(--size-2) var(--size-3);\n }\n\n .dg-pop-filter-rule-header {\n display: flex;\n gap: var(--size-1-5);\n align-items: center;\n }\n\n .dg-pop-filter-field {\n font-size: var(--text-xs);\n font-weight: var(--font-semibold);\n color: var(--color-contrast-800);\n white-space: nowrap;\n }\n\n .dg-pop-filter-op-select {\n --input-min-width: 0;\n --select-min-width: 0;\n --select-height: var(--size-6);\n --select-font-size: var(--text-2xs);\n --select-padding-x: var(--size-1-5);\n\n flex: 1 1 0;\n min-width: 6rem;\n }\n\n .dg-state-trigger {\n position: relative;\n }\n\n .dg-state-trigger[data-customized]::after {\n position: absolute;\n inset-block-start: var(--size-1);\n inset-inline-end: var(--size-1);\n width: var(--size-1-5);\n height: var(--size-1-5);\n content: '';\n background: var(--color-primary);\n border: var(--border) solid var(--_header-bg);\n border-radius: 50%;\n }\n\n .dg-pop-col {\n width: 18rem;\n }\n\n .dg-pop-col-list {\n display: grid;\n gap: var(--size-0-5);\n max-height: min(50vh, 24rem);\n padding: var(--size-2) var(--size-3);\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n\n .dg-pop-col-item {\n --checkbox-size: var(--size-4);\n --checkbox-font-size: var(--text-sm);\n\n width: 100%;\n padding: var(--size-1) var(--size-2);\n cursor: pointer;\n border-radius: var(--rounded-md);\n }\n\n .dg-pop-col-item:hover {\n background: var(--color-contrast-150);\n }\n\n .dg-pop-col-item::part(label) {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ── Footer / pagination bar ─────────────────────────────────────────────── */\n\n .dg-footer {\n display: flex;\n flex-wrap: wrap;\n gap: var(--size-2);\n align-items: center;\n padding: var(--size-1) var(--_cell-padding-x);\n font-size: var(--text-xs);\n color: var(--color-contrast-500);\n background: var(--_header-bg);\n }\n\n .dg-footer-info {\n white-space: nowrap;\n }\n\n .dg-footer-start {\n display: flex;\n flex: 1 1 auto;\n gap: var(--size-2);\n align-items: center;\n min-width: 0;\n }\n\n .dg-page-size-wrap {\n flex-shrink: 0;\n order: -1;\n width: var(--size-20);\n }\n\n .dg-page-size-select {\n --input-height: var(--size-8);\n --input-min-width: 0;\n --select-min-width: 0;\n --select-font-size: var(--text-xs);\n --select-padding-x: var(--size-2);\n }\n\n .dg-pagination {\n display: flex;\n flex-shrink: 0;\n gap: var(--size-1);\n align-items: center;\n margin-inline-start: auto;\n }\n\n :host(:dir(rtl)) .dg-page-btn,\n :dir(rtl) .dg-page-btn {\n transform: scaleX(-1);\n }\n\n .dg-page-btn {\n --button-color: var(--color-contrast-600);\n --button-radius: var(--rounded-md);\n\n width: var(--size-7);\n height: var(--size-7);\n }\n\n .dg-page-btn:hover {\n --button-color: var(--color-contrast-900);\n --button-bg: var(--color-contrast-300);\n }\n\n .dg-page-btn:disabled {\n cursor: not-allowed;\n opacity: 0.35;\n }\n\n .dg-page-label {\n padding-inline: var(--size-1);\n white-space: nowrap;\n }\n\n /* ── Column resize handle (F4) ───────────────────────────────────────────── */\n\n .dg-col-resize {\n position: absolute;\n inset-block: calc(var(--_cell-padding-y) * -1);\n inset-inline-end: calc((var(--_cell-padding-x) + var(--border) + 2px) * -1);\n width: var(--size-1);\n cursor: col-resize;\n user-select: none;\n background: transparent;\n border-radius: var(--rounded);\n transition: background var(--transition-fast);\n }\n\n .dg-col-resize:hover,\n .dg-col-resize:active {\n background: var(--_accent, var(--color-contrast-300));\n }\n\n /* ── Cell keyboard focus (ARIA grid roving tabindex) ─────────────────────── */\n\n .dg-td:focus-visible,\n .dg-th:focus-visible {\n outline: 2px solid var(--_row-selected-border);\n outline-offset: -2px;\n }\n\n /* ── Row expansion ────────────────────────────────────────────────────────── */\n\n .dg-tr-expanded .dg-td-expanded {\n padding: var(--_cell-padding-y) var(--_cell-padding-x);\n border: none;\n\n /* Paint background + bottom separator via box-shadow so it is\n direction-agnostic and covers the host outer border on both sides. */\n box-shadow:\n inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-600) 4%, var(--_bg, transparent)),\n inset 0 calc(var(--border) * -1) 0 var(--_border-color);\n }\n\n .dg-tr-expanded:last-child .dg-td-expanded {\n box-shadow: inset 0 0 0 9999px color-mix(in oklch, var(--color-contrast-900) 4%, var(--_bg, transparent));\n }\n\n .dg-tr:has(+ .dg-tr-expanded) {\n border-block-end: none;\n }\n\n .dg-th-expand,\n .dg-td-expand {\n width: var(--size-8);\n min-width: var(--size-8);\n padding-block: var(--size-1) 0;\n padding-inline: 0 var(--size-2);\n text-align: center;\n white-space: nowrap;\n border-right: none;\n border-left: none;\n }\n\n .dg-expand-btn {\n --button-color: var(--color-contrast-500);\n --button-radius: var(--rounded-sm);\n\n width: var(--size-5);\n height: var(--size-5);\n }\n\n .dg-expand-btn:hover {\n --button-color: var(--color-contrast-900);\n }\n\n .dg-expand-btn:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n /* ── Loading overlay ──────────────────────────────────────────────────────── */\n\n .dg-loading-overlay {\n position: absolute;\n inset: 0;\n z-index: 3;\n display: none;\n align-items: center;\n justify-content: center;\n background: color-mix(in oklch, var(--_bg, white) 40%, transparent);\n backdrop-filter: blur(1px);\n }\n\n :host([loading]) .dg-loading-overlay {\n display: flex;\n }\n\n :host([loading]) .dg-scroll table {\n pointer-events: none;\n opacity: 0.5;\n }\n\n .dg-spin {\n animation: dg-spin 1s linear infinite;\n }\n\n @keyframes dg-spin {\n from {\n transform: rotate(0deg);\n }\n\n to {\n transform: rotate(360deg);\n }\n }\n}\n"],"mappings":""}
|
|
@@ -6,8 +6,55 @@ import '../../inputs/select/select';
|
|
|
6
6
|
import '../../overlay/popover/popover';
|
|
7
7
|
import { type DataGridColumn, type DataGridView, type FilterOption, type SelectionMode, type SortDirection, type SortState } from './datagrid-model';
|
|
8
8
|
type SortMode = 'client' | 'server';
|
|
9
|
+
export type DataGridLabels = {
|
|
10
|
+
activeFilters: (count: number) => string;
|
|
11
|
+
addFilter: string;
|
|
12
|
+
ascending: string;
|
|
13
|
+
clearAllFilters: string;
|
|
14
|
+
clearFiltersAndSearch: string;
|
|
15
|
+
clearSort: string;
|
|
16
|
+
closeSearch: string;
|
|
17
|
+
collapseRow: string;
|
|
18
|
+
columnOptions: string;
|
|
19
|
+
columnVisibility: string;
|
|
20
|
+
comfortableDensity: string;
|
|
21
|
+
compactDensity: string;
|
|
22
|
+
contains: string;
|
|
23
|
+
cozyDensity: string;
|
|
24
|
+
data: string;
|
|
25
|
+
descending: string;
|
|
26
|
+
emptyValue: string;
|
|
27
|
+
equals: string;
|
|
28
|
+
expandRow: string;
|
|
29
|
+
filter: string;
|
|
30
|
+
filterBy: string;
|
|
31
|
+
filterOperator: (field: string) => string;
|
|
32
|
+
greaterThan: string;
|
|
33
|
+
hiddenColumns: (count: number) => string;
|
|
34
|
+
lessThan: string;
|
|
35
|
+
nextPage: string;
|
|
36
|
+
numericAscending: string;
|
|
37
|
+
numericDescending: string;
|
|
38
|
+
pageNavigation: string;
|
|
39
|
+
pagination: string;
|
|
40
|
+
previousPage: string;
|
|
41
|
+
property: string;
|
|
42
|
+
range: (start: number, end: number, total: number) => string;
|
|
43
|
+
removeFilter: string;
|
|
44
|
+
resetColumns: string;
|
|
45
|
+
rowDetails: string;
|
|
46
|
+
rows: (count: number) => string;
|
|
47
|
+
rowsPerPage: string;
|
|
48
|
+
search: string;
|
|
49
|
+
selectAllRows: string;
|
|
50
|
+
selectRow: string;
|
|
51
|
+
sort: string;
|
|
52
|
+
sortBy: string;
|
|
53
|
+
views: string;
|
|
54
|
+
visibleColumns: (count: number, total: number) => string;
|
|
55
|
+
};
|
|
9
56
|
export { COLUMN_TAG } from './datagrid-column';
|
|
10
|
-
export type { DataGridView, FilterOperator, FilterOption } from './datagrid-model';
|
|
57
|
+
export type { DataGridColumn, DataGridView, FilterOperator, FilterOption } from './datagrid-model';
|
|
11
58
|
/**
|
|
12
59
|
* Returns the Lucide icon name for a column's sort state.
|
|
13
60
|
* Pure function — no closure dependency on the grid model.
|
|
@@ -104,6 +151,8 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
104
151
|
getRowKey?: (row: T) => string;
|
|
105
152
|
/** Accessible label for the grid. Recommended for screen readers. */
|
|
106
153
|
label?: string;
|
|
154
|
+
/** Localized labels for built-in controls, filters, selection, and pagination. */
|
|
155
|
+
labels?: Partial<DataGridLabels>;
|
|
107
156
|
/** Show a busy/loading state with reduced opacity. */
|
|
108
157
|
loading?: boolean;
|
|
109
158
|
/** Number of rows per page. Defaults to `10`. Set to `0` to disable pagination. */
|
|
@@ -146,11 +195,11 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
146
195
|
/**
|
|
147
196
|
* A reactive data source from `@vielzeug/sourcerer` (or any compatible object).
|
|
148
197
|
* When set, the source drives row data, pagination, and search — the `rows` prop is ignored.
|
|
149
|
-
* Client-side sort and filter are bypassed;
|
|
198
|
+
* Client-side sort and filter are bypassed; update source params from grid events externally.
|
|
150
199
|
* @example
|
|
151
200
|
* ```js
|
|
152
201
|
* import { createPageSource } from '@vielzeug/sourcerer';
|
|
153
|
-
* const source = createPageSource({ load: ({
|
|
202
|
+
* const source = createPageSource({ load: ({ page, pageSize, signal }) => api.users({ page, pageSize }, { signal }) });
|
|
154
203
|
* grid.source = source;
|
|
155
204
|
* ```
|
|
156
205
|
*/
|
|
@@ -159,8 +208,8 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
159
208
|
striped?: boolean;
|
|
160
209
|
/**
|
|
161
210
|
* Named view definitions for the controls bar tab strip.
|
|
162
|
-
* Each view is
|
|
163
|
-
*
|
|
211
|
+
* Each view is displayed as a tab and may provide a client-side row filter.
|
|
212
|
+
* Switching tabs fires `view-change`; the consumer keeps `activeView` controlled.
|
|
164
213
|
* @example
|
|
165
214
|
* ```js
|
|
166
215
|
* grid.views = [
|
|
@@ -171,7 +220,7 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
171
220
|
* grid.activeView = 'all';
|
|
172
221
|
* ```
|
|
173
222
|
*/
|
|
174
|
-
views?: DataGridView[];
|
|
223
|
+
views?: DataGridView<T>[];
|
|
175
224
|
};
|
|
176
225
|
/**
|
|
177
226
|
* An accessible, keyboard-navigable data grid with sorting, pagination,
|
|
@@ -192,6 +241,7 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
192
241
|
* @attr {string} density - Cell density: compact | cozy (default) | comfortable
|
|
193
242
|
* @attr {string} empty-text - Text shown when there are no rows
|
|
194
243
|
* @attr {string} label - Accessible label for the grid
|
|
244
|
+
* @attr {data} labels - Localized built-in control labels and formatters
|
|
195
245
|
* @attr {string} active-view - ID of the currently active view tab
|
|
196
246
|
*
|
|
197
247
|
* @fires selection-change - Fired when row selection changes. detail: { keys: string[], rows: T[] }
|
|
@@ -247,21 +297,23 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
247
297
|
* sourcerer import is required in refine.
|
|
248
298
|
*
|
|
249
299
|
* When `source` is set on the grid:
|
|
250
|
-
* - `rows` prop is ignored; `source.
|
|
251
|
-
* - Pagination reads `source.
|
|
252
|
-
* - Prev/next buttons call `source.
|
|
253
|
-
* - Search calls `source.
|
|
254
|
-
* - `source.
|
|
255
|
-
* - Client-side sort and filter are bypassed;
|
|
300
|
+
* - `rows` prop is ignored; `source.state.items` drives displayed items.
|
|
301
|
+
* - Pagination reads `source.state.pagination`.
|
|
302
|
+
* - Prev/next buttons call `source.previous()` / `source.next()`.
|
|
303
|
+
* - Search calls `source.setParams(search)` when that optional method exists.
|
|
304
|
+
* - `source.state.loading` contributes to the grid's `aria-busy` state.
|
|
305
|
+
* - Client-side sort and filter are bypassed; update source params from grid events externally.
|
|
256
306
|
*
|
|
257
307
|
* @example
|
|
258
308
|
* ```ts
|
|
259
309
|
* import { createPageSource } from '@vielzeug/sourcerer';
|
|
260
310
|
*
|
|
261
311
|
* const source = createPageSource({
|
|
262
|
-
* load: ({
|
|
263
|
-
* fetch(`/api/users?page=${
|
|
264
|
-
*
|
|
312
|
+
* load: async ({ page, pageSize, params: search, signal }) => {
|
|
313
|
+
* const result = await fetch(`/api/users?page=${page}&limit=${pageSize}&search=${search}`, { signal }).then(r => r.json());
|
|
314
|
+
* return { items: result.data, totalItems: result.total };
|
|
315
|
+
* },
|
|
316
|
+
* params: '',
|
|
265
317
|
* });
|
|
266
318
|
*
|
|
267
319
|
* const grid = document.querySelector('ore-datagrid');
|
|
@@ -269,33 +321,23 @@ export type OreDataGridProps<T = Record<string, unknown>> = {
|
|
|
269
321
|
* ```
|
|
270
322
|
*/
|
|
271
323
|
export type DataGridSource<T = Record<string, unknown>> = {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
setQuery?(changes: {
|
|
277
|
-
search?: string;
|
|
278
|
-
}): Promise<void> | void;
|
|
279
|
-
readonly snapshot: {
|
|
280
|
-
readonly data: readonly T[];
|
|
324
|
+
next(): Promise<void> | void;
|
|
325
|
+
previous(): Promise<void> | void;
|
|
326
|
+
setParams?(search: string): Promise<void> | void;
|
|
327
|
+
readonly state: {
|
|
281
328
|
readonly error: {
|
|
282
329
|
message: string;
|
|
283
330
|
} | null;
|
|
284
|
-
readonly
|
|
331
|
+
readonly items: readonly T[];
|
|
332
|
+
readonly loading: boolean;
|
|
285
333
|
readonly pagination: {
|
|
286
|
-
readonly
|
|
287
|
-
readonly
|
|
288
|
-
readonly
|
|
289
|
-
readonly
|
|
290
|
-
readonly kind: 'page';
|
|
291
|
-
readonly size: number;
|
|
292
|
-
readonly total: number;
|
|
293
|
-
};
|
|
294
|
-
readonly query: {
|
|
295
|
-
readonly search?: string;
|
|
334
|
+
readonly page: number;
|
|
335
|
+
readonly pageCount: number;
|
|
336
|
+
readonly pageSize: number;
|
|
337
|
+
readonly totalItems: number;
|
|
296
338
|
};
|
|
297
339
|
};
|
|
298
|
-
subscribe(listener: (
|
|
340
|
+
subscribe(listener: (state: DataGridSource<T>['state']) => void): () => void;
|
|
299
341
|
};
|
|
300
342
|
export declare const DATAGRID_TAG: "ore-datagrid";
|
|
301
343
|
//# sourceMappingURL=datagrid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datagrid.d.ts","sourceRoot":"","sources":["../../../src/inputs/datagrid/datagrid.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"datagrid.d.ts","sourceRoot":"","sources":["../../../src/inputs/datagrid/datagrid.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../core/announcer.cjs");let t=require("@vielzeug/ripple"),n=require("@vielzeug/dnd");var r=e=>{if(e===0)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`,`TB`],r=Math.floor(Math.log(e)/Math.log(t));return`${parseFloat((e/t**r).toFixed(1))} ${n[r]}`},i=(e,t)=>!t||e.size<=t,a=e=>e>0?`${r(e)}/s`:``,o=e=>e?e.split(`,`).map(e=>e.trim()):[],s={error:null,loaded:0,speedBps:0,status:`idle`},c=e=>{if(e.speedBps<=0||e.status!==`uploading`)return``;let t=Math.max(0,Math.round((e.total-e.loaded)/e.speedBps));return t<60?`${t}s left`:`${Math.round(t/60)}m left`};function l(r){let l=(0,t.signal)([]),u=new Map,d=new Map;function f(e){let n=u.get(e);return n||(n=(0,t.signal)({...s,total:e.size}),u.set(e,n)),n}function p(e){return f(e).value}function m(e,t){let n=f(e);n.value={...n.value,...t}}function h(t,n=0){if(r.disabled.value)return;let i=r.upload.value;if(!i)return;d.get(t)?.abort();let a=new AbortController;d.set(t,a),m(t,{error:null,loaded:n,speedBps:0,status:`uploading`,total:t.size});let o=performance.now(),s=n;i(t,{onProgress:(e,n)=>{if(a.signal.aborted)return;let i=performance.now(),c=(i-o)/1e3,l=c>0?Math.max(0,(e-s)/c):0,u=p(t).speedBps,d=u>0?u*.7+l*.3:l;o=i,s=e,m(t,{loaded:e,speedBps:d,total:n||t.size}),r.onUploadProgress(t,e,n||t.size)},resumeFrom:n,signal:a.signal}).then(()=>{a.signal.aborted||(m(t,{loaded:t.size,speedBps:0,status:`success`}),e.announce(`${t.name} uploaded successfully.`,{document:r.document}),r.onUploadSuccess(t))}).catch(n=>{if(a.signal.aborted)return;let i=n instanceof Error?n.message:String(n);m(t,{error:i,speedBps:0,status:`error`}),e.announce(`${t.name} failed to upload: ${i}`,{document:r.document,politeness:`assertive`}),r.onUploadError(t,n)})}function g(e){r.disabled.value||h(e,p(e).loaded)}function _(e){let t=new Set(e);for(let e of u.keys())t.has(e)||(d.get(e)?.abort(),d.delete(e),u.delete(e))}let v=(0,t.watch)(l,_);function y(e,t){if(r.disabled.value)return;let a=r.multiple.value,s=Array.from(e);a||(s=s.slice(0,1));let c=o(r.accept.value),u=r.maxSize.value;s=s.filter(e=>(0,n.matchesAccept)(e,c)&&i(e,u));let d=a?[...l.value]:[],f=[];for(let e of s)d.includes(e)||(d.push(e),f.push(e));let p=r.maxFiles.value;p>0&&d.length>p&&(d=d.slice(0,p)),l.value=d,r.onChange(l.value,t);for(let e of f)l.value.includes(e)&&h(e)}function b(e,t){r.disabled.value||(l.value=l.value.filter(t=>t!==e),r.onRemove(e,l.value,t),r.onChange(l.value,t))}function x(e,t,a){if(r.disabled.value)return!1;let s=o(r.accept.value);return!(0,n.matchesAccept)(t,s)||!i(t,r.maxSize.value)?!1:(l.value=l.value.map(n=>n===e?t:n),r.onRemove(e,l.value,a),r.onChange(l.value,a),h(t),!0)}function S(e){let t=p(e);return t.total<=0?0:Math.min(100,Math.round(t.loaded/t.total*100))}function C(e){let t=p(e);return[a(t.speedBps),c(t)].filter(Boolean).join(` · `)}function w(e){let t=p(e).status;return t===`success`?`check`:t===`error`?`alert-circle`:`file`}function T(){v.dispose();for(let e of d.values())e.abort();d.clear(),u.clear()}return{addFiles:y,dispose:T,fileState:p,files:l,removeFile:b,replaceFile:x,retryUpload:g,statusIconName:w,uploadMetaText:C,uploadPercent:S}}exports.createFileQueue=l,exports.formatBytes=r;
|
|
1
|
+
const e=require("../../core/announcer.cjs");let t=require("@vielzeug/ripple"),n=require("@vielzeug/dnd/drop");var r=e=>{if(e===0)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`,`TB`],r=Math.floor(Math.log(e)/Math.log(t));return`${parseFloat((e/t**r).toFixed(1))} ${n[r]}`},i=(e,t)=>!t||e.size<=t,a=e=>e>0?`${r(e)}/s`:``,o=e=>e?e.split(`,`).map(e=>e.trim()):[],s={error:null,loaded:0,speedBps:0,status:`idle`},c=e=>{if(e.speedBps<=0||e.status!==`uploading`)return``;let t=Math.max(0,Math.round((e.total-e.loaded)/e.speedBps));return t<60?`${t}s left`:`${Math.round(t/60)}m left`};function l(r){let l=(0,t.signal)([]),u=new Map,d=new Map;function f(e){let n=u.get(e);return n||(n=(0,t.signal)({...s,total:e.size}),u.set(e,n)),n}function p(e){return f(e).value}function m(e,t){let n=f(e);n.value={...n.value,...t}}function h(t,n=0){if(r.disabled.value)return;let i=r.upload.value;if(!i)return;d.get(t)?.abort();let a=new AbortController;d.set(t,a),m(t,{error:null,loaded:n,speedBps:0,status:`uploading`,total:t.size});let o=performance.now(),s=n;i(t,{onProgress:(e,n)=>{if(a.signal.aborted)return;let i=performance.now(),c=(i-o)/1e3,l=c>0?Math.max(0,(e-s)/c):0,u=p(t).speedBps,d=u>0?u*.7+l*.3:l;o=i,s=e,m(t,{loaded:e,speedBps:d,total:n||t.size}),r.onUploadProgress(t,e,n||t.size)},resumeFrom:n,signal:a.signal}).then(()=>{a.signal.aborted||(m(t,{loaded:t.size,speedBps:0,status:`success`}),e.announce(`${t.name} uploaded successfully.`,{document:r.document}),r.onUploadSuccess(t))}).catch(n=>{if(a.signal.aborted)return;let i=n instanceof Error?n.message:String(n);m(t,{error:i,speedBps:0,status:`error`}),e.announce(`${t.name} failed to upload: ${i}`,{document:r.document,politeness:`assertive`}),r.onUploadError(t,n)})}function g(e){r.disabled.value||h(e,p(e).loaded)}function _(e){let t=new Set(e);for(let e of u.keys())t.has(e)||(d.get(e)?.abort(),d.delete(e),u.delete(e))}let v=(0,t.watch)(l,_);function y(e,t){if(r.disabled.value)return;let a=r.multiple.value,s=Array.from(e);a||(s=s.slice(0,1));let c=o(r.accept.value),u=r.maxSize.value;s=s.filter(e=>(0,n.matchesAccept)(e,c)&&i(e,u));let d=a?[...l.value]:[],f=[];for(let e of s)d.includes(e)||(d.push(e),f.push(e));let p=r.maxFiles.value;p>0&&d.length>p&&(d=d.slice(0,p)),l.value=d,r.onChange(l.value,t);for(let e of f)l.value.includes(e)&&h(e)}function b(e,t){r.disabled.value||(l.value=l.value.filter(t=>t!==e),r.onRemove(e,l.value,t),r.onChange(l.value,t))}function x(e,t,a){if(r.disabled.value)return!1;let s=o(r.accept.value);return!(0,n.matchesAccept)(t,s)||!i(t,r.maxSize.value)?!1:(l.value=l.value.map(n=>n===e?t:n),r.onRemove(e,l.value,a),r.onChange(l.value,a),h(t),!0)}function S(e){let t=p(e);return t.total<=0?0:Math.min(100,Math.round(t.loaded/t.total*100))}function C(e){let t=p(e);return[a(t.speedBps),c(t)].filter(Boolean).join(` · `)}function w(e){let t=p(e).status;return t===`success`?`check`:t===`error`?`alert-circle`:`file`}function T(){v.dispose();for(let e of d.values())e.abort();d.clear(),u.clear()}return{addFiles:y,dispose:T,fileState:p,files:l,removeFile:b,replaceFile:x,retryUpload:g,statusIconName:w,uploadMetaText:C,uploadPercent:S}}exports.createFileQueue=l,exports.formatBytes=r;
|
|
2
2
|
//# sourceMappingURL=file-input-upload.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-input-upload.cjs","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together — they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain — nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average — the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0 — see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files` — covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n incoming = incoming.filter((f) => matchesAccept(f, acceptList) && isFileSizeAllowed(f, maxSize));\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of incoming) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line — percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"file-input-upload.cjs","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd/drop';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together — they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain — nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average — the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0 — see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files` — covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n incoming = incoming.filter((f) => matchesAccept(f, acceptList) && isFileSizeAllowed(f, maxSize));\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of incoming) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line — percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"8GAOA,IAAa,EAAe,GAA0B,CACpD,GAAI,IAAU,EAAG,MAAO,MAExB,IAAM,EAAI,KACJ,EAAQ,CAAC,IAAK,KAAM,KAAM,KAAM,IAAI,EACpC,EAAI,KAAK,MAAM,KAAK,IAAI,CAAK,EAAI,KAAK,IAAI,CAAC,CAAC,EAElD,MAAO,GAAG,YAAY,EAAQ,GAAK,EAAA,CAAG,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAM,IAC7D,EAEM,GAAqB,EAAY,IAA6B,CAAC,GAAW,EAAK,MAAQ,EAEvF,EAAe,GAAoC,EAAiB,EAAI,GAAG,EAAY,CAAc,EAAE,IAAM,GAE7G,EAAe,GAA0C,EAAS,EAAO,MAAM,GAAG,CAAC,CAAC,IAAK,GAAM,EAAE,KAAK,CAAC,EAAI,CAAC,EAkB5G,EAAoD,CAAE,MAAO,KAAM,OAAQ,EAAG,SAAU,EAAG,OAAQ,MAAO,EAE1G,EAAa,GAAmC,CACpD,GAAI,EAAM,UAAY,GAAK,EAAM,SAAW,YAAa,MAAO,GAEhE,IAAM,EAAmB,KAAK,IAAI,EAAG,KAAK,OAAO,EAAM,MAAQ,EAAM,QAAU,EAAM,QAAQ,CAAC,EAI9F,OAFI,EAAmB,GAAW,GAAG,EAAiB,QAE/C,GAAG,KAAK,MAAM,EAAmB,EAAE,EAAE,OAC9C,EAoEA,SAAgB,EAAgB,EAAsC,CACpE,IAAM,GAAA,EAAQ,EAAA,OAAA,CAAe,CAAC,CAAC,EACzB,EAAe,IAAI,IACnB,EAAc,IAAI,IAExB,SAAS,EAAY,EAAwD,CAC3E,IAAI,EAAI,EAAa,IAAI,CAAI,EAO7B,OALK,IACH,GAAA,EAAI,EAAA,OAAA,CAAwB,CAAE,GAAG,EAAmB,MAAO,EAAK,IAAK,CAAC,EACtE,EAAa,IAAI,EAAM,CAAC,GAGnB,CACT,CAEA,SAAS,EAAU,EAA6B,CAC9C,OAAO,EAAY,CAAI,CAAC,CAAC,KAC3B,CAEA,SAAS,EAAW,EAAY,EAAuC,CACrE,IAAM,EAAI,EAAY,CAAI,EAE1B,EAAE,MAAQ,CAAE,GAAG,EAAE,MAAO,GAAG,CAAM,CACnC,CAIA,SAAS,EAAY,EAAY,EAAa,EAAS,CACrD,GAAI,EAAQ,SAAS,MAAO,OAE5B,IAAM,EAAW,EAAQ,OAAO,MAEhC,GAAI,CAAC,EAAU,OAEf,EAAY,IAAI,CAAI,CAAC,EAAE,MAAM,EAE7B,IAAM,EAAa,IAAI,gBAEvB,EAAY,IAAI,EAAM,CAAU,EAChC,EAAW,EAAM,CAAE,MAAO,KAAM,OAAQ,EAAY,SAAU,EAAG,OAAQ,YAAa,MAAO,EAAK,IAAK,CAAC,EAKxG,IAAI,EAAe,YAAY,IAAI,EAC/B,EAAmB,EAEvB,EAAS,EAAM,CACb,YAAa,EAAQ,IAAU,CAC7B,GAAI,EAAW,OAAO,QAAS,OAE/B,IAAM,EAAM,YAAY,IAAI,EACtB,GAAkB,EAAM,GAAgB,IACxC,EAAa,EAAiB,EAAI,KAAK,IAAI,GAAI,EAAS,GAAoB,CAAc,EAAI,EAC9F,EAAc,EAAU,CAAI,CAAC,CAAC,SAC9B,EAAW,EAAc,EAAI,EAAc,GAAM,EAAa,GAAM,EAE1E,EAAe,EACf,EAAmB,EACnB,EAAW,EAAM,CAAE,SAAQ,WAAU,MAAO,GAAS,EAAK,IAAK,CAAC,EAChE,EAAQ,iBAAiB,EAAM,EAAQ,GAAS,EAAK,IAAI,CAC3D,EACA,aACA,OAAQ,EAAW,MACrB,CAAC,CAAC,CACC,SAAW,CACN,EAAW,OAAO,UAEtB,EAAW,EAAM,CAAE,OAAQ,EAAK,KAAM,SAAU,EAAG,OAAQ,SAAU,CAAC,EACtE,EAAA,SAAS,GAAG,EAAK,KAAK,yBAA0B,CAAE,SAAU,EAAQ,QAAS,CAAC,EAC9E,EAAQ,gBAAgB,CAAI,EAC9B,CAAC,CAAC,CACD,MAAO,GAAmB,CACzB,GAAI,EAAW,OAAO,QAAS,OAE/B,IAAM,EAAU,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EAErE,EAAW,EAAM,CAAE,MAAO,EAAS,SAAU,EAAG,OAAQ,OAAQ,CAAC,EACjE,EAAA,SAAS,GAAG,EAAK,KAAK,qBAAqB,IAAW,CACpD,SAAU,EAAQ,SAClB,WAAY,WACd,CAAC,EACD,EAAQ,cAAc,EAAM,CAAK,CACnC,CAAC,CACL,CAKA,SAAS,EAAY,EAAkB,CACjC,EAAQ,SAAS,OAErB,EAAY,EAAM,EAAU,CAAI,CAAC,CAAC,MAAM,CAC1C,CAMA,SAAS,EAAkB,EAA2B,CACpD,IAAM,EAAO,IAAI,IAAI,CAAW,EAEhC,IAAK,IAAM,KAAQ,EAAa,KAAK,EAC/B,EAAK,IAAI,CAAI,IAEjB,EAAY,IAAI,CAAI,CAAC,EAAE,MAAM,EAC7B,EAAY,OAAO,CAAI,EACvB,EAAa,OAAO,CAAI,EAE5B,CAEA,IAAM,GAAA,EAAa,EAAA,MAAA,CAAM,EAAO,CAAiB,EAEjD,SAAS,EAAS,EAAkB,EAA6B,CAC/D,GAAI,EAAQ,SAAS,MAAO,OAE5B,IAAM,EAAa,EAAQ,SAAS,MAChC,EAAW,MAAM,KAAK,CAAQ,EAE7B,IAAY,EAAW,EAAS,MAAM,EAAG,CAAC,GAE/C,IAAM,EAAa,EAAY,EAAQ,OAAO,KAAK,EAC7C,EAAU,EAAQ,QAAQ,MAEhC,EAAW,EAAS,OAAQ,IAAA,EAAM,EAAA,cAAA,CAAc,EAAG,CAAU,GAAK,EAAkB,EAAG,CAAO,CAAC,EAE/F,IAAI,EAAkB,EAAa,CAAC,GAAG,EAAM,KAAK,EAAI,CAAC,EACjD,EAAgB,CAAC,EAEvB,IAAK,IAAM,KAAK,EACT,EAAQ,SAAS,CAAC,IACrB,EAAQ,KAAK,CAAC,EACd,EAAM,KAAK,CAAC,GAIhB,IAAM,EAAW,EAAQ,SAAS,MAE9B,EAAW,GAAK,EAAQ,OAAS,IAAU,EAAU,EAAQ,MAAM,EAAG,CAAQ,GAElF,EAAM,MAAQ,EACd,EAAQ,SAAS,EAAM,MAAO,CAAa,EAE3C,IAAK,IAAM,KAAK,EACV,EAAM,MAAM,SAAS,CAAC,GAAG,EAAY,CAAC,CAE9C,CAEA,SAAS,EAAW,EAAY,EAA6B,CACvD,EAAQ,SAAS,QAErB,EAAM,MAAQ,EAAM,MAAM,OAAQ,GAAM,IAAM,CAAI,EAClD,EAAQ,SAAS,EAAM,EAAM,MAAO,CAAa,EACjD,EAAQ,SAAS,EAAM,MAAO,CAAa,EAC7C,CAEA,SAAS,EAAY,EAAe,EAAe,EAAgC,CACjF,GAAI,EAAQ,SAAS,MAAO,MAAO,GAEnC,IAAM,EAAa,EAAY,EAAQ,OAAO,KAAK,EASnD,MAPI,EAAA,EAAC,EAAA,cAAA,CAAc,EAAS,CAAU,GAAK,CAAC,EAAkB,EAAS,EAAQ,QAAQ,KAAK,EAAU,IAEtG,EAAM,MAAQ,EAAM,MAAM,IAAK,GAAO,IAAM,EAAU,EAAU,CAAE,EAClE,EAAQ,SAAS,EAAS,EAAM,MAAO,CAAa,EACpD,EAAQ,SAAS,EAAM,MAAO,CAAa,EAC3C,EAAY,CAAO,EAEZ,GACT,CAEA,SAAS,EAAc,EAAoB,CACzC,IAAM,EAAQ,EAAU,CAAI,EAI5B,OAFI,EAAM,OAAS,EAAU,EAEtB,KAAK,IAAI,IAAK,KAAK,MAAO,EAAM,OAAS,EAAM,MAAS,GAAG,CAAC,CACrE,CAIA,SAAS,EAAe,EAAoB,CAC1C,IAAM,EAAQ,EAAU,CAAI,EAE5B,MAAO,CAAC,EAAY,EAAM,QAAQ,EAAG,EAAU,CAAK,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK,CACnF,CAEA,SAAS,EAAe,EAAoB,CAC1C,IAAM,EAAS,EAAU,CAAI,CAAC,CAAC,OAM/B,OAJI,IAAW,UAAkB,QAE7B,IAAW,QAAgB,eAExB,MACT,CAEA,SAAS,GAAgB,CACvB,EAAW,QAAQ,EAEnB,IAAK,IAAM,KAAc,EAAY,OAAO,EAAG,EAAW,MAAM,EAEhE,EAAY,MAAM,EAClB,EAAa,MAAM,CACrB,CAEA,MAAO,CACL,WACA,UACA,YACA,QACA,aACA,cACA,cACA,iBACA,iBACA,eACF,CACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { announce as e } from "../../core/announcer.js";
|
|
2
2
|
import { signal as t, watch as n } from "@vielzeug/ripple";
|
|
3
|
-
import { matchesAccept as r } from "@vielzeug/dnd";
|
|
3
|
+
import { matchesAccept as r } from "@vielzeug/dnd/drop";
|
|
4
4
|
//#region src/inputs/file-input/file-input-upload.ts
|
|
5
5
|
var i = (e) => {
|
|
6
6
|
if (e === 0) return "0 B";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-input-upload.js","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together — they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain — nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average — the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0 — see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files` — covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n incoming = incoming.filter((f) => matchesAccept(f, acceptList) && isFileSizeAllowed(f, maxSize));\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of incoming) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line — percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":";;;;AAOA,IAAa,KAAe,MAA0B;CACpD,IAAI,MAAU,GAAG,OAAO;CAExB,IAAM,IAAI,MACJ,IAAQ;EAAC;EAAK;EAAM;EAAM;EAAM;CAAI,GACpC,IAAI,KAAK,MAAM,KAAK,IAAI,CAAK,IAAI,KAAK,IAAI,CAAC,CAAC;CAElD,OAAO,GAAG,YAAY,IAAQ,KAAK,EAAA,CAAG,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAM;AAC7D,GAEM,KAAqB,GAAY,MAA6B,CAAC,KAAW,EAAK,QAAQ,GAEvF,KAAe,MAAoC,IAAiB,IAAI,GAAG,EAAY,CAAc,EAAE,MAAM,IAE7G,KAAe,MAA0C,IAAS,EAAO,MAAM,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,GAkB5G,IAAoD;CAAE,OAAO;CAAM,QAAQ;CAAG,UAAU;CAAG,QAAQ;AAAO,GAE1G,KAAa,MAAmC;CACpD,IAAI,EAAM,YAAY,KAAK,EAAM,WAAW,aAAa,OAAO;CAEhE,IAAM,IAAmB,KAAK,IAAI,GAAG,KAAK,OAAO,EAAM,QAAQ,EAAM,UAAU,EAAM,QAAQ,CAAC;CAI9F,OAFI,IAAmB,KAAW,GAAG,EAAiB,UAE/C,GAAG,KAAK,MAAM,IAAmB,EAAE,EAAE;AAC9C;AAoEA,SAAgB,EAAgB,GAAsC;CACpE,IAAM,IAAQ,EAAe,CAAC,CAAC,GACzB,oBAAe,IAAI,IAAsD,GACzE,oBAAc,IAAI,IAA2B;CAEnD,SAAS,EAAY,GAAwD;EAC3E,IAAI,IAAI,EAAa,IAAI,CAAI;EAO7B,OALK,MACH,IAAI,EAAwB;GAAE,GAAG;GAAmB,OAAO,EAAK;EAAK,CAAC,GACtE,EAAa,IAAI,GAAM,CAAC,IAGnB;CACT;CAEA,SAAS,EAAU,GAA6B;EAC9C,OAAO,EAAY,CAAI,CAAC,CAAC;CAC3B;CAEA,SAAS,EAAW,GAAY,GAAuC;EACrE,IAAM,IAAI,EAAY,CAAI;EAE1B,EAAE,QAAQ;GAAE,GAAG,EAAE;GAAO,GAAG;EAAM;CACnC;CAIA,SAAS,EAAY,GAAY,IAAa,GAAS;EACrD,IAAI,EAAQ,SAAS,OAAO;EAE5B,IAAM,IAAW,EAAQ,OAAO;EAEhC,IAAI,CAAC,GAAU;EAEf,EAAY,IAAI,CAAI,CAAC,EAAE,MAAM;EAE7B,IAAM,IAAa,IAAI,gBAAgB;EAGvC,AADA,EAAY,IAAI,GAAM,CAAU,GAChC,EAAW,GAAM;GAAE,OAAO;GAAM,QAAQ;GAAY,UAAU;GAAG,QAAQ;GAAa,OAAO,EAAK;EAAK,CAAC;EAKxG,IAAI,IAAe,YAAY,IAAI,GAC/B,IAAmB;EAEvB,EAAS,GAAM;GACb,aAAa,GAAQ,MAAU;IAC7B,IAAI,EAAW,OAAO,SAAS;IAE/B,IAAM,IAAM,YAAY,IAAI,GACtB,KAAkB,IAAM,KAAgB,KACxC,IAAa,IAAiB,IAAI,KAAK,IAAI,IAAI,IAAS,KAAoB,CAAc,IAAI,GAC9F,IAAc,EAAU,CAAI,CAAC,CAAC,UAC9B,IAAW,IAAc,IAAI,IAAc,KAAM,IAAa,KAAM;IAK1E,AAHA,IAAe,GACf,IAAmB,GACnB,EAAW,GAAM;KAAE;KAAQ;KAAU,OAAO,KAAS,EAAK;IAAK,CAAC,GAChE,EAAQ,iBAAiB,GAAM,GAAQ,KAAS,EAAK,IAAI;GAC3D;GACA;GACA,QAAQ,EAAW;EACrB,CAAC,CAAC,CACC,WAAW;GACN,EAAW,OAAO,YAEtB,EAAW,GAAM;IAAE,QAAQ,EAAK;IAAM,UAAU;IAAG,QAAQ;GAAU,CAAC,GACtE,EAAS,GAAG,EAAK,KAAK,0BAA0B,EAAE,UAAU,EAAQ,SAAS,CAAC,GAC9E,EAAQ,gBAAgB,CAAI;EAC9B,CAAC,CAAC,CACD,OAAO,MAAmB;GACzB,IAAI,EAAW,OAAO,SAAS;GAE/B,IAAM,IAAU,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;GAOrE,AALA,EAAW,GAAM;IAAE,OAAO;IAAS,UAAU;IAAG,QAAQ;GAAQ,CAAC,GACjE,EAAS,GAAG,EAAK,KAAK,qBAAqB,KAAW;IACpD,UAAU,EAAQ;IAClB,YAAY;GACd,CAAC,GACD,EAAQ,cAAc,GAAM,CAAK;EACnC,CAAC;CACL;CAKA,SAAS,EAAY,GAAkB;EACjC,EAAQ,SAAS,SAErB,EAAY,GAAM,EAAU,CAAI,CAAC,CAAC,MAAM;CAC1C;CAMA,SAAS,EAAkB,GAA2B;EACpD,IAAM,IAAO,IAAI,IAAI,CAAW;EAEhC,KAAK,IAAM,KAAQ,EAAa,KAAK,GAC/B,EAAK,IAAI,CAAI,MAEjB,EAAY,IAAI,CAAI,CAAC,EAAE,MAAM,GAC7B,EAAY,OAAO,CAAI,GACvB,EAAa,OAAO,CAAI;CAE5B;CAEA,IAAM,IAAa,EAAM,GAAO,CAAiB;CAEjD,SAAS,EAAS,GAAkB,GAA6B;EAC/D,IAAI,EAAQ,SAAS,OAAO;EAE5B,IAAM,IAAa,EAAQ,SAAS,OAChC,IAAW,MAAM,KAAK,CAAQ;EAElC,AAAK,MAAY,IAAW,EAAS,MAAM,GAAG,CAAC;EAE/C,IAAM,IAAa,EAAY,EAAQ,OAAO,KAAK,GAC7C,IAAU,EAAQ,QAAQ;EAEhC,IAAW,EAAS,QAAQ,MAAM,EAAc,GAAG,CAAU,KAAK,EAAkB,GAAG,CAAO,CAAC;EAE/F,IAAI,IAAkB,IAAa,CAAC,GAAG,EAAM,KAAK,IAAI,CAAC,GACjD,IAAgB,CAAC;EAEvB,KAAK,IAAM,KAAK,GACd,AAAK,EAAQ,SAAS,CAAC,MACrB,EAAQ,KAAK,CAAC,GACd,EAAM,KAAK,CAAC;EAIhB,IAAM,IAAW,EAAQ,SAAS;EAKlC,AAHI,IAAW,KAAK,EAAQ,SAAS,MAAU,IAAU,EAAQ,MAAM,GAAG,CAAQ,IAElF,EAAM,QAAQ,GACd,EAAQ,SAAS,EAAM,OAAO,CAAa;EAE3C,KAAK,IAAM,KAAK,GACd,AAAI,EAAM,MAAM,SAAS,CAAC,KAAG,EAAY,CAAC;CAE9C;CAEA,SAAS,EAAW,GAAY,GAA6B;EACvD,EAAQ,SAAS,UAErB,EAAM,QAAQ,EAAM,MAAM,QAAQ,MAAM,MAAM,CAAI,GAClD,EAAQ,SAAS,GAAM,EAAM,OAAO,CAAa,GACjD,EAAQ,SAAS,EAAM,OAAO,CAAa;CAC7C;CAEA,SAAS,EAAY,GAAe,GAAe,GAAgC;EACjF,IAAI,EAAQ,SAAS,OAAO,OAAO;EAEnC,IAAM,IAAa,EAAY,EAAQ,OAAO,KAAK;EASnD,OAPI,CAAC,EAAc,GAAS,CAAU,KAAK,CAAC,EAAkB,GAAS,EAAQ,QAAQ,KAAK,IAAU,MAEtG,EAAM,QAAQ,EAAM,MAAM,KAAK,MAAO,MAAM,IAAU,IAAU,CAAE,GAClE,EAAQ,SAAS,GAAS,EAAM,OAAO,CAAa,GACpD,EAAQ,SAAS,EAAM,OAAO,CAAa,GAC3C,EAAY,CAAO,GAEZ;CACT;CAEA,SAAS,EAAc,GAAoB;EACzC,IAAM,IAAQ,EAAU,CAAI;EAI5B,OAFI,EAAM,SAAS,IAAU,IAEtB,KAAK,IAAI,KAAK,KAAK,MAAO,EAAM,SAAS,EAAM,QAAS,GAAG,CAAC;CACrE;CAIA,SAAS,EAAe,GAAoB;EAC1C,IAAM,IAAQ,EAAU,CAAI;EAE5B,OAAO,CAAC,EAAY,EAAM,QAAQ,GAAG,EAAU,CAAK,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CACnF;CAEA,SAAS,EAAe,GAAoB;EAC1C,IAAM,IAAS,EAAU,CAAI,CAAC,CAAC;EAM/B,OAJI,MAAW,YAAkB,UAE7B,MAAW,UAAgB,iBAExB;CACT;CAEA,SAAS,IAAgB;EACvB,EAAW,QAAQ;EAEnB,KAAK,IAAM,KAAc,EAAY,OAAO,GAAG,EAAW,MAAM;EAGhE,AADA,EAAY,MAAM,GAClB,EAAa,MAAM;CACrB;CAEA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"file-input-upload.js","names":[],"sources":["../../../src/inputs/file-input/file-input-upload.ts"],"sourcesContent":["import { matchesAccept } from '@vielzeug/dnd/drop';\nimport { type Readable, signal, watch } from '@vielzeug/ripple';\n\nimport { announce } from '../../core';\n\n// ── Byte/speed formatting ────────────────────────────────────────────────────\n\nexport const formatBytes = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n};\n\nconst isFileSizeAllowed = (file: File, maxSize: number): boolean => !maxSize || file.size <= maxSize;\n\nconst formatSpeed = (bytesPerSecond: number): string => (bytesPerSecond > 0 ? `${formatBytes(bytesPerSecond)}/s` : '');\n\nconst parseAccept = (accept: string | undefined): string[] => (accept ? accept.split(',').map((t) => t.trim()) : []);\n\n// ── Upload lifecycle types ───────────────────────────────────────────────────\n\n/** Upload lifecycle status for a single file. `idle` covers both \"no `upload` handler wired up\"\n * (picker-only mode, fully supported) and \"not started yet\". */\nexport type FileUploadStatus = 'error' | 'idle' | 'success' | 'uploading';\n\n/** Per-file upload snapshot. One reactive signal per file — see `createFileQueue` — not one\n * signal holding a map, so a progress tick for one file never invalidates another's bindings. */\nexport type FileUploadState = {\n error: string | null;\n loaded: number;\n speedBps: number;\n status: FileUploadStatus;\n total: number;\n};\n\nconst IDLE_UPLOAD_STATE: Omit<FileUploadState, 'total'> = { error: null, loaded: 0, speedBps: 0, status: 'idle' };\n\nconst formatEta = (state: FileUploadState): string => {\n if (state.speedBps <= 0 || state.status !== 'uploading') return '';\n\n const remainingSeconds = Math.max(0, Math.round((state.total - state.loaded) / state.speedBps));\n\n if (remainingSeconds < 60) return `${remainingSeconds}s left`;\n\n return `${Math.round(remainingSeconds / 60)}m left`;\n};\n\n/**\n * Consumer-supplied transport — this component has no built-in network layer (zero-dependency,\n * transport-agnostic by design, same reasoning as `courier`'s own fetch-wrapping-only stance).\n * This function is the seam between the UI's upload lifecycle (progress, retry, per-file\n * isolation) and however the app actually moves bytes (`fetch`, `XMLHttpRequest`, a presigned S3\n * PUT, `@vielzeug/courier`, …).\n *\n * `resumeFrom` is a *hint*, not a guarantee: it's the byte offset the component last observed\n * via `onProgress` before the previous attempt failed (0 on a fresh upload). A transport that\n * supports byte ranges (e.g. an HTTP `Range`/`Content-Range` header, S3 multipart, tus) can use\n * it to genuinely resume from that point; a plain `fetch(url, { body: file })` can ignore it and\n * simply re-send the whole file — the UI still shows a resumed-from-N-bytes experience either\n * way, since the file itself was never dropped from memory.\n */\nexport type FileUploadFn = (\n file: File,\n ctx: { onProgress: (loaded: number, total: number) => void; resumeFrom: number; signal: AbortSignal },\n) => Promise<void>;\n\n// ── Queue ─────────────────────────────────────────────────────────────────────\n\nexport type FileQueueOptions = {\n accept: Readable<string | undefined>;\n disabled: Readable<boolean>;\n /** Document whose `<body>` hosts the announcer live region. Defaults to the global `document`. */\n document?: Document;\n maxFiles: Readable<number>;\n maxSize: Readable<number>;\n multiple: Readable<boolean>;\n onChange: (files: File[], originalEvent?: Event) => void;\n onRemove: (file: File, files: File[], originalEvent?: Event) => void;\n onUploadError: (file: File, error: unknown) => void;\n onUploadProgress: (file: File, loaded: number, total: number) => void;\n onUploadSuccess: (file: File) => void;\n upload: Readable<FileUploadFn | undefined>;\n};\n\nexport type FileQueue = {\n addFiles(newFiles: File[], originalEvent?: Event): void;\n dispose(): void;\n readonly files: Readable<File[]>;\n fileState(file: File): FileUploadState;\n removeFile(file: File, originalEvent?: Event): void;\n /** Validates `newFile` (accept/max-size) and, if accepted, swaps it in for `oldFile` in place\n * and starts its upload. Returns whether the replacement was accepted. Also fires `onRemove`\n * for `oldFile` — a replace is a removal-and-add that happen to land in the same slot, and a\n * consumer listening only for removal (e.g. to clean up a server-side record) still needs to\n * hear that the old file is gone. */\n replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean;\n retryUpload(file: File): void;\n statusIconName(file: File): string;\n uploadMetaText(file: File): string;\n uploadPercent(file: File): number;\n};\n\n/**\n * Owns file selection *and* the opt-in upload lifecycle together — they were never really\n * separable (adding a file starts its upload; removing one must abort it; replacing one is both\n * at once). Independent of `html`/rendering, following the same shape as `datagrid`'s\n * `createDataGridControls` and `combobox`'s `combobox-options.ts`: a factory `file-input.ts`\n * wires into drag/drop/native-input events and the template, directly unit-testable without\n * mounting a component.\n *\n * Every file-level rule (accept, max-size, max-files, disabled) and every upload-lifecycle rule\n * (progress, retry-from-offset, per-file isolation) lives here in one place.\n */\nexport function createFileQueue(options: FileQueueOptions): FileQueue {\n const files = signal<File[]>([]);\n const stateSignals = new Map<File, ReturnType<typeof signal<FileUploadState>>>();\n const controllers = new Map<File, AbortController>();\n\n function stateSignal(file: File): ReturnType<typeof signal<FileUploadState>> {\n let s = stateSignals.get(file);\n\n if (!s) {\n s = signal<FileUploadState>({ ...IDLE_UPLOAD_STATE, total: file.size });\n stateSignals.set(file, s);\n }\n\n return s;\n }\n\n function fileState(file: File): FileUploadState {\n return stateSignal(file).value;\n }\n\n function patchState(file: File, patch: Partial<FileUploadState>): void {\n const s = stateSignal(file);\n\n s.value = { ...s.value, ...patch };\n }\n\n // Each file gets its own `AbortController` and its own promise chain — nothing here `await`s\n // another file's upload, so one file's rejection can never block, freeze, or cancel the others.\n function startUpload(file: File, resumeFrom = 0): void {\n if (options.disabled.value) return;\n\n const uploadFn = options.upload.value;\n\n if (!uploadFn) return;\n\n controllers.get(file)?.abort();\n\n const controller = new AbortController();\n\n controllers.set(file, controller);\n patchState(file, { error: null, loaded: resumeFrom, speedBps: 0, status: 'uploading', total: file.size });\n\n // Rolling (exponential-moving-average) speed estimate rather than a naive\n // `loaded / elapsedSinceStart` average — the latter drags heavily behind the file's actual\n // *current* rate right after a slow start or a mid-transfer stall.\n let lastSampleAt = performance.now();\n let lastSampleLoaded = resumeFrom;\n\n uploadFn(file, {\n onProgress: (loaded, total) => {\n if (controller.signal.aborted) return;\n\n const now = performance.now();\n const elapsedSeconds = (now - lastSampleAt) / 1000;\n const instantBps = elapsedSeconds > 0 ? Math.max(0, (loaded - lastSampleLoaded) / elapsedSeconds) : 0;\n const previousBps = fileState(file).speedBps;\n const speedBps = previousBps > 0 ? previousBps * 0.7 + instantBps * 0.3 : instantBps;\n\n lastSampleAt = now;\n lastSampleLoaded = loaded;\n patchState(file, { loaded, speedBps, total: total || file.size });\n options.onUploadProgress(file, loaded, total || file.size);\n },\n resumeFrom,\n signal: controller.signal,\n })\n .then(() => {\n if (controller.signal.aborted) return;\n\n patchState(file, { loaded: file.size, speedBps: 0, status: 'success' });\n announce(`${file.name} uploaded successfully.`, { document: options.document });\n options.onUploadSuccess(file);\n })\n .catch((error: unknown) => {\n if (controller.signal.aborted) return;\n\n const message = error instanceof Error ? error.message : String(error);\n\n patchState(file, { error: message, speedBps: 0, status: 'error' });\n announce(`${file.name} failed to upload: ${message}`, {\n document: options.document,\n politeness: 'assertive',\n });\n options.onUploadError(file, error);\n });\n }\n\n // Resumes from the last byte offset this file actually reached, not from 0 — see\n // `FileUploadFn`'s doc comment on what \"resume\" honestly means when the transport is\n // consumer-supplied.\n function retryUpload(file: File): void {\n if (options.disabled.value) return;\n\n startUpload(file, fileState(file).loaded);\n }\n\n // Aborts + drops upload state for any file no longer in `files` — covers plain removal, a\n // non-`multiple` selection replacing the previous file, and `replaceFile()` swapping one File\n // for another. One cleanup path instead of duplicating it at every call site that can shrink\n // `files`.\n function dropOrphanedState(activeFiles: File[]): void {\n const keep = new Set(activeFiles);\n\n for (const file of stateSignals.keys()) {\n if (keep.has(file)) continue;\n\n controllers.get(file)?.abort();\n controllers.delete(file);\n stateSignals.delete(file);\n }\n }\n\n const filesWatch = watch(files, dropOrphanedState);\n\n function addFiles(newFiles: File[], originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n const isMultiple = options.multiple.value;\n let incoming = Array.from(newFiles);\n\n if (!isMultiple) incoming = incoming.slice(0, 1);\n\n const acceptList = parseAccept(options.accept.value);\n const maxSize = options.maxSize.value;\n\n incoming = incoming.filter((f) => matchesAccept(f, acceptList) && isFileSizeAllowed(f, maxSize));\n\n let updated: File[] = isMultiple ? [...files.value] : [];\n const added: File[] = [];\n\n for (const f of incoming) {\n if (!updated.includes(f)) {\n updated.push(f);\n added.push(f);\n }\n }\n\n const maxFiles = options.maxFiles.value;\n\n if (maxFiles > 0 && updated.length > maxFiles) updated = updated.slice(0, maxFiles);\n\n files.value = updated;\n options.onChange(files.value, originalEvent);\n\n for (const f of added) {\n if (files.value.includes(f)) startUpload(f);\n }\n }\n\n function removeFile(file: File, originalEvent?: Event): void {\n if (options.disabled.value) return;\n\n files.value = files.value.filter((f) => f !== file);\n options.onRemove(file, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n }\n\n function replaceFile(oldFile: File, newFile: File, originalEvent?: Event): boolean {\n if (options.disabled.value) return false;\n\n const acceptList = parseAccept(options.accept.value);\n\n if (!matchesAccept(newFile, acceptList) || !isFileSizeAllowed(newFile, options.maxSize.value)) return false;\n\n files.value = files.value.map((f) => (f === oldFile ? newFile : f));\n options.onRemove(oldFile, files.value, originalEvent);\n options.onChange(files.value, originalEvent);\n startUpload(newFile);\n\n return true;\n }\n\n function uploadPercent(file: File): number {\n const state = fileState(file);\n\n if (state.total <= 0) return 0;\n\n return Math.min(100, Math.round((state.loaded / state.total) * 100));\n }\n\n // Speed + ETA on one line — percent itself is rendered separately (e.g. `<ore-progress>`'s\n // own `label`), so this only needs the two metrics that wouldn't otherwise fit there.\n function uploadMetaText(file: File): string {\n const state = fileState(file);\n\n return [formatSpeed(state.speedBps), formatEta(state)].filter(Boolean).join(' · ');\n }\n\n function statusIconName(file: File): string {\n const status = fileState(file).status;\n\n if (status === 'success') return 'check';\n\n if (status === 'error') return 'alert-circle';\n\n return 'file';\n }\n\n function dispose(): void {\n filesWatch.dispose();\n\n for (const controller of controllers.values()) controller.abort();\n\n controllers.clear();\n stateSignals.clear();\n }\n\n return {\n addFiles,\n dispose,\n fileState,\n files,\n removeFile,\n replaceFile,\n retryUpload,\n statusIconName,\n uploadMetaText,\n uploadPercent,\n };\n}\n"],"mappings":"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"}
|
package/dist/inputs/index.d.ts
CHANGED
|
@@ -9,10 +9,9 @@ export type { OreCheckboxEvents, OreCheckboxProps } from './checkbox/checkbox';
|
|
|
9
9
|
export { CHECKBOX_TAG } from './checkbox/checkbox';
|
|
10
10
|
export type { OreCheckboxGroupEvents, OreCheckboxGroupProps } from './checkbox-group/checkbox-group';
|
|
11
11
|
export { CHECKBOX_GROUP_CTX, CHECKBOX_GROUP_TAG } from './checkbox-group/checkbox-group';
|
|
12
|
-
export type { OreComboboxEvents, OreComboboxProps } from './combobox/combobox';
|
|
13
|
-
export { COMBOBOX_TAG } from './combobox/combobox';
|
|
14
|
-
export type {
|
|
15
|
-
export type { DataGridSource, OreDataGridEvents, OreDataGridProps } from './datagrid/datagrid';
|
|
12
|
+
export type { ComboboxOptionInput, OreComboboxEvents, OreComboboxOptionProps, OreComboboxProps, } from './combobox/combobox';
|
|
13
|
+
export { COMBOBOX_OPTION_TAG, COMBOBOX_TAG } from './combobox/combobox';
|
|
14
|
+
export type { DataGridColumn, DataGridLabels, DataGridSource, DataGridView, OreDataGridEvents, OreDataGridProps, } from './datagrid/datagrid';
|
|
16
15
|
export { DATAGRID_TAG } from './datagrid/datagrid';
|
|
17
16
|
export type { OreDatePickerEvents, OreDatePickerProps } from './date-picker/date-picker';
|
|
18
17
|
export { DATE_PICKER_TAG } from './date-picker/date-picker';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inputs/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACrF,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AACjF,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC9F,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACrG,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACzF,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inputs/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=`@layer refine.base{:host{--_font-size:var(--input-font-size,var(--text-sm));--_gap:var(--input-gap,var(--size-2));--_field-height:var(--input-height,var(--size-10));--_padding:var(--input-padding,var(--size-1-5) var(--size-3));--_label-block-inset:var(--input-label-block-inset,var(--size-2));--_label-inset:var(--input-label-inset,var(--size-3));--_radius:var(--input-radius,var(--rounded-lg));--_placeholder:var(--input-placeholder-color,var(--color-contrast-500));--_bg:var(--input-bg,var(--color-contrast-100));--_border-color:var(--input-border-color,var(--color-contrast-300));min-width:var(--input-min-width,12rem);flex-direction:column;align-items:stretch;display:inline-flex}.input-wrapper{flex-direction:column;gap:0;width:100%;display:flex}.field{box-sizing:border-box;height:var(--_field-height);min-height:var(--_field-height);padding:var(--_padding);background:var(--_bg);border:var(--border) solid var(--_border-color);border-radius:var(--_radius);box-shadow:var(--shadow-2xs);transition:var(--_motion-transition,background var(--transition-fast), backdrop-filter var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast));flex-direction:column;justify-content:center;align-items:stretch;gap:0;display:flex}.input-wrapper:has(.label:not([hidden])) .field{height:auto}:host(:not([label-placement=outside])) .input-wrapper{position:relative}:host(:not([label-placement=outside])) .label{z-index:1;pointer-events:none;top:var(--_label-block-inset);position:absolute}:host(:not([label-placement=outside])) .label:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){left:var(--_label-inset)}:host(:not([label-placement=outside])) .label:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){right:var(--_label-inset)}:host(:not([label-placement=outside])) .input-wrapper:has(.label:not([hidden])) .field{padding-top:calc(var(--_label-block-inset) + 1lh + 2px)}.input-row{gap:var(--_gap);flex:1;align-items:center;display:flex}.label,label.label{text-overflow:ellipsis;font-size:var(--text-xs);font-weight:var(--font-medium);line-height:var(--leading-tight);color:var(--color-contrast-500);white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;transition:var(--_motion-transition,color var(--transition-fast));margin-bottom:2px;overflow:hidden}:host([label-placement=outside]) .label,:host([label-placement=outside]) label.label{font-size:var(--text-xs);line-height:var(--leading-loose);order:-1}.helper-text{margin-top:var(--size-1-5);font-size:var(--text-xs);line-height:var(--leading-tight);color:color-mix(in oklch, var(--color-contrast-500) 80%, var(--color-canvas));overflow-wrap:anywhere;padding-left:2px;padding-right:2px}.helper-text[hidden]{display:none}::slotted(svg){width:var(--_icon-size,var(--leading-6));height:var(--_icon-size,var(--leading-6));flex-shrink:0}}@layer refine.overrides{input{min-width:0;font:inherit;font-size:var(--_font-size);line-height:var(--leading-normal);color:var(--color-contrast-900);background:0 0;border:none;outline:none;flex:1}input::placeholder{color:var(--_placeholder);transition:color var(--transition-fast)}input:focus-visible{outline:none}:host(:not([disabled],[variant=bordered],[variant=flat])) .field:hover{border-color:var(--color-contrast-400)}:host(:not([disabled])) .field:focus-within .label,:host(:not([disabled])) .field:focus-within ::slotted([slot=prefix]),:host(:not([disabled])) .field:focus-within ::slotted([slot=suffix]){color:var(--_theme-focus)}.clear-btn{width:var(--size-5);min-width:var(--_touch-target);height:var(--size-5);min-height:var(--_touch-target);color:var(--color-contrast-500);cursor:pointer;border-radius:var(--rounded-sm);transition:var(--_motion-transition,color var(--transition-fast));background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:none}.clear-btn:hover{color:var(--color-contrast-900)}.clear-btn:focus-visible{outline:var(--border-2) solid var(--_theme-focus);outline-offset:var(--border)}:host([clearable]) .clear-btn{display:flex}:host([clearable]:not([has-value])) .clear-btn{visibility:hidden;pointer-events:none}.pwd-toggle-btn{width:var(--size-5);min-width:var(--_touch-target);height:var(--size-5);min-height:var(--_touch-target);color:var(--color-contrast-500);cursor:pointer;border-radius:var(--rounded-sm);transition:var(--_motion-transition,color var(--transition-fast));background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:none}.pwd-toggle-btn:hover{color:var(--color-contrast-900)}.pwd-toggle-btn:focus-visible{outline:var(--border-2) solid var(--_theme-focus);outline-offset:var(--border)}:host([type=password]) .pwd-toggle-btn{display:flex}.char-counter{font-size:var(--text-xs);line-height:var(--leading-tight);color:var(--color-contrast-400);white-space:nowrap;padding-left:2px;padding-right:2px}.char-counter:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){text-align:right}.char-counter:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){text-align:left}.char-counter[data-near-limit]{color:var(--color-warning,#f59e0b)}.char-counter[data-at-limit]{color:var(--color-error)}:host([error]:not([error=""])) .field{border-color:var(--color-error)}:host([error]:not([error=""])) .label,.helper-text[role=alert]{color:var(--color-error)}:host([success]) .field{border-color:var(--color-success)}.status-icon{width:var(--size-5);height:var(--size-5);flex-shrink:0;justify-content:center;align-items:center;display:none}:host([error]:not([error=""])) .status-icon,:host([success]) .status-icon{display:flex}.status-icon[data-status=error]{color:var(--color-error)}.status-icon[data-status=success]{color:var(--color-success)}@keyframes spin{to{transform:rotate(360deg)}}.field-spinner{width:var(--size-4);height:var(--size-4);border:var(--border-2) solid var(--_theme-focus,currentcolor);animation:var(--_motion-animation,spin var(--transition-loader) infinite);border-radius:50%;flex-shrink:0;display:none}.field-spinner:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){border-right-color:#0000}.field-spinner:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){border-left-color:#0000}:host([loading]) .field-spinner{display:inline-block}}@layer refine.variants{:host([variant=text]){--_bg:var(--input-bg,transparent);--_border-color:var(--input-border-color,var(--color-contrast-300))}:host([variant=text]) .field{box-shadow:none;background:0 0;border:none;border-radius:0}:host([variant=text]) .field:focus-within{box-shadow:none}:host(:not([disabled],[variant=text],[variant=flat])) .field:focus-within{background:var(--color-canvas);border-color:var(--_theme-focus);box-shadow:var(--_theme-shadow,var(--color-primary-focus-shadow))}:host([error]:not([error=""])) .field:focus-within{border-color:var(--color-error);box-shadow:var(--color-error-focus-shadow)}:host([success]) .field:focus-within{border-color:var(--color-success);box-shadow:var(--color-success-focus-shadow)}}@layer refine.utilities{:host([fullwidth]){width:100%}}:host([disabled]){opacity:1}:host([disabled]) .field{background:var(--color-contrast-100);border-color:var(--color-contrast-200);box-shadow:none}:host([disabled]) input{color:var(--color-contrast-400)}:host([disabled]) input::placeholder{color:var(--color-contrast-300)}:host([disabled]) .label{color:var(--color-contrast-400)}`;exports.default=e;
|
|
1
|
+
var e=`@layer refine.base{:host{--_font-size:var(--input-font-size,var(--text-sm));--_gap:var(--input-gap,var(--size-2));--_field-height:var(--input-height,var(--size-10));--_padding:var(--input-padding,var(--size-1-5) var(--size-3));--_label-block-inset:var(--input-label-block-inset,var(--size-2));--_label-inset:var(--input-label-inset,var(--size-3));--_radius:var(--input-radius,var(--rounded-lg));--_placeholder:var(--input-placeholder-color,var(--color-contrast-500));--_bg:var(--input-bg,var(--color-contrast-100));--_border-color:var(--input-border-color,var(--color-contrast-300));min-width:var(--input-min-width,12rem);flex-direction:column;align-items:stretch;display:inline-flex}.input-wrapper{flex-direction:column;gap:0;width:100%;display:flex}.field{box-sizing:border-box;height:var(--_field-height);min-height:var(--_field-height);padding:var(--_padding);background:var(--_bg);border:var(--border) solid var(--_border-color);border-radius:var(--_radius);box-shadow:var(--shadow-2xs);transition:var(--_motion-transition,background var(--transition-fast), backdrop-filter var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast));flex-direction:column;justify-content:center;align-items:stretch;gap:0;display:flex}.input-wrapper:has(.label:not([hidden])) .field{height:auto}:host(:not([label-placement=outside])) .input-wrapper{position:relative}:host(:not([label-placement=outside])) .label{z-index:1;pointer-events:none;top:var(--_label-block-inset);position:absolute}:host(:not([label-placement=outside])) .label:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){left:var(--_label-inset)}:host(:not([label-placement=outside])) .label:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){right:var(--_label-inset)}:host(:not([label-placement=outside])) .input-wrapper:has(.label:not([hidden])) .field{padding-top:calc(var(--_label-block-inset) + 1lh + 2px)}.input-row{gap:var(--_gap);flex:1;align-items:center;display:flex}.label,label.label{text-overflow:ellipsis;font-size:var(--text-xs);font-weight:var(--font-medium);line-height:var(--leading-tight);color:var(--color-contrast-500);white-space:nowrap;cursor:pointer;-webkit-user-select:none;user-select:none;transition:var(--_motion-transition,color var(--transition-fast));margin-bottom:2px;overflow:hidden}:host([label-placement=outside]) .label,:host([label-placement=outside]) label.label{font-size:var(--text-xs);line-height:var(--leading-loose);order:-1}.helper-text{margin-top:var(--size-1-5);font-size:var(--text-xs);line-height:var(--leading-tight);color:color-mix(in oklch, var(--color-contrast-500) 80%, var(--color-canvas));overflow-wrap:anywhere;padding-left:2px;padding-right:2px}.helper-text[hidden]{display:none}::slotted(svg){width:var(--_icon-size,var(--leading-6));height:var(--_icon-size,var(--leading-6));flex-shrink:0}}@layer refine.overrides{input{min-width:0;min-height:var(--size-6);font:inherit;font-size:var(--_font-size);line-height:var(--leading-normal);color:var(--color-contrast-900);background:0 0;border:none;outline:none;flex:1}input::placeholder{color:var(--_placeholder);transition:color var(--transition-fast)}input:focus-visible{outline:none}:host(:not([disabled],[variant=bordered],[variant=flat])) .field:hover{border-color:var(--color-contrast-400)}:host(:not([disabled])) .field:focus-within .label,:host(:not([disabled])) .field:focus-within ::slotted([slot=prefix]),:host(:not([disabled])) .field:focus-within ::slotted([slot=suffix]){color:var(--_theme-focus)}.clear-btn{width:var(--size-5);min-width:var(--_touch-target);height:var(--size-5);min-height:var(--_touch-target);color:var(--color-contrast-500);cursor:pointer;border-radius:var(--rounded-sm);transition:var(--_motion-transition,color var(--transition-fast));background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:none}.clear-btn:hover{color:var(--color-contrast-900)}.clear-btn:focus-visible{outline:var(--border-2) solid var(--_theme-focus);outline-offset:var(--border)}:host([clearable]) .clear-btn{display:flex}:host([clearable]:not([has-value])) .clear-btn{visibility:hidden;pointer-events:none}.pwd-toggle-btn{width:var(--size-5);min-width:var(--_touch-target);height:var(--size-5);min-height:var(--_touch-target);color:var(--color-contrast-500);cursor:pointer;border-radius:var(--rounded-sm);transition:var(--_motion-transition,color var(--transition-fast));background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:none}.pwd-toggle-btn:hover{color:var(--color-contrast-900)}.pwd-toggle-btn:focus-visible{outline:var(--border-2) solid var(--_theme-focus);outline-offset:var(--border)}:host([type=password]) .pwd-toggle-btn{display:flex}.char-counter{font-size:var(--text-xs);line-height:var(--leading-tight);color:var(--color-contrast-400);white-space:nowrap;padding-left:2px;padding-right:2px}.char-counter:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){text-align:right}.char-counter:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){text-align:left}.char-counter[data-near-limit]{color:var(--color-warning,#f59e0b)}.char-counter[data-at-limit]{color:var(--color-error)}:host([error]:not([error=""])) .field{border-color:var(--color-error)}:host([error]:not([error=""])) .label,.helper-text[role=alert]{color:var(--color-error)}:host([success]) .field{border-color:var(--color-success)}.status-icon{width:var(--size-5);height:var(--size-5);flex-shrink:0;justify-content:center;align-items:center;display:none}:host([error]:not([error=""])) .status-icon,:host([success]) .status-icon{display:flex}.status-icon[data-status=error]{color:var(--color-error)}.status-icon[data-status=success]{color:var(--color-success)}@keyframes spin{to{transform:rotate(360deg)}}.field-spinner{width:var(--size-4);height:var(--size-4);border:var(--border-2) solid var(--_theme-focus,currentcolor);animation:var(--_motion-animation,spin var(--transition-loader) infinite);border-radius:50%;flex-shrink:0;display:none}.field-spinner:not(:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi))){border-right-color:#0000}.field-spinner:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)){border-left-color:#0000}:host([loading]) .field-spinner{display:inline-block}}@layer refine.variants{:host([variant=text]){--_bg:var(--input-bg,transparent);--_border-color:var(--input-border-color,var(--color-contrast-300))}:host([variant=text]) .field{box-shadow:none;background:0 0;border:none;border-radius:0}:host([variant=text]) .field:focus-within{box-shadow:none}:host(:not([disabled],[variant=text],[variant=flat])) .field:focus-within{background:var(--color-canvas);border-color:var(--_theme-focus);box-shadow:var(--_theme-shadow,var(--color-primary-focus-shadow))}:host([error]:not([error=""])) .field:focus-within{border-color:var(--color-error);box-shadow:var(--color-error-focus-shadow)}:host([success]) .field:focus-within{border-color:var(--color-success);box-shadow:var(--color-success-focus-shadow)}}@layer refine.utilities{:host([fullwidth]){width:100%}}:host([disabled]){opacity:1}:host([disabled]) .field{background:var(--color-contrast-100);border-color:var(--color-contrast-200);box-shadow:none}:host([disabled]) input{color:var(--color-contrast-400)}:host([disabled]) input::placeholder{color:var(--color-contrast-300)}:host([disabled]) .label{color:var(--color-contrast-400)}`;exports.default=e;
|
|
2
2
|
//# sourceMappingURL=input.css.cjs.map
|