yukit-web 0.2.5 → 0.2.7

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.
@@ -1,2 +1,2 @@
1
1
 
2
- b6a3f5d17a006de5ea1f34d89f74d5c2de271c5b {"key":"make-fetch-happen:request-cache:https://registry.npmjs.org/yukit-web","integrity":"sha512-tWR64Hz7gxOMMLfGYKkDT4oMYFzu+M2dZKw1rGtf4uDIhqDKa6Qs0qtbiZTMMd2En4eUqC1Bh5rmegMgPRuFbw==","time":1791187166152,"size":442289,"metadata":{"time":1791187166141,"url":"https://registry.npmjs.org/yukit-web","reqHeaders":{"accept":"application/json"},"resHeaders":{"cache-control":"public, max-age=300","content-encoding":"gzip","content-type":"application/json","date":"Mon, 05 Oct 2026 07:59:26 GMT","etag":"W/\"4254860e85dcfd4f54bed6b20e5788b5\"","last-modified":"Mon, 05 Oct 2026 07:20:25 GMT","vary":"accept-encoding, accept"},"options":{"compress":true}}}
2
+ f00c4f131ee9a4750566e920cf724e655c407801 {"key":"make-fetch-happen:request-cache:https://registry.npmjs.org/yukit-web","integrity":"sha512-71VofAGrufFN0HjjZSbPl00fYUpxUpsAgNL7t9osd6bU5JV5RX96gYYdk07iRYsuWwVWEyse+CUZLd9SjHp3Pg==","time":1791195229628,"size":461093,"metadata":{"time":1791195229613,"url":"https://registry.npmjs.org/yukit-web","reqHeaders":{"accept":"application/json"},"resHeaders":{"cache-control":"public, max-age=300","content-encoding":"gzip","content-type":"application/json","date":"Mon, 05 Oct 2026 10:13:49 GMT","etag":"W/\"df5cfbb3fb67249220b2df7b814d4d6d\"","last-modified":"Mon, 05 Oct 2026 08:28:21 GMT","vary":"accept-encoding, accept"},"options":{"compress":true}}}
@@ -5,24 +5,24 @@
5
5
  4 silly config load:file:/builds/yurest/yurest-ui/dist/yukit-web/.npmrc
6
6
  5 silly config load:file:/root/.npmrc
7
7
  6 silly config load:file:/usr/local/etc/npmrc
8
- 7 verbose title npm view yukit-web@0.2.5 version
9
- 8 verbose argv "view" "yukit-web@0.2.5" "version"
10
- 9 verbose logfile logs-max:10 dir:/builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T07_59_25_810Z-
11
- 10 verbose logfile /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T07_59_25_810Z-debug-0.log
8
+ 7 verbose title npm view yukit-web@0.2.7 version
9
+ 8 verbose argv "view" "yukit-web@0.2.7" "version"
10
+ 9 verbose logfile logs-max:10 dir:/builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T10_13_49_268Z-
11
+ 10 verbose logfile /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T10_13_49_268Z-debug-0.log
12
12
  11 silly logfile done cleaning log files
13
- 12 http fetch GET 200 https://registry.npmjs.org/yukit-web 199ms (cache miss)
14
- 13 verbose stack Error: No match found for version 0.2.5
13
+ 12 http fetch GET 200 https://registry.npmjs.org/yukit-web 171ms (cache miss)
14
+ 13 verbose stack Error: No match found for version 0.2.7
15
15
  13 verbose stack at #getData (/usr/local/lib/node_modules/npm/lib/commands/view.js:192:27)
16
16
  13 verbose stack at async #viewPackage (/usr/local/lib/node_modules/npm/lib/commands/view.js:112:28)
17
17
  13 verbose stack at async View.exec (/usr/local/lib/node_modules/npm/lib/commands/view.js:70:5)
18
18
  13 verbose stack at async Npm.exec (/usr/local/lib/node_modules/npm/lib/npm.js:207:9)
19
19
  13 verbose stack at async module.exports (/usr/local/lib/node_modules/npm/lib/cli/entry.js:74:5)
20
20
  14 verbose statusCode 404
21
- 15 verbose pkgid yukit-web@0.2.5
21
+ 15 verbose pkgid yukit-web@0.2.7
22
22
  16 error code E404
23
- 17 error 404 No match found for version 0.2.5
23
+ 17 error 404 No match found for version 0.2.7
24
24
  18 error 404
25
- 19 error 404 'yukit-web@0.2.5' is not in this registry.
25
+ 19 error 404 'yukit-web@0.2.7' is not in this registry.
26
26
  20 error 404
27
27
  21 error 404 Note that you can also install from a
28
28
  22 error 404 tarball, folder, http url, or git url.
@@ -32,4 +32,4 @@
32
32
  26 verbose npm v10.9.9
33
33
  27 verbose exit 1
34
34
  28 verbose code 1
35
- 29 error A complete log of this run can be found in: /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T07_59_25_810Z-debug-0.log
35
+ 29 error A complete log of this run can be found in: /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T10_13_49_268Z-debug-0.log
@@ -7,8 +7,8 @@
7
7
  6 silly config load:file:/usr/local/etc/npmrc
8
8
  7 verbose title npm publish
9
9
  8 verbose argv "publish" "--access" "public"
10
- 9 verbose logfile logs-max:10 dir:/builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T07_59_26_211Z-
11
- 10 verbose logfile /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T07_59_26_211Z-debug-0.log
10
+ 9 verbose logfile logs-max:10 dir:/builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T10_13_49_709Z-
11
+ 10 verbose logfile /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T10_13_49_709Z-debug-0.log
12
12
  11 silly logfile done cleaning log files
13
13
  12 verbose publish [ '.' ]
14
14
  13 silly packumentCache heap:2197815296 maxSize:549453824 maxEntrySize:274726912
package/README.md CHANGED
@@ -208,6 +208,27 @@ document.documentElement.classList.toggle('dark');
208
208
  Note that `<yuw-toaster>` defaults to `theme="light"` — pass `theme="dark"` or `theme="system"`
209
209
  explicitly in apps that render dark.
210
210
 
211
+ ### Reveal on hover
212
+
213
+ Repeated secondary controls (a row's edit button, an item's "…" menu) can stay hidden until the
214
+ user points at their container. Two plain CSS classes, shipped in `theme.css`, do it with a fade:
215
+
216
+ ```html
217
+ <li class="yuw-reveal-host">
218
+ Item name
219
+ <yuw-button class="yuw-reveal" variant="ghost" size="icon" ariaLabel="Edit">…</yuw-button>
220
+ </li>
221
+ ```
222
+
223
+ - `yuw-reveal-host` goes on the hover area, `yuw-reveal` on the element that fades.
224
+ - It shows on hover, on keyboard focus (`:focus-visible`, so a mouse click does not pin it open),
225
+ and on an ancestor carrying `data-highlighted`.
226
+ - Devices that cannot hover (phones, tablets) never hide the element, so the control stays
227
+ reachable by touch.
228
+ - Respects `prefers-reduced-motion`.
229
+
230
+ `yuw-datatable` rows already carry `yuw-reveal-host`, and the actions column applies `yuw-reveal`.
231
+
211
232
  ## Conventions
212
233
 
213
234
  A few rules hold across the whole library. Knowing them removes most of the per-component surprises.
@@ -929,6 +950,10 @@ Sorting, pagination, row selection, column visibility and a loading skeleton, ov
929
950
  | :------------------- | :-------- | :-------------------------------------------------- |
930
951
  | `selectedRowsChange` | `TData[]` | Selection changes. The initial state does not emit. |
931
952
 
953
+ > `buildYuwActionsColumn(label, onEdit, cellClass?, header?)` builds an edit-button column whose
954
+ > button is revealed on row hover (see [Reveal on hover](#reveal-on-hover)). Because the button is
955
+ > hidden by default, pass `header` (already translated) to give the column a visible title.
956
+
932
957
  > `selectable` enables the selection _state_; the checkbox column itself is a display column you
933
958
  > provide (`id: 'select'`, with `flexRenderComponent(...)` header/cell calling TanStack's toggle
934
959
  > handlers).
@@ -3306,7 +3306,8 @@ const INSIDE_COLUMNS_SELECTOR = '.yuw-columns-trigger, hlm-popover-content, hlm-
3306
3306
  * directly and one click is enough. Tabbing focus out of the popover closes it the same way.
3307
3307
  *
3308
3308
  * A set `viewMode` adds the list/block switch. The selected entry drops its border so both entries
3309
- * keep the same height; consumers get that for free and bind only `viewMode`.
3309
+ * keep the same height, and the entry after it takes back the leading border the group strips, so the
3310
+ * seam stays drawn. Consumers get that for free and bind only `viewMode`.
3310
3311
  */
3311
3312
  class YuwListToolbarActionsComponent {
3312
3313
  constructor() {
@@ -3329,15 +3330,16 @@ class YuwListToolbarActionsComponent {
3329
3330
  this.hasHiddenColumns = computed(() => this.hiddenColumns().length > 0, ...(ngDevMode ? [{ debugName: "hasHiddenColumns" }] : /* istanbul ignore next */ []));
3330
3331
  this.searchPopoverAlign = computed(() => (this._searchIsOnlyAction() ? 'end' : 'center'), ...(ngDevMode ? [{ debugName: "searchPopoverAlign" }] : /* istanbul ignore next */ []));
3331
3332
  this.searchPopoverOffsetX = computed(() => (this._searchIsOnlyAction() ? 0 : -16), ...(ngDevMode ? [{ debugName: "searchPopoverOffsetX" }] : /* istanbul ignore next */ []));
3332
- this.viewModeButtons = computed(() => VIEW_MODES.map((mode) => {
3333
+ this.viewModeButtons = computed(() => VIEW_MODES.map((mode, index) => {
3333
3334
  const selected = mode === this.viewMode();
3335
+ const followsSelected = VIEW_MODES[index - 1] === this.viewMode();
3334
3336
  return {
3335
3337
  id: mode,
3336
3338
  ariaLabel: this.labels().viewMode?.[mode],
3337
3339
  tooltip: this.labels().viewMode?.[mode],
3338
3340
  size: 'icon',
3339
3341
  selected,
3340
- class: selected ? 'border-0' : '',
3342
+ class: cn(selected && 'border-0', followsSelected && 'border-s!'),
3341
3343
  };
3342
3344
  }), ...(ngDevMode ? [{ debugName: "viewModeButtons" }] : /* istanbul ignore next */ []));
3343
3345
  effect(() => {
@@ -3826,11 +3828,11 @@ class YuwListToolbarFilterSetsComponent {
3826
3828
  }
3827
3829
  }
3828
3830
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwListToolbarFilterSetsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3829
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: YuwListToolbarFilterSetsComponent, isStandalone: true, selector: "yuw-list-toolbar-filter-sets", inputs: { canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, canSave: { classPropertyName: "canSave", publicName: "canSave", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: true, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, sets: { classPropertyName: "sets", publicName: "sets", isSignal: true, isRequired: true, transformFunction: null }, stretchActions: { classPropertyName: "stretchActions", publicName: "stretchActions", isSignal: true, isRequired: false, transformFunction: null }, titleHidden: { classPropertyName: "titleHidden", publicName: "titleHidden", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleteRequested: "deleteRequested", editRequested: "editRequested", itemEditRequested: "itemEditRequested", picked: "picked", saveRequested: "saveRequested" }, host: { classAttribute: "flex max-w-full flex-col gap-3" }, providers: [provideIcons({ lucideEllipsis, lucidePencil, lucideTrash2 })], viewQueries: [{ propertyName: "_combobox", first: true, predicate: YuwComboboxComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (!titleHidden()) {\n <p class=\"text-sm font-medium\">{{ labels().title }}</p>\n}\n\n<yuw-combobox\n readonlyTrigger\n [options]=\"options()\"\n [value]=\"options().length > 0 ? selectedId() : undefined\"\n [placeholder]=\"labels().select\"\n [emptyText]=\"labels().empty\"\n [triggerAriaLabel]=\"labels().select\"\n (valueChange)=\"onPick($event)\"\n>\n <ng-template yuwComboboxItem let-option>\n <span class=\"min-w-0 flex-1 truncate\">{{ option.label }}</span>\n\n @if (option.data) {\n <span\n class=\"inline-flex opacity-0 transition-opacity duration-150 focus-within:opacity-100 [[data-highlighted]_&]:opacity-100\"\n (click)=\"$event.stopPropagation()\"\n >\n <yuw-button variant=\"ghost\" size=\"icon-sm\" [ariaLabel]=\"labels().edit\" (clicked)=\"onItemEdit(option.value)\">\n <ng-icon name=\"lucideEllipsis\" size=\"14\" />\n </yuw-button>\n </span>\n }\n </ng-template>\n</yuw-combobox>\n\n@if (canSave()) {\n <yuw-button variant=\"outline\" size=\"sm\" class=\"max-w-full min-w-0\" (clicked)=\"saveRequested.emit()\">\n <span class=\"truncate\">{{ labels().save }}</span>\n </yuw-button>\n}\n\n@if (canEdit()) {\n <div class=\"flex min-w-0 max-w-full\" [class.gap-1]=\"!stretchActions()\">\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"outline\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"editRequested.emit()\"\n >\n <ng-icon name=\"lucidePencil\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().edit }}</span>\n </yuw-button>\n </div>\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"ghost\"\n color=\"destructive\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"deleteRequested.emit()\"\n >\n <ng-icon name=\"lucideTrash2\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().delete }}</span>\n </yuw-button>\n </div>\n </div>\n}\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: YuwButtonComponent, selector: "yuw-button", inputs: ["animationDisabled", "ariaLabel", "color", "disabled", "href", "size", "target", "transparent", "type", "class", "variant", "yuwDropdownMenuTrigger"], outputs: ["clicked"] }, { kind: "component", type: YuwComboboxComponent, selector: "yuw-combobox", inputs: ["clearAriaLabel", "emptyText", "inputId", "invalid", "keepValueOnEscape", "multiple", "options", "placeholder", "readonlyTrigger", "search", "searchPlaceholder", "size", "triggerAriaLabel", "value"], outputs: ["searchChange", "valueChange"] }, { kind: "directive", type: YuwComboboxItemDirective, selector: "[yuwComboboxItem]", inputs: ["yuwComboboxItemTypeOf"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3831
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: YuwListToolbarFilterSetsComponent, isStandalone: true, selector: "yuw-list-toolbar-filter-sets", inputs: { canEdit: { classPropertyName: "canEdit", publicName: "canEdit", isSignal: true, isRequired: false, transformFunction: null }, canSave: { classPropertyName: "canSave", publicName: "canSave", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: true, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, sets: { classPropertyName: "sets", publicName: "sets", isSignal: true, isRequired: true, transformFunction: null }, stretchActions: { classPropertyName: "stretchActions", publicName: "stretchActions", isSignal: true, isRequired: false, transformFunction: null }, titleHidden: { classPropertyName: "titleHidden", publicName: "titleHidden", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { deleteRequested: "deleteRequested", editRequested: "editRequested", itemEditRequested: "itemEditRequested", picked: "picked", saveRequested: "saveRequested" }, host: { classAttribute: "flex max-w-full flex-col gap-3" }, providers: [provideIcons({ lucideEllipsis, lucidePencil, lucideTrash2 })], viewQueries: [{ propertyName: "_combobox", first: true, predicate: YuwComboboxComponent, descendants: true, isSignal: true }], ngImport: i0, template: "@if (!titleHidden()) {\n <p class=\"text-sm font-medium\">{{ labels().title }}</p>\n}\n\n<yuw-combobox\n readonlyTrigger\n [options]=\"options()\"\n [value]=\"options().length > 0 ? selectedId() : undefined\"\n [placeholder]=\"labels().select\"\n [emptyText]=\"labels().empty\"\n [triggerAriaLabel]=\"labels().select\"\n (valueChange)=\"onPick($event)\"\n>\n <ng-template yuwComboboxItem let-option>\n <span class=\"min-w-0 flex-1 truncate\">{{ option.label }}</span>\n\n @if (option.data) {\n <span class=\"yuw-reveal inline-flex\" (click)=\"$event.stopPropagation()\">\n <yuw-button variant=\"ghost\" size=\"icon-sm\" [ariaLabel]=\"labels().edit\" (clicked)=\"onItemEdit(option.value)\">\n <ng-icon name=\"lucideEllipsis\" size=\"14\" />\n </yuw-button>\n </span>\n }\n </ng-template>\n</yuw-combobox>\n\n@if (canSave()) {\n <yuw-button variant=\"outline\" size=\"sm\" class=\"max-w-full min-w-0\" (clicked)=\"saveRequested.emit()\">\n <span class=\"truncate\">{{ labels().save }}</span>\n </yuw-button>\n}\n\n@if (canEdit()) {\n <div class=\"flex min-w-0 max-w-full\" [class.gap-1]=\"!stretchActions()\">\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"outline\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"editRequested.emit()\"\n >\n <ng-icon name=\"lucidePencil\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().edit }}</span>\n </yuw-button>\n </div>\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"ghost\"\n color=\"destructive\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"deleteRequested.emit()\"\n >\n <ng-icon name=\"lucideTrash2\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().delete }}</span>\n </yuw-button>\n </div>\n </div>\n}\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: YuwButtonComponent, selector: "yuw-button", inputs: ["animationDisabled", "ariaLabel", "color", "disabled", "href", "size", "target", "transparent", "type", "class", "variant", "yuwDropdownMenuTrigger"], outputs: ["clicked"] }, { kind: "component", type: YuwComboboxComponent, selector: "yuw-combobox", inputs: ["clearAriaLabel", "emptyText", "inputId", "invalid", "keepValueOnEscape", "multiple", "options", "placeholder", "readonlyTrigger", "search", "searchPlaceholder", "size", "triggerAriaLabel", "value"], outputs: ["searchChange", "valueChange"] }, { kind: "directive", type: YuwComboboxItemDirective, selector: "[yuwComboboxItem]", inputs: ["yuwComboboxItemTypeOf"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
3830
3832
  }
3831
3833
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwListToolbarFilterSetsComponent, decorators: [{
3832
3834
  type: Component,
3833
- args: [{ selector: 'yuw-list-toolbar-filter-sets', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgIcon, YuwButtonComponent, YuwComboboxComponent, YuwComboboxItemDirective], providers: [provideIcons({ lucideEllipsis, lucidePencil, lucideTrash2 })], host: { class: 'flex max-w-full flex-col gap-3' }, template: "@if (!titleHidden()) {\n <p class=\"text-sm font-medium\">{{ labels().title }}</p>\n}\n\n<yuw-combobox\n readonlyTrigger\n [options]=\"options()\"\n [value]=\"options().length > 0 ? selectedId() : undefined\"\n [placeholder]=\"labels().select\"\n [emptyText]=\"labels().empty\"\n [triggerAriaLabel]=\"labels().select\"\n (valueChange)=\"onPick($event)\"\n>\n <ng-template yuwComboboxItem let-option>\n <span class=\"min-w-0 flex-1 truncate\">{{ option.label }}</span>\n\n @if (option.data) {\n <span\n class=\"inline-flex opacity-0 transition-opacity duration-150 focus-within:opacity-100 [[data-highlighted]_&]:opacity-100\"\n (click)=\"$event.stopPropagation()\"\n >\n <yuw-button variant=\"ghost\" size=\"icon-sm\" [ariaLabel]=\"labels().edit\" (clicked)=\"onItemEdit(option.value)\">\n <ng-icon name=\"lucideEllipsis\" size=\"14\" />\n </yuw-button>\n </span>\n }\n </ng-template>\n</yuw-combobox>\n\n@if (canSave()) {\n <yuw-button variant=\"outline\" size=\"sm\" class=\"max-w-full min-w-0\" (clicked)=\"saveRequested.emit()\">\n <span class=\"truncate\">{{ labels().save }}</span>\n </yuw-button>\n}\n\n@if (canEdit()) {\n <div class=\"flex min-w-0 max-w-full\" [class.gap-1]=\"!stretchActions()\">\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"outline\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"editRequested.emit()\"\n >\n <ng-icon name=\"lucidePencil\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().edit }}</span>\n </yuw-button>\n </div>\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"ghost\"\n color=\"destructive\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"deleteRequested.emit()\"\n >\n <ng-icon name=\"lucideTrash2\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().delete }}</span>\n </yuw-button>\n </div>\n </div>\n}\n" }]
3835
+ args: [{ selector: 'yuw-list-toolbar-filter-sets', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgIcon, YuwButtonComponent, YuwComboboxComponent, YuwComboboxItemDirective], providers: [provideIcons({ lucideEllipsis, lucidePencil, lucideTrash2 })], host: { class: 'flex max-w-full flex-col gap-3' }, template: "@if (!titleHidden()) {\n <p class=\"text-sm font-medium\">{{ labels().title }}</p>\n}\n\n<yuw-combobox\n readonlyTrigger\n [options]=\"options()\"\n [value]=\"options().length > 0 ? selectedId() : undefined\"\n [placeholder]=\"labels().select\"\n [emptyText]=\"labels().empty\"\n [triggerAriaLabel]=\"labels().select\"\n (valueChange)=\"onPick($event)\"\n>\n <ng-template yuwComboboxItem let-option>\n <span class=\"min-w-0 flex-1 truncate\">{{ option.label }}</span>\n\n @if (option.data) {\n <span class=\"yuw-reveal inline-flex\" (click)=\"$event.stopPropagation()\">\n <yuw-button variant=\"ghost\" size=\"icon-sm\" [ariaLabel]=\"labels().edit\" (clicked)=\"onItemEdit(option.value)\">\n <ng-icon name=\"lucideEllipsis\" size=\"14\" />\n </yuw-button>\n </span>\n }\n </ng-template>\n</yuw-combobox>\n\n@if (canSave()) {\n <yuw-button variant=\"outline\" size=\"sm\" class=\"max-w-full min-w-0\" (clicked)=\"saveRequested.emit()\">\n <span class=\"truncate\">{{ labels().save }}</span>\n </yuw-button>\n}\n\n@if (canEdit()) {\n <div class=\"flex min-w-0 max-w-full\" [class.gap-1]=\"!stretchActions()\">\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"outline\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"editRequested.emit()\"\n >\n <ng-icon name=\"lucidePencil\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().edit }}</span>\n </yuw-button>\n </div>\n <div [class]=\"stretchActions() ? stretchedCell : 'min-w-0'\">\n <yuw-button\n variant=\"ghost\"\n color=\"destructive\"\n size=\"sm\"\n [class]=\"stretchActions() ? 'w-full min-w-0 gap-1' : 'max-w-full min-w-0 gap-1'\"\n (clicked)=\"deleteRequested.emit()\"\n >\n <ng-icon name=\"lucideTrash2\" size=\"16\" class=\"shrink-0\" />\n <span class=\"truncate\">{{ labels().delete }}</span>\n </yuw-button>\n </div>\n </div>\n}\n" }]
3834
3836
  }], propDecorators: { _combobox: [{ type: i0.ViewChild, args: [i0.forwardRef(() => YuwComboboxComponent), { isSignal: true }] }], canEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "canEdit", required: false }] }], canSave: [{ type: i0.Input, args: [{ isSignal: true, alias: "canSave", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: true }] }], selectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedId", required: false }] }], sets: [{ type: i0.Input, args: [{ isSignal: true, alias: "sets", required: true }] }], stretchActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "stretchActions", required: false }] }], titleHidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleHidden", required: false }] }], deleteRequested: [{ type: i0.Output, args: ["deleteRequested"] }], editRequested: [{ type: i0.Output, args: ["editRequested"] }], itemEditRequested: [{ type: i0.Output, args: ["itemEditRequested"] }], picked: [{ type: i0.Output, args: ["picked"] }], saveRequested: [{ type: i0.Output, args: ["saveRequested"] }] } });
3835
3837
 
3836
3838
  const FORM_VIEWS = new Set(['save', 'rename', 'delete']);
@@ -4373,7 +4375,7 @@ class YuwDatatableComponent {
4373
4375
  this._isEmpty = computed(() => !this.loading() && this.table.getRowModel().rows.length === 0, ...(ngDevMode ? [{ debugName: "_isEmpty" }] : /* istanbul ignore next */ []));
4374
4376
  this._rowClass = computed(() => datatableRowVariants({ appearance: this.appearance() }), ...(ngDevMode ? [{ debugName: "_rowClass" }] : /* istanbul ignore next */ []));
4375
4377
  this._rowClasses = computed(() => {
4376
- const base = this._rowClass();
4378
+ const base = cn('yuw-reveal-host', this._rowClass());
4377
4379
  const rowClass = this.rowClass();
4378
4380
  if (typeof rowClass !== 'function') {
4379
4381
  return new Map(this.table.getRowModel().rows.map((row) => [row.id, cn(base, rowClass)]));
@@ -4454,7 +4456,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
4454
4456
  }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "onToggle", required: false }] }] } });
4455
4457
 
4456
4458
  /**
4457
- * Pencil button cell that runs `onEdit`, an input because `flexRenderComponent` does not subscribe
4459
+ * Pencil button cell that runs `onEdit`, hidden until its row is hovered or keyboard-focused (`yuw-reveal`,
4460
+ * see `theme/components/reveal-on-hover.css`); touch devices keep it visible. An input because `flexRenderComponent` does not subscribe
4458
4461
  * outputs. The host stops the click so a clickable row does not fire twice.
4459
4462
  */
4460
4463
  class YuwActionsCellComponent {
@@ -4469,18 +4472,21 @@ class YuwActionsCellComponent {
4469
4472
  event.stopPropagation();
4470
4473
  }
4471
4474
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwActionsCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4472
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.22", type: YuwActionsCellComponent, isStandalone: true, selector: "yuw-actions-cell", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, onEdit: { classPropertyName: "onEdit", publicName: "onEdit", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onHostClick($event)" } }, providers: [provideIcons({ lucidePencil })], ngImport: i0, template: "<yuw-button variant=\"ghost\" size=\"icon\" [ariaLabel]=\"label()\" (clicked)=\"edit()\">\n <ng-icon name=\"lucidePencil\" size=\"16\" />\n</yuw-button>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: YuwButtonComponent, selector: "yuw-button", inputs: ["animationDisabled", "ariaLabel", "color", "disabled", "href", "size", "target", "transparent", "type", "class", "variant", "yuwDropdownMenuTrigger"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4475
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.22", type: YuwActionsCellComponent, isStandalone: true, selector: "yuw-actions-cell", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, onEdit: { classPropertyName: "onEdit", publicName: "onEdit", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "click": "onHostClick($event)" }, classAttribute: "yuw-reveal" }, providers: [provideIcons({ lucidePencil })], ngImport: i0, template: "<yuw-button variant=\"ghost\" size=\"icon\" [ariaLabel]=\"label()\" (clicked)=\"edit()\">\n <ng-icon name=\"lucidePencil\" size=\"16\" />\n</yuw-button>\n", dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "component", type: YuwButtonComponent, selector: "yuw-button", inputs: ["animationDisabled", "ariaLabel", "color", "disabled", "href", "size", "target", "transparent", "type", "class", "variant", "yuwDropdownMenuTrigger"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
4473
4476
  }
4474
4477
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwActionsCellComponent, decorators: [{
4475
4478
  type: Component,
4476
- args: [{ selector: 'yuw-actions-cell', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgIcon, YuwButtonComponent], providers: [provideIcons({ lucidePencil })], host: { '(click)': 'onHostClick($event)' }, template: "<yuw-button variant=\"ghost\" size=\"icon\" [ariaLabel]=\"label()\" (clicked)=\"edit()\">\n <ng-icon name=\"lucidePencil\" size=\"16\" />\n</yuw-button>\n" }]
4479
+ args: [{ selector: 'yuw-actions-cell', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgIcon, YuwButtonComponent], providers: [provideIcons({ lucidePencil })], host: { class: 'yuw-reveal', '(click)': 'onHostClick($event)' }, template: "<yuw-button variant=\"ghost\" size=\"icon\" [ariaLabel]=\"label()\" (clicked)=\"edit()\">\n <ng-icon name=\"lucidePencil\" size=\"16\" />\n</yuw-button>\n" }]
4477
4480
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], onEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "onEdit", required: false }] }] } });
4478
4481
 
4479
- /** Headerless `actions` column; `label` is the button's accessible name, already translated. */
4480
- function buildYuwActionsColumn(label, onEdit, cellClass) {
4482
+ /**
4483
+ * `actions` column; `label` is the button's accessible name and `header` the optional visible
4484
+ * column title, both already translated. Without `header` the column stays headerless.
4485
+ */
4486
+ function buildYuwActionsColumn(label, onEdit, cellClass, header = '') {
4481
4487
  return {
4482
4488
  id: 'actions',
4483
- header: '',
4489
+ header,
4484
4490
  enableSorting: false,
4485
4491
  meta: cellClass ? { align: 'end', cellClass } : { align: 'end' },
4486
4492
  cell: ({ row }) => flexRenderComponent(YuwActionsCellComponent, {