@kerfjs/ui 5.0.0-beta.8 → 5.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +20 -20
  2. package/ai/component-catalog.json +123 -80
  3. package/ai/public-api-signatures-v1.md +181 -26
  4. package/ai/skill.md +12 -12
  5. package/ai/webawesome-jsx-signatures-v1.md +1 -1
  6. package/dist/browser/list-action-row.js +4 -0
  7. package/dist/browser/{menu-header.js → list-header.js} +2 -2
  8. package/dist/browser/{menu-item.js → list-item.js} +2 -2
  9. package/dist/catalog.d.ts +119 -0
  10. package/dist/catalog.js +128 -0
  11. package/dist/catalog.js.map +1 -0
  12. package/dist/{chunk-BNQ2YDV4.js → chunk-2TS7ZSYX.js} +2 -2
  13. package/dist/{chunk-BNQ2YDV4.js.map → chunk-2TS7ZSYX.js.map} +1 -1
  14. package/dist/{chunk-PONXLV5U.js → chunk-37T3VNIZ.js} +1 -1
  15. package/dist/{chunk-PONXLV5U.js.map → chunk-37T3VNIZ.js.map} +1 -1
  16. package/dist/{chunk-3YD6ZNRA.js → chunk-7JB7VPRI.js} +8 -8
  17. package/dist/{chunk-3YD6ZNRA.js.map → chunk-7JB7VPRI.js.map} +1 -1
  18. package/dist/{chunk-YKUUZPED.js → chunk-L4OKFDJY.js} +7 -7
  19. package/dist/{chunk-YKUUZPED.js.map → chunk-L4OKFDJY.js.map} +1 -1
  20. package/dist/{chunk-H6URQGJI.js → chunk-W5EQ3HYQ.js} +11 -11
  21. package/dist/{chunk-H6URQGJI.js.map → chunk-W5EQ3HYQ.js.map} +1 -1
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.js +10 -10
  24. package/dist/{menu-action-row.d.ts → list-action-row.d.ts} +7 -7
  25. package/dist/list-action-row.js +5 -0
  26. package/dist/list-action-row.js.map +1 -0
  27. package/dist/{menu-header.d.ts → list-header.d.ts} +9 -9
  28. package/dist/list-header.js +7 -0
  29. package/dist/list-header.js.map +1 -0
  30. package/dist/{menu-item.d.ts → list-item.d.ts} +5 -5
  31. package/dist/list-item.js +5 -0
  32. package/dist/list-item.js.map +1 -0
  33. package/dist/panel-header.js +1 -1
  34. package/dist/split-view.d.ts +1 -1
  35. package/dist/split-view.js.map +1 -1
  36. package/dist/styles/catalog.css +482 -0
  37. package/dist/styles/list-action-row.css +160 -0
  38. package/dist/styles/{menu-header.css → list-header.css} +33 -33
  39. package/dist/styles/list-item.css +123 -0
  40. package/dist/styles/styles.css +4 -3
  41. package/dist/value-table.js +1 -1
  42. package/dist/wire-catalog.d.ts +26 -0
  43. package/dist/wire-catalog.js +50 -0
  44. package/dist/wire-catalog.js.map +1 -0
  45. package/docs/accessibility.md +5 -5
  46. package/docs/catalog.md +154 -0
  47. package/docs/component-contract.md +12 -12
  48. package/docs/component-selection.md +12 -11
  49. package/docs/design-philosophy.md +4 -4
  50. package/docs/layout.md +8 -8
  51. package/docs/recipes.md +17 -5
  52. package/docs/split-view.md +1 -1
  53. package/docs/ux-demo.md +5 -5
  54. package/llms.txt +12 -8
  55. package/package.json +25 -16
  56. package/ux-demo/recipes/app-shell.tsx +3 -3
  57. package/ux-demo/recipes/list-detail-dialog.tsx +45 -0
  58. package/ux-demo/recipes/loaders.ts +4 -2
  59. package/ux-demo/recipes/loading-inspector.tsx +79 -0
  60. package/ux-demo/recipes/navigation-sidebar.tsx +5 -5
  61. package/ux-demo/recipes/navigation-stack.tsx +4 -4
  62. package/ux-demo/recipes/recipes.css +13 -7
  63. package/dist/browser/menu-action-row.js +0 -4
  64. package/dist/menu-action-row.js +0 -5
  65. package/dist/menu-action-row.js.map +0 -1
  66. package/dist/menu-header.js +0 -7
  67. package/dist/menu-header.js.map +0 -1
  68. package/dist/menu-item.js +0 -5
  69. package/dist/menu-item.js.map +0 -1
  70. package/dist/styles/menu-action-row.css +0 -160
  71. package/dist/styles/menu-item.css +0 -123
  72. package/ux-demo/recipes/master-detail-dialog.tsx +0 -45
@@ -0,0 +1,160 @@
1
+ .kui-list-action-row {
2
+ --kui-list-action-row-color: var(--kui-color-neutral-on-normal);
3
+ --kui-list-action-row-border: transparent;
4
+ --kui-list-action-row-background: transparent;
5
+ --kui-list-action-row-hover-border: var(--kui-color-neutral-border-normal);
6
+ --kui-list-action-row-hover-background: var(--kui-color-neutral-fill-quiet);
7
+ --kui-list-action-row-selected-color: var(--kui-color-brand-on-quiet);
8
+ --kui-list-action-row-selected-border: var(--kui-color-brand-border-quiet);
9
+ --kui-list-action-row-selected-background: var(--kui-color-brand-fill-normal);
10
+ --kui-list-action-row-trailing-hover-background: var(
11
+ --kui-color-neutral-fill-normal
12
+ );
13
+ display: flex;
14
+ width: calc(100% - (2 * var(--kui-layout-inline-margin, 0.5rem)));
15
+ min-height: 2.75rem;
16
+ margin-inline: var(--kui-layout-inline-margin, 0.5rem);
17
+ border-radius: var(--kui-layout-rounded-radius, calc(1px + 0.6875rem));
18
+ color: var(--kui-list-action-row-color);
19
+ background: var(--kui-list-action-row-background);
20
+ box-shadow: inset 0 0 0 1px var(--kui-list-action-row-border);
21
+ align-items: stretch;
22
+ }
23
+
24
+ .kui-list-action-row:has(.kui-list-action-row__primary:hover) {
25
+ --kui-list-action-row-border: var(--kui-list-action-row-hover-border);
26
+ --kui-list-action-row-background: var(--kui-list-action-row-hover-background);
27
+ }
28
+
29
+ .kui-list-action-row[data-selected="true"],
30
+ .kui-list-action-row[data-pressed="true"] {
31
+ --kui-list-action-row-border: var(--kui-list-action-row-selected-border);
32
+ --kui-list-action-row-background: var(
33
+ --kui-list-action-row-selected-background
34
+ );
35
+ color: var(--kui-list-action-row-selected-color);
36
+ font-weight: 650;
37
+ }
38
+
39
+ .kui-list-action-row:has(.kui-list-action-row__primary:focus-visible) {
40
+ outline: var(--kui-focus-ring);
41
+ outline-offset: 1px;
42
+ }
43
+
44
+ .kui-list-action-row__primary,
45
+ .kui-list-action-row__trailing-action {
46
+ height: auto !important;
47
+ min-height: 2.75rem;
48
+ padding: 0;
49
+ border: 0;
50
+ appearance: none;
51
+ color: inherit;
52
+ background: transparent;
53
+ cursor: pointer;
54
+ font: inherit;
55
+ }
56
+
57
+ .kui-list-action-row__primary {
58
+ position: relative;
59
+ display: block;
60
+ flex: 1 1 auto;
61
+ min-width: 2.75rem;
62
+ padding: var(--kui-layout-item-padding, 0.5rem);
63
+ padding-inline-start: calc(1px + var(--kui-layout-item-padding, 0.5rem));
64
+ text-align: start;
65
+ }
66
+
67
+ .kui-list-action-row__primary:focus-visible {
68
+ outline: none;
69
+ }
70
+
71
+ .kui-list-action-row__primary:disabled,
72
+ .kui-list-action-row__trailing-action:disabled {
73
+ cursor: not-allowed;
74
+ opacity: 0.58;
75
+ }
76
+
77
+ .kui-list-action-row__icon {
78
+ position: absolute;
79
+ inset-block-start: 50%;
80
+ inset-inline-start: calc(1px + var(--kui-layout-item-padding, 0.5rem));
81
+ display: grid;
82
+ width: 1.5rem;
83
+ height: 1.5rem;
84
+ color: currentColor;
85
+ transform: translateY(-50%);
86
+ place-items: center;
87
+ }
88
+
89
+ .kui-list-action-row__icon svg {
90
+ display: block;
91
+ width: 1.5rem;
92
+ height: 1.5rem;
93
+ }
94
+
95
+ .kui-list-action-row__label {
96
+ display: grid;
97
+ min-width: 0;
98
+ min-height: calc(
99
+ 2.75rem - (2 * var(--kui-layout-item-padding, 0.5rem))
100
+ );
101
+ overflow: hidden;
102
+ text-overflow: ellipsis;
103
+ white-space: nowrap;
104
+ align-items: center;
105
+ }
106
+
107
+ .kui-list-action-row[data-has-icon="true"] .kui-list-action-row__label {
108
+ margin-inline-start: calc(
109
+ 1.5rem + var(--kui-layout-item-gap, 0.5rem)
110
+ );
111
+ }
112
+
113
+ .kui-list-action-row[data-multiline="true"] .kui-list-action-row__label {
114
+ display: block;
115
+ overflow: visible;
116
+ overflow-wrap: anywhere;
117
+ white-space: normal;
118
+ }
119
+
120
+ .kui-list-action-row[data-multiline="true"] .kui-list-action-row__icon {
121
+ inset-block-start: calc(
122
+ var(--kui-layout-item-padding, 0.5rem) + (1lh / 2)
123
+ );
124
+ }
125
+
126
+ .kui-list-action-row__trailing-action {
127
+ display: grid;
128
+ width: 2.75rem;
129
+ min-width: 2.75rem;
130
+ border-radius: var(--kui-radius-pill);
131
+ place-items: center;
132
+ }
133
+
134
+ .kui-list-action-row__trailing-action:hover:not(:disabled) {
135
+ background: var(--kui-list-action-row-trailing-hover-background);
136
+ }
137
+
138
+ .kui-list-action-row__trailing-action:focus-visible {
139
+ outline: var(--kui-focus-ring);
140
+ outline-offset: -2px;
141
+ }
142
+
143
+ .kui-list-action-row__trailing-action > svg {
144
+ width: 1rem;
145
+ height: 1rem;
146
+ }
147
+
148
+ @media (forced-colors: active) {
149
+ .kui-list-action-row {
150
+ --kui-list-action-row-border: ButtonText;
151
+ outline: 1px solid ButtonText;
152
+ outline-offset: -1px;
153
+ }
154
+
155
+ .kui-list-action-row[data-selected="true"],
156
+ .kui-list-action-row[data-pressed="true"] {
157
+ --kui-list-action-row-selected-color: HighlightText;
158
+ --kui-list-action-row-selected-background: Highlight;
159
+ }
160
+ }
@@ -1,8 +1,8 @@
1
- .kui-menu-header {
2
- --kui-menu-header-color: var(--kui-color-neutral-on-quiet);
3
- --kui-menu-header-hover-background: var(--kui-color-neutral-border-quiet);
4
- --_kui-menu-header-action-icon-size: var(
5
- --kui-menu-header-action-icon-size,
1
+ .kui-list-header {
2
+ --kui-list-header-color: var(--kui-color-neutral-on-quiet);
3
+ --kui-list-header-hover-background: var(--kui-color-neutral-border-quiet);
4
+ --_kui-list-header-action-icon-size: var(
5
+ --kui-list-header-action-icon-size,
6
6
  1.125rem
7
7
  );
8
8
  display: flex;
@@ -12,14 +12,14 @@
12
12
  margin-inline: var(--kui-layout-inline-margin, 0.5rem);
13
13
  padding: 0;
14
14
  box-sizing: border-box;
15
- color: var(--kui-menu-header-color);
15
+ color: var(--kui-list-header-color);
16
16
  background: transparent;
17
17
  align-items: stretch;
18
18
  gap: var(--kui-layout-item-gap, 0.5rem);
19
19
  }
20
20
 
21
- .kui-menu-header__title,
22
- .kui-menu-header__action {
21
+ .kui-list-header__title,
22
+ .kui-list-header__action {
23
23
  box-sizing: border-box;
24
24
  border: 1px solid transparent;
25
25
  border-radius: var(--kui-layout-rounded-radius, calc(1px + 0.6875rem));
@@ -27,7 +27,7 @@
27
27
  background: transparent;
28
28
  }
29
29
 
30
- .kui-menu-header__title {
30
+ .kui-list-header__title {
31
31
  display: flex;
32
32
  min-width: 0;
33
33
  min-height: 2.75rem;
@@ -39,7 +39,7 @@
39
39
  text-align: start;
40
40
  }
41
41
 
42
- .kui-menu-header__label {
42
+ .kui-list-header__label {
43
43
  min-width: 0;
44
44
  margin: 0;
45
45
  overflow: hidden;
@@ -51,7 +51,7 @@
51
51
  white-space: nowrap;
52
52
  }
53
53
 
54
- .kui-menu-header__badge {
54
+ .kui-list-header__badge {
55
55
  display: inline-flex;
56
56
  min-height: 1.5rem;
57
57
  padding-inline: var(--kui-layout-item-padding, 0.5rem);
@@ -66,7 +66,7 @@
66
66
  line-height: 1;
67
67
  }
68
68
 
69
- .kui-menu-header__count {
69
+ .kui-list-header__count {
70
70
  display: inline-grid;
71
71
  min-width: 1.35rem;
72
72
  height: 1.35rem;
@@ -78,7 +78,7 @@
78
78
  place-items: center;
79
79
  flex: none;
80
80
  font-size: var(
81
- --kui-menu-header-count-font-size,
81
+ --kui-list-header-count-font-size,
82
82
  var(--wa-font-size-3xs, 0.625rem)
83
83
  );
84
84
  font-weight: 700;
@@ -86,26 +86,26 @@
86
86
  line-height: 1;
87
87
  }
88
88
 
89
- .kui-menu-header__action,
90
- .kui-menu-header__action-layer {
89
+ .kui-list-header__action,
90
+ .kui-list-header__action-layer {
91
91
  display: grid;
92
92
  place-items: center;
93
93
  }
94
94
 
95
- .kui-menu-header__action {
95
+ .kui-list-header__action {
96
96
  /* A fitted square: the icon size plus its padding and 1px border, instead of a
97
97
  forced 44px box that left an oversized halo around the ~18px icon and, via the
98
98
  parent's align-items: stretch, a vertically stretched oval. `align-self:
99
99
  center` keeps it centered in the header's 44px row without stretching. Sizing
100
100
  off the icon variable keeps it square and correct at any icon size. */
101
- --_kui-menu-header-action-size: calc(
102
- var(--_kui-menu-header-action-icon-size) +
101
+ --_kui-list-header-action-size: calc(
102
+ var(--_kui-list-header-action-icon-size) +
103
103
  (2 * var(--kui-layout-item-padding, 0.5rem)) + 2px
104
104
  );
105
105
  appearance: none;
106
106
  display: inline-flex;
107
- width: var(--_kui-menu-header-action-size);
108
- height: var(--_kui-menu-header-action-size);
107
+ width: var(--_kui-list-header-action-size);
108
+ height: var(--_kui-list-header-action-size);
109
109
  padding: var(--kui-layout-item-padding, 0.5rem);
110
110
  align-items: center;
111
111
  justify-content: center;
@@ -114,47 +114,47 @@
114
114
  flex: none;
115
115
  }
116
116
 
117
- .kui-menu-header__toggle {
117
+ .kui-list-header__toggle {
118
118
  appearance: none;
119
119
  width: 100%;
120
120
  cursor: pointer;
121
121
  }
122
122
 
123
- .kui-menu-header__action-layer {
123
+ .kui-list-header__action-layer {
124
124
  width: 1.5rem;
125
125
  height: 1.5rem;
126
126
  margin-inline-start: auto;
127
127
  flex: none;
128
128
  }
129
129
 
130
- .kui-menu-header__action:hover,
131
- .kui-menu-header__toggle:hover {
130
+ .kui-list-header__action:hover,
131
+ .kui-list-header__toggle:hover {
132
132
  border-color: var(--kui-color-neutral-border-normal);
133
- background: var(--kui-menu-header-hover-background);
133
+ background: var(--kui-list-header-hover-background);
134
134
  }
135
135
 
136
- .kui-menu-header__action:focus-visible,
137
- .kui-menu-header__toggle:focus-visible {
136
+ .kui-list-header__action:focus-visible,
137
+ .kui-list-header__toggle:focus-visible {
138
138
  outline: var(--kui-focus-ring);
139
139
  outline-offset: 1px;
140
140
  }
141
141
 
142
- .kui-menu-header__action:disabled {
142
+ .kui-list-header__action:disabled {
143
143
  cursor: not-allowed;
144
144
  opacity: 0.48;
145
145
  }
146
146
 
147
- .kui-menu-header__action:disabled:hover {
147
+ .kui-list-header__action:disabled:hover {
148
148
  border-color: transparent;
149
149
  background: transparent;
150
150
  }
151
151
 
152
- .kui-menu-header__action > svg,
153
- .kui-menu-header__action-layer > svg {
152
+ .kui-list-header__action > svg,
153
+ .kui-list-header__action-layer > svg {
154
154
  /* min-block-size: 0 overrides the flex item's automatic minimum, which would
155
155
  otherwise resolve to the icon SVG's intrinsic 24px viewBox height and stretch
156
156
  the button past its used icon size. */
157
- width: var(--_kui-menu-header-action-icon-size);
158
- height: var(--_kui-menu-header-action-icon-size);
157
+ width: var(--_kui-list-header-action-icon-size);
158
+ height: var(--_kui-list-header-action-icon-size);
159
159
  min-block-size: 0;
160
160
  }
@@ -0,0 +1,123 @@
1
+ .kui-list-item {
2
+ --kui-list-item-color: var(--kui-color-neutral-on-normal);
3
+ --kui-list-item-border: transparent;
4
+ --kui-list-item-background: transparent;
5
+ --kui-list-item-hover-border: var(--kui-color-neutral-border-normal);
6
+ --kui-list-item-hover-background: var(--kui-color-neutral-fill-quiet);
7
+ --kui-list-item-selected-color: var(--kui-color-brand-on-quiet);
8
+ --kui-list-item-selected-border: var(--kui-color-brand-border-quiet);
9
+ --kui-list-item-selected-background: var(--kui-color-brand-fill-normal);
10
+ display: grid;
11
+ width: calc(100% - (2 * var(--kui-layout-inline-margin, 0.5rem)));
12
+ min-height: 2.75rem;
13
+ margin-inline: var(--kui-layout-inline-margin, 0.5rem);
14
+ padding: var(--kui-layout-item-padding, 0.5rem);
15
+ box-sizing: border-box;
16
+ border: 1px solid var(--kui-list-item-border);
17
+ border-radius: var(--kui-layout-rounded-radius, calc(1px + 0.6875rem));
18
+ color: var(--kui-list-item-color);
19
+ background: var(--kui-list-item-background);
20
+ cursor: pointer;
21
+ grid-template-columns: minmax(0, 1fr) auto;
22
+ align-items: center;
23
+ column-gap: var(--kui-layout-item-gap, 0.5rem);
24
+ font: inherit;
25
+ text-align: start;
26
+ }
27
+
28
+ .kui-list-item[data-has-icon="true"] {
29
+ grid-template-columns: 1.5rem minmax(0, 1fr) auto;
30
+ }
31
+
32
+ .kui-list-item[data-has-icon="true"] .kui-list-item__icon {
33
+ grid-column: 1;
34
+ }
35
+
36
+ .kui-list-item[data-has-icon="true"] .kui-list-item__label {
37
+ grid-column: 2;
38
+ }
39
+
40
+ .kui-list-item[data-has-icon="true"] .kui-list-item__trailing {
41
+ grid-column: 3;
42
+ }
43
+
44
+ .kui-list-item[data-has-icon="false"] .kui-list-item__label {
45
+ grid-column: 1;
46
+ }
47
+
48
+ .kui-list-item[data-has-icon="false"] .kui-list-item__trailing {
49
+ grid-column: 2;
50
+ }
51
+
52
+ .kui-list-item:hover {
53
+ border-color: var(--kui-list-item-hover-border);
54
+ background: var(--kui-list-item-hover-background);
55
+ }
56
+
57
+ .kui-list-item:disabled {
58
+ color: var(--kui-color-neutral-on-quiet);
59
+ cursor: not-allowed;
60
+ opacity: 0.58;
61
+ }
62
+
63
+ .kui-list-item[aria-current="page"],
64
+ .kui-list-item[aria-pressed="true"] {
65
+ border-color: var(--kui-list-item-selected-border);
66
+ color: var(--kui-list-item-selected-color);
67
+ background: var(--kui-list-item-selected-background);
68
+ font-weight: 650;
69
+ }
70
+
71
+ .kui-list-item:focus-visible {
72
+ outline: var(--kui-focus-ring);
73
+ outline-offset: 1px;
74
+ }
75
+
76
+ .kui-list-item__icon {
77
+ display: grid;
78
+ width: 1.5rem;
79
+ height: 1.5rem;
80
+ color: currentColor;
81
+ place-items: center;
82
+ }
83
+
84
+ .kui-list-item__icon svg {
85
+ display: block;
86
+ width: 1.5rem;
87
+ height: 1.5rem;
88
+ }
89
+
90
+ .kui-list-item__label {
91
+ min-width: 0;
92
+ overflow: hidden;
93
+ text-overflow: ellipsis;
94
+ white-space: nowrap;
95
+ }
96
+
97
+ .kui-list-item[data-multiline="true"] {
98
+ height: fit-content;
99
+ white-space: normal;
100
+ }
101
+
102
+ .kui-list-item[data-multiline="true"] .kui-list-item__label {
103
+ overflow: visible;
104
+ white-space: normal;
105
+ }
106
+
107
+ .kui-list-item[data-multiline="true"] .kui-list-item__icon {
108
+ align-self: start;
109
+ margin-block-start: calc((1lh - 1.5rem) / 2);
110
+ }
111
+
112
+ .kui-list-item__trailing {
113
+ display: inline-flex;
114
+ min-width: 0;
115
+ align-items: center;
116
+ justify-content: flex-end;
117
+ gap: var(--kui-layout-item-gap, 0.5rem);
118
+ }
119
+
120
+ .kui-list-item__trailing > svg {
121
+ width: 1rem;
122
+ height: 1rem;
123
+ }
@@ -5,11 +5,12 @@
5
5
  @import "./toolbar.css";
6
6
  @import "./toolbar-control-group.css";
7
7
  @import "./toolbar-text.css";
8
- @import "./menu-action-row.css";
9
- @import "./menu-item.css";
10
- @import "./menu-header.css";
8
+ @import "./list-action-row.css";
9
+ @import "./list-item.css";
10
+ @import "./list-header.css";
11
11
  @import "./resizable-region.css";
12
12
  @import "./app-tab.css";
13
+ @import "./catalog.css";
13
14
  @import "./tab-bar.css";
14
15
  @import "./loading-spinner.css";
15
16
  @import "./skeleton.css";
@@ -1,4 +1,4 @@
1
- export { ValueTable, ValueTableRow } from './chunk-PONXLV5U.js';
1
+ export { ValueTable, ValueTableRow } from './chunk-37T3VNIZ.js';
2
2
 
3
3
 
4
4
  //# sourceMappingURL=value-table.js.map
@@ -0,0 +1,26 @@
1
+ interface WireCatalogOptions {
2
+ /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */
3
+ onSelect: (id: string) => void;
4
+ /** Invoked when the sidebar collapse/expand control is activated. */
5
+ onToggleSidebar?: () => void;
6
+ /** Invoked when the theme toggle is activated. */
7
+ onToggleTheme?: () => void;
8
+ /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */
9
+ onToggleSecondary?: () => void;
10
+ /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */
11
+ urlParam?: string;
12
+ selectAction?: string;
13
+ toggleSidebarAction?: string;
14
+ toggleThemeAction?: string;
15
+ toggleSecondaryAction?: string;
16
+ }
17
+ /**
18
+ * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar
19
+ * item selection (and the related-entry selector), the sidebar collapse toggle, and
20
+ * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates
21
+ * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.
22
+ * Returns a disposer.
23
+ */
24
+ declare function wireCatalog(root: HTMLElement, { onSelect, onToggleSidebar, onToggleTheme, onToggleSecondary, urlParam, selectAction, toggleSidebarAction, toggleThemeAction, toggleSecondaryAction, }: WireCatalogOptions): () => void;
25
+
26
+ export { type WireCatalogOptions, wireCatalog };
@@ -0,0 +1,50 @@
1
+ import { delegate } from 'kerfjs';
2
+
3
+ // src/wire-catalog.ts
4
+ function wireCatalog(root, {
5
+ onSelect,
6
+ onToggleSidebar,
7
+ onToggleTheme,
8
+ onToggleSecondary,
9
+ urlParam,
10
+ selectAction = "catalog-select",
11
+ toggleSidebarAction = "catalog-toggle-sidebar",
12
+ toggleThemeAction = "catalog-toggle-theme",
13
+ toggleSecondaryAction = "catalog-toggle-secondary"
14
+ }) {
15
+ const select = (id) => {
16
+ onSelect(id);
17
+ const view = root.ownerDocument.defaultView;
18
+ if (urlParam && view) {
19
+ const url = new URL(view.location.href);
20
+ url.searchParams.set(urlParam, id);
21
+ view.history.replaceState(null, "", url);
22
+ }
23
+ };
24
+ const disposers = [
25
+ delegate(root, "click", `[data-action="${selectAction}"]`, (_event, element) => {
26
+ const id = element.dataset.itemId;
27
+ if (id) select(id);
28
+ }),
29
+ delegate(root, "change", '[data-catalog-related] [data-component="select"]', (event) => {
30
+ const value = event.target.value;
31
+ if (value) select(value);
32
+ })
33
+ ];
34
+ if (onToggleSidebar) {
35
+ disposers.push(delegate(root, "click", `[data-action="${toggleSidebarAction}"]`, () => onToggleSidebar()));
36
+ }
37
+ if (onToggleTheme) {
38
+ disposers.push(delegate(root, "click", `[data-action="${toggleThemeAction}"]`, () => onToggleTheme()));
39
+ }
40
+ if (onToggleSecondary) {
41
+ disposers.push(delegate(root, "click", `[data-action="${toggleSecondaryAction}"]`, () => onToggleSecondary()));
42
+ }
43
+ return () => {
44
+ for (const dispose of disposers.splice(0)) dispose();
45
+ };
46
+ }
47
+
48
+ export { wireCatalog };
49
+
50
+ //# sourceMappingURL=wire-catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wire-catalog.ts"],"names":[],"mappings":";;;AA0BO,SAAS,YACd,IAAA,EACA;AAAA,EACE,QAAA;AAAA,EACA,eAAA;AAAA,EACA,aAAA;AAAA,EACA,iBAAA;AAAA,EACA,QAAA;AAAA,EACA,YAAA,GAAe,gBAAA;AAAA,EACf,mBAAA,GAAsB,wBAAA;AAAA,EACtB,iBAAA,GAAoB,sBAAA;AAAA,EACpB,qBAAA,GAAwB;AAC1B,CAAA,EACY;AACZ,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,KAAe;AAC7B,IAAA,QAAA,CAAS,EAAE,CAAA;AACX,IAAA,MAAM,IAAA,GAAO,KAAK,aAAA,CAAc,WAAA;AAChC,IAAA,IAAI,YAAY,IAAA,EAAM;AACpB,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,IAAA,CAAK,SAAS,IAAI,CAAA;AACtC,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAA,EAAU,EAAE,CAAA;AACjC,MAAA,IAAA,CAAK,OAAA,CAAQ,YAAA,CAAa,IAAA,EAAM,EAAA,EAAI,GAAG,CAAA;AAAA,IACzC;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,SAAA,GAA+B;AAAA,IACnC,QAAA,CAAS,MAAM,OAAA,EAAS,CAAA,cAAA,EAAiB,YAAY,CAAA,EAAA,CAAA,EAAM,CAAC,QAAQ,OAAA,KAAY;AAC9E,MAAA,MAAM,EAAA,GAAM,QAAwB,OAAA,CAAQ,MAAA;AAC5C,MAAA,IAAI,EAAA,SAAW,EAAE,CAAA;AAAA,IACnB,CAAC,CAAA;AAAA,IACD,QAAA,CAAS,IAAA,EAAM,QAAA,EAAU,kDAAA,EAAoD,CAAC,KAAA,KAAU;AACtF,MAAA,MAAM,KAAA,GAAS,MAAM,MAAA,CAA4C,KAAA;AACjE,MAAA,IAAI,KAAA,SAAc,KAAK,CAAA;AAAA,IACzB,CAAC;AAAA,GACH;AACA,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,SAAA,CAAU,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,OAAA,EAAS,CAAA,cAAA,EAAiB,mBAAmB,CAAA,EAAA,CAAA,EAAM,MAAM,eAAA,EAAiB,CAAC,CAAA;AAAA,EAC3G;AACA,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,SAAA,CAAU,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,OAAA,EAAS,CAAA,cAAA,EAAiB,iBAAiB,CAAA,EAAA,CAAA,EAAM,MAAM,aAAA,EAAe,CAAC,CAAA;AAAA,EACvG;AACA,EAAA,IAAI,iBAAA,EAAmB;AACrB,IAAA,SAAA,CAAU,IAAA,CAAK,QAAA,CAAS,IAAA,EAAM,OAAA,EAAS,CAAA,cAAA,EAAiB,qBAAqB,CAAA,EAAA,CAAA,EAAM,MAAM,iBAAA,EAAmB,CAAC,CAAA;AAAA,EAC/G;AACA,EAAA,OAAO,MAAM;AACX,IAAA,KAAA,MAAW,OAAA,IAAW,SAAA,CAAU,MAAA,CAAO,CAAC,GAAG,OAAA,EAAQ;AAAA,EACrD,CAAA;AACF","file":"wire-catalog.js","sourcesContent":["import { delegate } from 'kerfjs';\n\nexport interface WireCatalogOptions {\n /** Invoked with the entry id when a sidebar item or a related-entry option is chosen. */\n onSelect: (id: string) => void;\n /** Invoked when the sidebar collapse/expand control is activated. */\n onToggleSidebar?: () => void;\n /** Invoked when the theme toggle is activated. */\n onToggleTheme?: () => void;\n /** Invoked when the secondary (ecosystem) group's disclosure toggle is activated. */\n onToggleSecondary?: () => void;\n /** When set, `?<urlParam>=<id>` is written on select via `history.replaceState`. */\n urlParam?: string;\n selectAction?: string;\n toggleSidebarAction?: string;\n toggleThemeAction?: string;\n toggleSecondaryAction?: string;\n}\n\n/**\n * Wire a {@link Catalog}'s interactions with one delegated listener set: sidebar\n * item selection (and the related-entry selector), the sidebar collapse toggle, and\n * the theme toggle. The app owns the `active`/`collapsed`/`theme` signals and updates\n * them in the callbacks; optionally mirror the active id into the URL via `urlParam`.\n * Returns a disposer.\n */\nexport function wireCatalog(\n root: HTMLElement,\n {\n onSelect,\n onToggleSidebar,\n onToggleTheme,\n onToggleSecondary,\n urlParam,\n selectAction = 'catalog-select',\n toggleSidebarAction = 'catalog-toggle-sidebar',\n toggleThemeAction = 'catalog-toggle-theme',\n toggleSecondaryAction = 'catalog-toggle-secondary',\n }: WireCatalogOptions,\n): () => void {\n const select = (id: string) => {\n onSelect(id);\n const view = root.ownerDocument.defaultView;\n if (urlParam && view) {\n const url = new URL(view.location.href);\n url.searchParams.set(urlParam, id);\n view.history.replaceState(null, '', url);\n }\n };\n\n const disposers: Array<() => void> = [\n delegate(root, 'click', `[data-action=\"${selectAction}\"]`, (_event, element) => {\n const id = (element as HTMLElement).dataset.itemId;\n if (id) select(id);\n }),\n delegate(root, 'change', '[data-catalog-related] [data-component=\"select\"]', (event) => {\n const value = (event.target as HTMLElement & { value?: string }).value;\n if (value) select(value);\n }),\n ];\n if (onToggleSidebar) {\n disposers.push(delegate(root, 'click', `[data-action=\"${toggleSidebarAction}\"]`, () => onToggleSidebar()));\n }\n if (onToggleTheme) {\n disposers.push(delegate(root, 'click', `[data-action=\"${toggleThemeAction}\"]`, () => onToggleTheme()));\n }\n if (onToggleSecondary) {\n disposers.push(delegate(root, 'click', `[data-action=\"${toggleSecondaryAction}\"]`, () => onToggleSecondary()));\n }\n return () => {\n for (const dispose of disposers.splice(0)) dispose();\n };\n}\n"]}
@@ -6,18 +6,18 @@ These are normative contracts for package components and consuming applications.
6
6
 
7
7
  - All controls have accessible names and visible `:focus-visible` treatment.
8
8
  - Decorative `LucideIcon` output is `aria-hidden`; pass `label` only when the SVG itself conveys meaning.
9
- - `MenuItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
- - `MenuHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
- - In any `.kui-pane`, menu rows, `MenuHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `MenuHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-menu-header-action-icon-size`; do not reduce the target to the visible icon.
9
+ - `ListItem` uses native button Enter/Space behavior. `selected` maps to `aria-current="page"`; `pressed` maps to `aria-pressed`. Its `trailing` content is dormant and must not contain controls. Its `rootAttributes` slot accepts only application `data-*` metadata and deliberately cannot emit `role="menuitem"` in isolation—a true ARIA menu must own arrow, Home/End, Escape, and focus behavior as one widget.
10
+ - `ListHeader` owns action naming, disabled state, and disclosure `aria-expanded`. Toggle mode supplies one decorative 18px `DisclosureArrow` when `actionIcon` is omitted; it mirrors `expanded` while the native button's accessible name stays stable. The application must update that controlled state and reveal or hide real content. A valid `count` is visually rendered in an `aria-hidden` neutral pill while the required localized `countLabel` becomes part of the owning heading or disclosure button's accessible name; zero remains a real count. Its narrow `triggerAttributes` slot may describe a native popover or controlled-content relationship with `popoverTarget`, `popoverTargetAction`, `aria-controls`, and `aria-haspopup`; the application owns the target surface and its focus/dismissal behavior.
11
+ - In any `.kui-pane`, menu rows, `ListHeader` actions, and toolbar groups keep a minimum 44px target in both dimensions. A `ListHeader` fills the available inline width and keeps that action at the logical end, including in RTL. Its action visual defaults to 18px through `--kui-list-header-action-icon-size`; do not reduce the target to the visible icon.
12
12
  - `LoadingSpinner` is either labeled (`role="img"`) or hidden. Its rotation stops for reduced motion.
13
13
  - `StateBanner` defaults to polite `role="status"`; use `urgency="alert"` only for an attention-requiring failure.
14
14
  - `EmptyState` reports busy state through `aria-busy` and never relies on an illustration as its label.
15
15
  - `Select` follows the Web Awesome host's standard `input`/`change` events. Application tests verify the live `value`, focus, and events—not attributes alone. Its decorative option icons and value-dependent selected content remain present after controlled rerenders, so the visible choice does not silently lose its non-text cue.
16
16
  - `DisclosureArrow` is an 18px root-scaled decorative visual by default, not an interaction target. Its owning native control supplies a stable accessible name, pointer and keyboard interaction, and `aria-expanded`; multiple arrows keep independently controlled state. Consumers may override `--kui-disclosure-arrow-size` without changing that ownership. Configured directions animate over the shortest path; a 180-degree closed-to-open tie uses counterclockwise rotation.
17
17
 
18
- ## MenuActionRow
18
+ ## ListActionRow
19
19
 
20
- `MenuActionRow` renders a noninteractive visual root containing primary and
20
+ `ListActionRow` renders a noninteractive visual root containing primary and
21
21
  trailing native buttons as siblings. The primary comes first in DOM and Tab
22
22
  order and alone receives `aria-current` or `aria-pressed`; the trailing button
23
23
  has its own required accessible name and disabled state. Both controls carry