@apliteni/apliteni-ui 0.66.0 → 0.68.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 +1 -1
- package/react/dist/index.d.ts +2 -2
- package/react/dist/index.js +3 -3
- package/src/components/filter-bar.js +18 -2
- package/src/index.d.ts +3 -0
- package/src/index.js +1 -0
- package/src/logic/filter-bar.js +29 -0
- package/src/styles/filter-bar.css +16 -1
package/package.json
CHANGED
package/react/dist/index.d.ts
CHANGED
|
@@ -483,8 +483,8 @@ declare function Segmented({ options, value, onChange, label, appearance, disabl
|
|
|
483
483
|
|
|
484
484
|
type Filter = {
|
|
485
485
|
id: string;
|
|
486
|
-
label: string;
|
|
487
|
-
value
|
|
486
|
+
label: string; /** empty or absent while nothing is chosen; the chip then shows `label` */
|
|
487
|
+
value?: string;
|
|
488
488
|
items: DropdownEntry[];
|
|
489
489
|
disabled?: boolean;
|
|
490
490
|
open?: boolean;
|
package/react/dist/index.js
CHANGED
|
@@ -1938,6 +1938,7 @@ function Segmented({ options, value, onChange, label, appearance = "pill", disab
|
|
|
1938
1938
|
|
|
1939
1939
|
// src/FilterBar.tsx
|
|
1940
1940
|
import { useRef as useRef11, useState as useState7 } from "react";
|
|
1941
|
+
import { filterChipText, filterChipName, filterChipUnset } from "@apliteni/apliteni-ui";
|
|
1941
1942
|
import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1942
1943
|
function FilterBar({ filters, label = "Filters", clearLabel = "Clear all filters", disabled = false, busy = false, onRemove, onClear, onChange }) {
|
|
1943
1944
|
const bar = useRef11(null);
|
|
@@ -1983,11 +1984,10 @@ function FilterBar({ filters, label = "Filters", clearLabel = "Clear all filters
|
|
|
1983
1984
|
/* @__PURE__ */ jsx23(
|
|
1984
1985
|
Dropdown,
|
|
1985
1986
|
{
|
|
1986
|
-
label: filter.label,
|
|
1987
|
-
value: filter.value,
|
|
1988
1987
|
items: filter.items,
|
|
1989
1988
|
variant: "select",
|
|
1990
|
-
ariaLabel:
|
|
1989
|
+
ariaLabel: filterChipName(filter),
|
|
1990
|
+
triggerContent: /* @__PURE__ */ jsx23("span", { className: filterChipUnset(filter) ? "ui-dropdown__value is-placeholder" : "ui-dropdown__value", children: filterChipText(filter) }),
|
|
1991
1991
|
open: !blocked && !filter.disabled && (opened[filter.id] && opened[filter.id].against === filter.open ? opened[filter.id].open : !!filter.open),
|
|
1992
1992
|
onOpenChange: (open) => setOpened((previous) => ({ ...previous, [filter.id]: { open, against: filter.open } })),
|
|
1993
1993
|
onSelect: (value) => {
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { button, esc } from './index.js';
|
|
2
2
|
import { dropdown, wireDropdown } from './dropdown.js';
|
|
3
|
+
import { filterChipText, filterChipName, filterChipUnset } from '../logic/filter-bar.js';
|
|
3
4
|
|
|
4
5
|
export function filterBar({ filters = [], label = 'Filters', clearLabel = 'Clear all filters', disabled = false, busy = false } = {}) {
|
|
5
6
|
return `<fieldset class="ui-filter-bar" data-filter-bar${disabled || busy ? ' disabled' : ''}${busy ? ' aria-busy="true"' : ''}>`
|
|
6
7
|
+ `<legend class="ui-filter-bar__legend">${esc(label)}</legend>`
|
|
7
8
|
+ filters.map(filter => `<fieldset class="ui-filter-bar__chip" data-filter-id="${esc(filter.id)}"${filter.disabled ? ' disabled' : ''}>`
|
|
8
9
|
+ `<legend class="ui-filter-bar__legend">${esc(filter.label)}</legend>`
|
|
9
|
-
+ dropdown({
|
|
10
|
+
+ dropdown({ items: filter.items || [], variant: 'select',
|
|
11
|
+
// The chip prints one line; the field's name reaches a reader through the
|
|
12
|
+
// trigger's name and the chip's own legend. why: docs/specification.md#dense-financial-tables
|
|
13
|
+
triggerContent: `<span class="${filterChipUnset(filter) ? 'ui-dropdown__value is-placeholder' : 'ui-dropdown__value'}">${esc(filterChipText(filter))}</span>`,
|
|
14
|
+
ariaLabel: filterChipName(filter), open: !!filter.open && !disabled && !busy && !filter.disabled })
|
|
10
15
|
+ `<button type="button" class="ui-filter-bar__remove" data-filter-remove aria-label="${esc(`Remove ${filter.label} filter`)}">×</button></fieldset>`).join('')
|
|
11
16
|
+ `<span data-filter-clear>${button({ label: clearLabel, size: 'sm', variant: 'ghost', disabled: !filters.length })}</span></fieldset>`;
|
|
12
17
|
}
|
|
@@ -46,7 +51,18 @@ export function initFilterBar(host, options = {}) {
|
|
|
46
51
|
if (item.getAttribute('aria-disabled') !== 'true') {
|
|
47
52
|
const id = chip?.dataset.filterId, value = item.dataset.value;
|
|
48
53
|
// Dropdown completes its own close/focus before the consumer replaces the markup.
|
|
49
|
-
queueMicrotask(() =>
|
|
54
|
+
queueMicrotask(() => {
|
|
55
|
+
// Dropdown writes the picked row's label into the trigger; the name it is
|
|
56
|
+
// read by follows it, so the two cannot disagree while the consumer answers.
|
|
57
|
+
const filter = (current.filters || []).find(f => f.id === id);
|
|
58
|
+
const trigger = chip?.querySelector('[data-dropdown-trigger]');
|
|
59
|
+
const span = trigger?.querySelector('.ui-dropdown__value');
|
|
60
|
+
const shown = span?.textContent;
|
|
61
|
+
if (filter && trigger) trigger.setAttribute('aria-label', filterChipName({ ...filter, value: shown }));
|
|
62
|
+
// A chip that was empty stops being a placeholder the moment it shows a pick.
|
|
63
|
+
if (span) span.classList.toggle('is-placeholder', filterChipUnset({ value: shown }));
|
|
64
|
+
emit('ui-filter-change', { id, value });
|
|
65
|
+
});
|
|
50
66
|
}
|
|
51
67
|
}
|
|
52
68
|
};
|
package/src/index.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ export declare function numericValue(opts?: Record<string, unknown>): string;
|
|
|
7
7
|
export declare function deltaValue(opts?: Record<string, unknown>): string;
|
|
8
8
|
export declare function filterBar(opts?: Record<string, unknown>): string;
|
|
9
9
|
export declare function initFilterBar(host: Element, opts?: Record<string, unknown>): { update: (opts: Record<string, unknown>) => void; destroy: () => void };
|
|
10
|
+
export declare function filterChipText(filter?: { label?: string; value?: string }): string;
|
|
11
|
+
export declare function filterChipName(filter?: { label?: string; value?: string }): string;
|
|
12
|
+
export declare function filterChipUnset(filter?: { value?: string }): boolean;
|
|
10
13
|
export declare function segmentedNextIndex(key: string, index: number, length: number): number | null;
|
|
11
14
|
export declare function icon(name: string, className?: string): string;
|
|
12
15
|
export declare function snippet(opts?: Record<string, unknown>): string;
|
package/src/index.js
CHANGED
|
@@ -30,6 +30,7 @@ export * from './components/filter-bar.js';
|
|
|
30
30
|
export { dropdownMatch, dropdownFiltering } from './logic/dropdown.js';
|
|
31
31
|
export { SCORE, rankGroups, rankCommands, scoreCommand, paletteHotkey } from './logic/command-palette.js';
|
|
32
32
|
export { segmentedNextIndex } from './logic/segmented.js';
|
|
33
|
+
export { filterChipText, filterChipName, filterChipUnset } from './logic/filter-bar.js';
|
|
33
34
|
export { PAGE_SIZES, DEFAULT_PAGE_SIZE } from './logic/pagination.js';
|
|
34
35
|
export { calloutIcons } from './logic/callout.js';
|
|
35
36
|
export { formatNumericValue, formatDeltaValue } from './logic/table-values.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* What a filter chip shows, what it is called, and when it counts as unset.
|
|
2
|
+
* The chip prints the chosen value by itself — never the field's name beside it
|
|
3
|
+
* — and the field's name stands in while nothing is chosen. Both faces ask
|
|
4
|
+
* these, so a chip reads the same in vanilla and in React.
|
|
5
|
+
*
|
|
6
|
+
* `value` is display text: it is the chip's only visible line, so a consumer
|
|
7
|
+
* answers a change with text a reader can read, not a raw code.
|
|
8
|
+
* why: docs/specification.md#dense-financial-tables
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const text = (v) => (v == null ? '' : String(v));
|
|
12
|
+
|
|
13
|
+
/** No value chosen: absent, null or the empty string. */
|
|
14
|
+
export function filterChipUnset({ value } = {}) {
|
|
15
|
+
return text(value) === '';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** The one line the chip prints: the value, or the field's name while unset. */
|
|
19
|
+
export function filterChipText(filter = {}) {
|
|
20
|
+
return filterChipUnset(filter) ? text(filter.label) : text(filter.value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** What a reader hears: the field, and its value or the empty state. A filter
|
|
24
|
+
* with no label is named by its value alone — "undefined" is not a name. */
|
|
25
|
+
export function filterChipName(filter = {}) {
|
|
26
|
+
const field = text(filter.label);
|
|
27
|
+
if (!field) return text(filter.value);
|
|
28
|
+
return filterChipUnset(filter) ? `${field}: any` : `${field}: ${text(filter.value)}`;
|
|
29
|
+
}
|
|
@@ -7,4 +7,19 @@
|
|
|
7
7
|
.ui-filter-bar:focus-visible { outline: 2px solid transparent; box-shadow: var(--ring); }
|
|
8
8
|
.ui-filter-bar button:disabled { /* muted-ink: state — disabled filters */ color: var(--disabled-ink); cursor: not-allowed; }
|
|
9
9
|
|
|
10
|
-
.ui-filter-bar .ui-
|
|
10
|
+
.ui-filter-bar .ui-dropdown__value { color: inherit; }
|
|
11
|
+
/* A chip with nothing chosen prints the field's name in the slot the value will
|
|
12
|
+
take. `:enabled` leaves a disabled chip its own ink. why: guidelines/labels-and-titles.md */
|
|
13
|
+
.ui-filter-bar button:enabled .ui-dropdown__value.is-placeholder { /* muted-ink: placeholder — a filter chip with nothing chosen */ color: var(--muted); }
|
|
14
|
+
|
|
15
|
+
/* A chip's panel is as wide as the chip's trigger, never the 240px floor
|
|
16
|
+
.ui-dropdown__panel gives a standalone dropdown. A panel is positioned at its
|
|
17
|
+
trigger's inline start, so on a narrow row a later chip put its panel past the
|
|
18
|
+
screen — shut as well as open, a `visibility: hidden` box still being laid out.
|
|
19
|
+
The percentage resolves against .ui-dropdown, the panel's containing block,
|
|
20
|
+
which is the trigger's width because the trigger is its one in-flow child.
|
|
21
|
+
`overflow-wrap` keeps that a bound rather than a clip: a panel is
|
|
22
|
+
`overflow: visible`, so a value with no break opportunity would run out of it
|
|
23
|
+
and off the page. It inherits, reaching an option's label and description.
|
|
24
|
+
why: docs/specification.md#a-filter-row-holds-its-panels */
|
|
25
|
+
.ui-filter-bar .ui-dropdown__panel { min-width: 100%; max-width: 100%; overflow-wrap: anywhere; }
|