yukit-web 0.2.6 → 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
- bcc0b080d2001264ae10fd34254f6909e6e93411 {"key":"make-fetch-happen:request-cache:https://registry.npmjs.org/yukit-web","integrity":"sha512-vsJYXRhkfcCdAQWk9NVBK7Ri60ls9mc5xHgfIvWucUHy4N/EEXsmYV8/B9AtduNss11Syfj6WgKfLNvja1yaCA==","time":1791188741390,"size":451691,"metadata":{"time":1791188741378,"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 08:25:41 GMT","etag":"W/\"22a7d325116db3f71eb0ed596f615980\"","last-modified":"Mon, 05 Oct 2026 08:01:35 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.6 version
9
- 8 verbose argv "view" "yukit-web@0.2.6" "version"
10
- 9 verbose logfile logs-max:10 dir:/builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T08_25_41_092Z-
11
- 10 verbose logfile /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T08_25_41_092Z-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 162ms (cache miss)
14
- 13 verbose stack Error: No match found for version 0.2.6
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.6
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.6
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.6' 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-05T08_25_41_092Z-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-05T08_25_41_448Z-
11
- 10 verbose logfile /builds/yurest/yurest-ui/dist/yukit-web/.npm/_logs/2026-10-05T08_25_41_448Z-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).
@@ -3828,11 +3828,11 @@ class YuwListToolbarFilterSetsComponent {
3828
3828
  }
3829
3829
  }
3830
3830
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwListToolbarFilterSetsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
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\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 }); }
3832
3832
  }
3833
3833
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwListToolbarFilterSetsComponent, decorators: [{
3834
3834
  type: Component,
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\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" }]
3836
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"] }] } });
3837
3837
 
3838
3838
  const FORM_VIEWS = new Set(['save', 'rename', 'delete']);
@@ -4375,7 +4375,7 @@ class YuwDatatableComponent {
4375
4375
  this._isEmpty = computed(() => !this.loading() && this.table.getRowModel().rows.length === 0, ...(ngDevMode ? [{ debugName: "_isEmpty" }] : /* istanbul ignore next */ []));
4376
4376
  this._rowClass = computed(() => datatableRowVariants({ appearance: this.appearance() }), ...(ngDevMode ? [{ debugName: "_rowClass" }] : /* istanbul ignore next */ []));
4377
4377
  this._rowClasses = computed(() => {
4378
- const base = this._rowClass();
4378
+ const base = cn('yuw-reveal-host', this._rowClass());
4379
4379
  const rowClass = this.rowClass();
4380
4380
  if (typeof rowClass !== 'function') {
4381
4381
  return new Map(this.table.getRowModel().rows.map((row) => [row.id, cn(base, rowClass)]));
@@ -4456,7 +4456,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
4456
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 }] }] } });
4457
4457
 
4458
4458
  /**
4459
- * 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
4460
4461
  * outputs. The host stops the click so a clickable row does not fire twice.
4461
4462
  */
4462
4463
  class YuwActionsCellComponent {
@@ -4471,18 +4472,21 @@ class YuwActionsCellComponent {
4471
4472
  event.stopPropagation();
4472
4473
  }
4473
4474
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwActionsCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4474
- 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 }); }
4475
4476
  }
4476
4477
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: YuwActionsCellComponent, decorators: [{
4477
4478
  type: Component,
4478
- 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" }]
4479
4480
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], onEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "onEdit", required: false }] }] } });
4480
4481
 
4481
- /** Headerless `actions` column; `label` is the button's accessible name, already translated. */
4482
- 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 = '') {
4483
4487
  return {
4484
4488
  id: 'actions',
4485
- header: '',
4489
+ header,
4486
4490
  enableSorting: false,
4487
4491
  meta: cellClass ? { align: 'end', cellClass } : { align: 'end' },
4488
4492
  cell: ({ row }) => flexRenderComponent(YuwActionsCellComponent, {