@apliteni/apliteni-ui 0.105.0 → 0.107.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.105.0",
3
+ "version": "0.107.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
@@ -469,6 +469,8 @@ type DropdownProps = {
469
469
  /** Replaces the trigger's label/value pair. With `ariaLabel`, names the trigger. */
470
470
  triggerContent?: ReactNode;
471
471
  triggerClass?: string;
472
+ /** The trigger is off: no stop, no panel, the kit's unavailable paint. #580 */
473
+ disabled?: boolean;
472
474
  chevron?: boolean;
473
475
  /** The edge the panel hugs. */
474
476
  align?: 'start' | 'end';
@@ -490,7 +492,7 @@ type DropdownProps = {
490
492
  /** Draw the row yourself. `props` is everything the row needs; spread it. */
491
493
  row?: (item: DropdownItem, props: DropdownRowProps) => ReactNode;
492
494
  };
493
- declare function Dropdown({ label, value, placeholder, variant, items, sections, header, footer, triggerContent, triggerClass, chevron, align, direction, scroll, search, ariaLabel, id, panelClass, open: openProp, defaultOpen, onOpenChange, onSelect, row, }: DropdownProps): react.JSX.Element;
495
+ declare function Dropdown({ label, value, placeholder, variant, items, sections, header, footer, triggerContent, triggerClass, disabled, chevron, align, direction, scroll, search, ariaLabel, id, panelClass, open: openProp, defaultOpen, onOpenChange, onSelect, row, }: DropdownProps): react.JSX.Element;
494
496
 
495
497
  type BackLinkOwnProps = {
496
498
  /** The address. No address, or a `javascript:` one, and nothing is rendered. */
@@ -1939,6 +1939,7 @@ function Dropdown({
1939
1939
  footer,
1940
1940
  triggerContent,
1941
1941
  triggerClass = "",
1942
+ disabled = false,
1942
1943
  chevron = true,
1943
1944
  align = "start",
1944
1945
  direction = "down",
@@ -2235,6 +2236,7 @@ function Dropdown({
2235
2236
  trigger.current?.focus();
2236
2237
  };
2237
2238
  const onKeyDown = (e) => {
2239
+ if (disabled && !open) return;
2238
2240
  const onTrigger = e.target === trigger.current;
2239
2241
  const onField = e.target === field.current;
2240
2242
  if (onField && composing(e)) return;
@@ -2299,14 +2301,14 @@ function Dropdown({
2299
2301
  if (!isRow(entry)) {
2300
2302
  return /* @__PURE__ */ jsx20("div", { className: "ui-dropdown__sep", role: "separator", hidden: filtering || void 0 }, key);
2301
2303
  }
2302
- const disabled = !!entry.disabled;
2304
+ const disabled2 = !!entry.disabled;
2303
2305
  const selected = selectedOf(entry);
2304
- const asLink = !!entry.href && !disabled && !asOption;
2306
+ const asLink = !!entry.href && !disabled2 && !asOption;
2305
2307
  const props = {
2306
2308
  className: cx6(
2307
2309
  "ui-dropdown__item",
2308
2310
  selected && "is-selected",
2309
- disabled && "is-disabled",
2311
+ disabled2 && "is-disabled",
2310
2312
  entry.danger && "is-danger",
2311
2313
  Boolean(sx) && open && entry === active && "is-active"
2312
2314
  ),
@@ -2315,7 +2317,7 @@ function Dropdown({
2315
2317
  tabIndex: -1,
2316
2318
  ...entry.value != null ? { "data-value": String(entry.value) } : null,
2317
2319
  ...asOption ? { "aria-selected": selected } : null,
2318
- ...disabled ? { "aria-disabled": true } : null,
2320
+ ...disabled2 ? { "aria-disabled": true } : null,
2319
2321
  ...asLink ? { href: entry.href, ...entry.target ? { target: entry.target } : null } : null,
2320
2322
  ...sx ? { id: rowIds.get(entry), hidden: hiddenBy(entry) || void 0 } : null,
2321
2323
  onClick: (e) => choose(entry, e),
@@ -2362,6 +2364,7 @@ function Dropdown({
2362
2364
  type: "button",
2363
2365
  className: cx6("ui-dropdown__trigger", triggerClass),
2364
2366
  "data-dropdown-trigger": "",
2367
+ disabled: disabled || void 0,
2365
2368
  "aria-haspopup": sx ? "dialog" : listRole,
2366
2369
  "aria-expanded": open,
2367
2370
  "aria-label": ariaLabel && triggerContent != null ? ariaLabel : void 0,
@@ -157,6 +157,8 @@ function ddSearchBody({ items, sections }, sx, name, scroll) {
157
157
  * @param {boolean} [o.portal] mount the panel on <body>, for a clipping or sticky ancestor
158
158
  * @param {boolean|number} [o.scroll] true, or a maxHeight in px, to cap and scroll
159
159
  * @param {boolean} [o.open] render already-open (handy for screenshots)
160
+ * @param {boolean} [o.disabled] the trigger is off: no stop, no panel, the kit's
161
+ * unavailable paint. #580
160
162
  * @param {string} [o.ariaLabel] accessible name for the panel and trigger
161
163
  * @param {boolean|object} [o.search] true, or { placeholder, label, empty, hint, query } —
162
164
  * a field above the rows that filters them; `empty` may carry {q}
@@ -166,7 +168,7 @@ export function dropdown({
166
168
  label, value, placeholder = 'Select…', variant, items, sections,
167
169
  foot = '', header = '', footer = '', triggerContent, triggerClass = '', chevron = true,
168
170
  align = 'start', direction = 'down', portal = false,
169
- scroll = false, open = false, ariaLabel, id, panelClass = '', search = false,
171
+ scroll = false, open = false, disabled = false, ariaLabel, id, panelClass = '', search = false,
170
172
  } = {}) {
171
173
  const flat = sections ? sections.flatMap((s) => s.items || []) : (items || []);
172
174
  const isSelect = variant === 'select' || (variant == null && flat.some((it) => it && (it.selected || it.value != null)));
@@ -185,6 +187,9 @@ export function dropdown({
185
187
  'data-dropdown-trigger',
186
188
  `aria-haspopup="${sx ? 'dialog' : listRole}"`,
187
189
  `aria-expanded="${open ? 'true' : 'false'}"`,
190
+ // The attribute, not aria-disabled: a dropdown's trigger carries no message a
191
+ // reader has to stop on, so it leaves the tab order the way .ui-btn's does.
192
+ disabled ? 'disabled' : '',
188
193
  ariaLabel && triggerContent != null ? `aria-label="${esc(ariaLabel)}"` : '',
189
194
  ].filter(Boolean).join(' ');
190
195
 
@@ -269,6 +274,17 @@ const DD_GAP = 9;
269
274
  // below asks the container for its panel rather than querying inside it.
270
275
  const ddPanelOf = (dd) => dd.__ddPanel || dd.querySelector('[data-dropdown-panel]');
271
276
 
277
+ // A trigger the sheet paints off. The kit paints both spellings unavailable, so
278
+ // the wiring refuses both: the native attribute keeps the browser from
279
+ // dispatching anything, but an aria-disabled trigger is still a keyboard stop,
280
+ // and a click or an Enter on it reached openDropdown() until #580's round two.
281
+ // ddItemsOf() above refuses an aria-disabled ROW the same way.
282
+ // why: docs/foundations.md#colour-and-contrast
283
+ const ddTriggerOff = (dd) => {
284
+ const trigger = dd.querySelector('[data-dropdown-trigger]');
285
+ return !!trigger && (trigger.disabled === true || trigger.getAttribute('aria-disabled') === 'true');
286
+ };
287
+
272
288
  function ddGap(panel) {
273
289
  const declared = parseFloat(ddViewOf(panel).getComputedStyle(panel).getPropertyValue('--ui-dropdown-gap'));
274
290
  return Number.isFinite(declared) ? declared : DD_GAP;
@@ -836,6 +852,9 @@ function closeAllDropdowns(except) {
836
852
  }
837
853
 
838
854
  function openDropdown(dd, focusIdx) {
855
+ // Every way in goes through here — the trigger's click, the Enter the browser
856
+ // turns into one, and the arrows — so one refusal covers them all. #580
857
+ if (ddTriggerOff(dd)) return;
839
858
  closeAllDropdowns(dd);
840
859
  const panel = ddPanelOf(dd);
841
860
  const search = ddSearchOf(dd);
@@ -18,7 +18,11 @@
18
18
  font-weight: 500;
19
19
  color: var(--text);
20
20
  background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
21
- border: 1px solid var(--border);
21
+ /* The kit's live control edge, the one .ui-btn takes on the same fill. --border
22
+ is what --disabled-border resolves to in LIGHT, so a live trigger used to draw
23
+ the exact line the kit paints an unavailable control with. #580
24
+ why: docs/foundations.md#colour-and-contrast */
25
+ border: 1px solid var(--control-edge);
22
26
  border-radius: var(--radius-pill);
23
27
  padding: 7px 11px 7px 13px;
24
28
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
@@ -29,6 +33,28 @@
29
33
  what the kit's rows already chose. why: docs/foundations.md#the-focus-ring */
30
34
  .ui-dropdown__trigger:hover:not(:focus-visible) { border-color: var(--accent); }
31
35
  .ui-dropdown__trigger:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
36
+ /* Off. The off hover-not-focus selectors are (0,4,0), above live hover's (0,3,0).
37
+ A pointer over a dead control changes nothing while an aria-disabled one keeps its stop and the ring.
38
+ No fill: --disabled-surface IS this control's --surface, in both themes. The
39
+ activation is refused in ddTriggerOff(), not with pointer-events, which would
40
+ take that stop away. #580 why: docs/foundations.md#colour-and-contrast */
41
+ .ui-dropdown__trigger:disabled,
42
+ .ui-dropdown__trigger:disabled:hover,
43
+ .ui-dropdown__trigger:disabled:hover:not(:focus-visible),
44
+ .ui-dropdown__trigger:disabled:focus-visible,
45
+ .ui-dropdown__trigger[aria-disabled="true"]:hover,
46
+ .ui-dropdown__trigger[aria-disabled="true"]:hover:not(:focus-visible),
47
+ .ui-dropdown__trigger[aria-disabled="true"]:focus-visible,
48
+ .ui-dropdown__trigger[aria-disabled="true"] {
49
+ border-color: var(--disabled-border);
50
+ /* muted-ink: state — unavailable control */ color: var(--disabled-ink);
51
+ cursor: not-allowed;
52
+ }
53
+ /* The prefix and the value carry inks of their own, so they take the state's. */
54
+ .ui-dropdown__trigger:disabled .ui-dropdown__pre,
55
+ .ui-dropdown__trigger:disabled .ui-dropdown__value,
56
+ .ui-dropdown__trigger[aria-disabled="true"] .ui-dropdown__pre,
57
+ .ui-dropdown__trigger[aria-disabled="true"] .ui-dropdown__value { color: inherit; }
32
58
  .ui-dropdown__pre { color: var(--text); }
33
59
  .ui-dropdown__value { color: var(--strong); font-weight: 600; letter-spacing: 0.01em; }
34
60
 
@@ -206,10 +206,10 @@
206
206
  transition: background var(--dur-fast) var(--ease);
207
207
  }
208
208
  /* Menu rows use the reading surface and an edge on hover — see
209
- src/styles/dropdown.css:245 `.ui-dropdown__item:hover`.
209
+ src/styles/dropdown.css:271 `.ui-dropdown__item:hover`.
210
210
  why: docs/foundations.md#elevation */
211
211
  .vopt:hover { outline: 1px solid var(--control-edge); outline-offset: -1px; background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */ }
212
- /* The row is a keyboard stop: src/components/dropdown.js:1088
212
+ /* The row is a keyboard stop: src/components/dropdown.js:1107
213
213
  `items[next < 0 ? 0 : next]?.focus()` moves focus onto a `[data-dd-item]` with
214
214
  the arrow keys, and this is one. It takes the ring on its own 10px radius, the
215
215
  same way `.amenu a` above does. #482 */