@andes-ng/primitives 0.1.0-rc.2 → 0.2.0-rc.1
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/README.md +15 -116
- package/package.json +18 -1
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
# @andes-ng/primitives
|
|
2
2
|
|
|
3
|
-
Andes NG's own accessible, unstyled Angular behavior directives
|
|
3
|
+
Andes NG's own accessible, unstyled Angular behavior directives and signals: the layer `@andes-ng/ui` builds its components on. They set no styling and no ARIA beyond the pattern they implement, so they can also be used on their own.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What is in the package
|
|
6
|
+
|
|
7
|
+
| Primitive | What it does | Used today by |
|
|
8
|
+
| --------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------- |
|
|
9
|
+
| `AndesButtonPrimitive` | Disabled state and anchor click prevention for button-like elements | `AndesButton` |
|
|
10
|
+
| `AndesOverlayPrimitive` (+ trigger, content, close) | Portal, anchored or centered positioning, focus handling, inert background, exit animation | Panels in upcoming components |
|
|
11
|
+
| `AndesListNavigation` | Roving tabindex or `aria-activedescendant` keyboard navigation with typeahead | Upcoming components |
|
|
12
|
+
| [Choice group](#choice-group) | One tab stop, arrow keys, single or multiple selection for radio, tabs, toggle and listbox patterns | Upcoming components |
|
|
13
|
+
| [Tree navigation](#tree-navigation) | Keyboard model of trees, cascaders and menus | Upcoming components |
|
|
14
|
+
| [Viewport](#viewport) | Element size, media query and breakpoint signals | Upcoming components |
|
|
15
|
+
| [Form control](#form-control) | `ControlValueAccessor` base that works with reactive, template-driven and signal forms | Upcoming components |
|
|
16
|
+
| [Selection model](#selection-model) | Single, multiple and checked-tree selection signals | Upcoming components |
|
|
17
|
+
|
|
18
|
+
Everything is exported from `@andes-ng/primitives`.
|
|
6
19
|
|
|
7
20
|
## Choice group
|
|
8
21
|
|
|
@@ -59,17 +72,6 @@ export class ViewSwitcher {
|
|
|
59
72
|
- **Right-to-left** is read from the surrounding `dir` (CDK `Directionality`), or forced with `textDirection`.
|
|
60
73
|
- **Activation.** The group handles `Space`/`Enter` itself and cancels the default action, so a `<button>` does not also fire a click and a `<div role="radio">` behaves the same as a button. The tab stop returns to the selected item whenever focus leaves the group, so manual-activation tabs and toggle groups re-enter where the choice is.
|
|
61
74
|
|
|
62
|
-
### Which components should adopt this
|
|
63
|
-
|
|
64
|
-
Paths are under `packages/ui/src/lib/`.
|
|
65
|
-
|
|
66
|
-
- **`segmented/`** — `segmented.ts` `onKeydown` (filters enabled options, mirrors left/right for RTL, wraps, handles `Home`/`End`, then focuses and selects), the `tabbable` computed and `AndesSegmentedOption.tabIndex`/`button` role="radio" logic. Replace with `andesChoiceGroup role="radiogroup"` on the host and `andesChoiceItem` on the option's `<button>`; keep the sliding thumb measuring against `group.items()`/`selectedItem()`. Keep the hidden form input and `compareWith` pass-through.
|
|
67
|
-
- **`toggle/`** — `toggle-group.ts` (`onKeydown` focus-only roving, `tabbable`, `toggle()`, `isPressed`, the `single`/`multiple` + `allowEmpty` value rules) and `toggle.ts` `tabIndex`. Replace with `andesChoiceGroup role="group"` + `[mode]` + `[required]` (`allowEmpty` is the inverse of `required`), and `andesChoiceItem` on the inner `<button>`. A standalone `<andes-toggle>` stays as it is.
|
|
68
|
-
- **`tabs/`** — `tabs-trigger.ts` (the automatic-activation `effect` that selects when the trigger becomes the active item, plus its `aria-selected`/`aria-controls` bindings) and the tab-stop sync in `tabs.ts` (`syncActiveItem`, the effect around line 475). `AndesTabsList` already forwards keys to `AndesListNavigation`; swap that for `andesChoiceGroup role="tablist"` (`selectionFollowsFocus` = `activationMode() === 'automatic'`) and the trigger's `<button>` for `andesChoiceItem`. The fallback-to-first-enabled-tab rule, overflow menu, drag-reorder and editable-card removal stay in `AndesTabs`.
|
|
69
|
-
- **`radio-group/`** — a lower-priority adopter: it uses native `<input type="radio">`, which already provide roving focus and form participation, so there is no hand-written `keydown` to remove. Adopt only for the `optionType="button"` face if it should stop depending on hidden native inputs.
|
|
70
|
-
|
|
71
|
-
Not a fit, deliberately: **`rate/`** is a `role="slider"` over a number (`onKeydown` steps a value by 1 or 0.5, and digit keys set it) — a different pattern with no item set to choose from. **`accordion/`** triggers keep every trigger in the natural tab order and optionally move focus with the arrows (`accordion.ts` `onKeydown`); there is no selection, only `aria-expanded` per item, so it should adopt `AndesListNavigation` directly rather than this.
|
|
72
|
-
|
|
73
75
|
## Tree navigation
|
|
74
76
|
|
|
75
77
|
`AndesTreeNavigation` is the keyboard model of every hierarchical widget: a tree, a cascading picker, a menu with fly-out submenus. It owns the order of the _visible_ nodes (collapsed subtrees are skipped), the active node, the expanded keys, the WAI-ARIA TreeView keys, and the ARIA facts each node needs. It sets no `role`, owns no selection, and never interprets `Enter` or `Space` — activation stays with the component.
|
|
@@ -129,19 +131,6 @@ What the item gives the template, all as signals: `ariaLevel`, `ariaPosinset`, `
|
|
|
129
131
|
- **Menus and columns.** `scope: 'level'` keeps `Up`/`Down`/`Home`/`End`/typeahead inside the active node's sibling group; `enterOnExpand` makes the open key also step into the first child (waiting for lazy children to arrive); `collapseOnLeave` closes the parent when stepping back out; `orientation: 'horizontal'` swaps the axes for a bar; `wrap` wraps within the scope.
|
|
130
132
|
- **Disabled.** Skipped by default (`skipDisabled`). With `skipDisabled: false` they hold focus, and `expandDisabled` decides whether the keyboard may still open them.
|
|
131
133
|
|
|
132
|
-
### Which components should adopt this
|
|
133
|
-
|
|
134
|
-
The behavior below is what each migration deletes; all paths are under `packages/ui/src/lib/`.
|
|
135
|
-
|
|
136
|
-
- `tree/tree.ts` — `onKeydown` (Up/Down/Right/Left/Home/End/`*`), `runTypeahead`, `expandSiblings`, the `activeKey`/`tabbableKey`/`visibleRows`/`visibleIndex` computeds and `focusRow`. The tree keeps selection, checking and drag-and-drop; it feeds `setNodes` from its flat rows (which also makes its virtual mode work, replacing `scrollToKey` for keyboard reveal) and binds `expandedKeys`. Its "disabled but still openable" nodes map to `skipDisabled: false` + `expandDisabled: true`.
|
|
137
|
-
- `tree-select/tree-select.ts` — hosts a `tree` and forwards `ArrowDown` into it (`focusTree`); it gets this behavior through `tree` and needs no keyboard code of its own beyond that hand-off.
|
|
138
|
-
- `cascader/cascader.ts` — `onKeydown` and `moveSibling`/`expandCursor`/`edgeNode`/`cursorColumn`: Up/Down and Home/End inside a column, Right into the next column (waiting on `ensureLoaded` for lazy columns), Left to the parent. That is `scope: 'level'`, `enterOnExpand`, `focusMode: 'active-descendant'` and a node source built from its `tree().roots`; the pending-expand bookkeeping (`pendingExpand`) goes away.
|
|
139
|
-
- `menu/menu.ts`, `menu/sub-menu.ts`, `menu/menu-row.ts` — `keydown` (Up/Down, `forward`/`backward` into and out of submenus, Home/End), `typeahead` with its buffer and timer, `ensureTabStop`/`tabStopSignal` and `navigableRows`. That is `scope: 'level'`, `enterOnExpand`, `collapseOnLeave`, `wrap`. Items in an inline submenu and in popup submenus share one navigation through the injector.
|
|
140
|
-
- `menubar/menubar.ts` (`onKeydown`, `jumpTo`, `moveFrom` over the triggers) — a flat `orientation: 'horizontal'` navigation with `Down` as the open key; `menubar/menubar-menu.ts` already forwards panel keys to a list navigation and can switch to a tree navigation per panel when the panel has fly-out submenus.
|
|
141
|
-
- `navigation-menu/navigation-menu.ts` — the bar handler (`forward`/`backward`, Home/End, `ArrowDown` to open content, `moveAlongBar`) and the content handler (Arrow/Home/End over `focusables`) are two flat navigations, horizontal for the bar and vertical with `wrap` for the content.
|
|
142
|
-
- `accordion/accordion.ts` — `onKeydown` (Up/Down/Home/End over `TRIGGER_CONTROL_SELECTOR`, wrapping): a flat vertical navigation with `wrap: true` and `typeahead: false`; expansion stays with the accordion's own item state because Enter/Space toggle it.
|
|
143
|
-
- `breadcrumb/breadcrumb.ts` has no arrow-key code of its own (its overflow dropdown reuses the menu), so it needs nothing until a roving toolbar-style breadcrumb is wanted: then it is a flat horizontal navigation.
|
|
144
|
-
|
|
145
134
|
## Viewport
|
|
146
135
|
|
|
147
136
|
Signals for "how big is it" and "what does the environment prefer", with the server guard, the missing-API guard and the teardown written once. Everything is a no-op on the server and where `ResizeObserver` / `matchMedia` do not exist, and every listener is removed with the owning injector.
|
|
@@ -196,33 +185,6 @@ Behavior worth knowing:
|
|
|
196
185
|
- **Container buckets** map a name to the minimum width where it starts (the same shape as `ANDES_BREAKPOINTS`), and the signal changes only when a threshold is crossed. Pass a signal of buckets when the thresholds come from inputs.
|
|
197
186
|
- `andesMediaQuery` accepts a `Signal<string>` for queries that depend on an input and moves its listener when the query changes. Each call owns one listener; use `AndesBreakpoints` for viewport widths, which is shared by the whole application.
|
|
198
187
|
|
|
199
|
-
### Which components should adopt this
|
|
200
|
-
|
|
201
|
-
Paths are in `packages/ui/src/lib/`.
|
|
202
|
-
|
|
203
|
-
Element size (`andesElementSize` / `AndesResizeObserver`), replacing a hand-written `new ResizeObserver` with its `typeof ResizeObserver` guard and `disconnect()`:
|
|
204
|
-
|
|
205
|
-
- `alert/alert.ts`: marquee track width (`track.offsetWidth` into `marqueeDistance`).
|
|
206
|
-
- `avatar/avatar-fallback.ts`: host and text width, to scale the fallback text to fit.
|
|
207
|
-
- `descriptions/descriptions.ts`: host width (`getBoundingClientRect().width`) to pick the column count.
|
|
208
|
-
- `steps/steps.ts`: host width against `breakpoint()` to switch to the narrow layout (a good fit for `andesContainerBreakpoint`).
|
|
209
|
-
- `grid/grid-row.ts`: the container breakpoint (`andesBreakpointForWidth` over `contentRect.width`); becomes `andesContainerBreakpoint(host, ANDES_BREAKPOINTS)`.
|
|
210
|
-
- `scroll-area/scroll-area.ts`: viewport and content boxes, to re-measure the scrollbars.
|
|
211
|
-
- `collapsible/collapsible-content.ts`: inner `scrollHeight`, to publish the content height variable.
|
|
212
|
-
- `masonry/masonry.ts`: host width, to re-run the layout (its own `requestAnimationFrame` coalescing and `window` resize listener also go away).
|
|
213
|
-
- `splitter/splitter.ts`: host size, to re-layout panels.
|
|
214
|
-
- `table/table.ts`, `card/card.ts`, `tabs/tabs.ts`, `menu/menu.ts`, `segmented/segmented.ts`: container, strip, nav and option sizes, to recompute scroll edges, fades, overflow and the thumb.
|
|
215
|
-
- `textarea/textarea.ts`: width/height deduped by hand against the last size (the primitive already de-duplicates).
|
|
216
|
-
- `select/select-trigger.ts`, `toast/toast-item.ts`, `spinner/spin.ts`, `typography/typography-base.ts`, `affix/affix.ts`: box, row, scroller and host sizes feeding re-positioning or truncation.
|
|
217
|
-
- `tooltip/tooltip.ts`, `hover-card/hover-card.ts`, `popover/popover-base.ts`, `tour/tour.ts`, `navigation-menu/navigation-menu.ts`: observe the floating panel or target to re-position; use `AndesResizeObserver.observe` with a callback.
|
|
218
|
-
|
|
219
|
-
Media queries (`andesMediaQuery` and the preference helpers), replacing `matchMedia` + `addEventListener('change')` + `removeEventListener`:
|
|
220
|
-
|
|
221
|
-
- `andesPrefersReducedMotion()`: `button/button.ts`, `carousel/carousel.ts` (kept in sync today), `alert/alert.ts`, `anchor/anchor.ts`, `masonry/masonry.ts`, `scroll-area/scroll-area.ts`, `statistic/countdown.ts`, `float-button/back-top.ts`, `tour/tour.ts`, `navigation-menu/navigation-menu.ts`. Most read it once with a bare `matchMedia(...)` call (some without a guard) and never update.
|
|
222
|
-
- `andesMediaQuery` with a width query: `pagination/pagination.ts` (`(max-width: 575.98px)`; better as `AndesBreakpoints.down('sm')`), `navigation-menu/navigation-menu.ts` (`(max-width: <mobileBreakpoint>px)` in an effect), `layout/sidebar-provider.ts` and `layout/layout-sider.ts` (`max-width: <breakpoint>px` below-breakpoint tests; `down(name)`).
|
|
223
|
-
|
|
224
|
-
Breakpoints (`AndesBreakpoints`, `ANDES_BREAKPOINTS`): `grid/breakpoints.ts` (its `AndesBreakpointService`, `ANDES_BREAKPOINTS` and `andesBreakpointForWidth` move here), `avatar/avatar-size.ts` (`injectAvatarBreakpoint` registers its own queries per avatar), and every consumer of `AndesBreakpointService.atLeast(name)` (`up(name)`).
|
|
225
|
-
|
|
226
188
|
## Form control
|
|
227
189
|
|
|
228
190
|
`AndesFormControlBase`, `AndesValueAccessor`, `provideAndesValueAccessor` and `injectAndesControlStatus` (in `src/lib/form-control/`) are the shared plumbing of every component that holds a value and has to work inside an Angular form: `writeValue`, `registerOnChange`, `registerOnTouched`, `setDisabledState`, the `disabled` input merged with the form's disabled state, touched on blur, and the validation status the `andes-form-field` wrapper needs.
|
|
@@ -314,45 +276,6 @@ protected readonly invalid = computed(() => this.ariaInvalid() || this.status.sh
|
|
|
314
276
|
|
|
315
277
|
`AndesFormControlBase` also satisfies signal forms' custom-control contract (`value` model, `disabled` input, `touch` output). A subclass that **omits** `provideAndesValueAccessor` binds to `[formField]` natively but cannot be used with reactive or template-driven forms; one that **provides** it works with all three, signal forms going through the accessor (a provided accessor wins over the contract). Components that must keep working with `formControl` / `ngModel` should provide it.
|
|
316
278
|
|
|
317
|
-
### Which components should adopt this
|
|
318
|
-
|
|
319
|
-
Every item below re-implements `writeValue` / `registerOnChange` / `registerOnTouched` / `setDisabledState`, a `disabledByForm` / `cvaDisabled` / `formDisabled` signal and an `isDisabled` computed of `disabled() || thatSignal()`, and a `providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(...), multi: true }]` block. Migrating deletes all of it. File names are under `packages/ui/src/lib/`.
|
|
320
|
-
|
|
321
|
-
| Component (file) | Value signal | `writeValue` does today / what stays component-specific |
|
|
322
|
-
| ------------------------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
323
|
-
| `input/input-base.ts` (`andes-input`, password, search) | `value` (`''`) | `null -> ''`, `String(value)`; text commits from `onInput` / `onCompositionEnd`; touched in `onBlur` |
|
|
324
|
-
| `input/input-otp.ts` | `value` (`''`) | `null -> ''`, `String(value)`; touched when focus leaves the slots |
|
|
325
|
-
| `textarea/textarea.ts` | `value` (`''`) | same as input; `onChange('')` on clear |
|
|
326
|
-
| `mentions/mentions.ts` | `value` (`''`) | same, plus `context.set(null)` (use `onWrite`) |
|
|
327
|
-
| `input-number/input-number.ts` | `value` (`number\|null`) | `coerce(value)`, resets `editing`, `syncText()` (use `coerce` + `onWrite`) |
|
|
328
|
-
| `rate/rate.ts` | `value` (`0`) | `normalizeValue(value, total())` (use `coerce`); `onChangeFn` / `onTouchedFn` naming |
|
|
329
|
-
| `slider/slider.ts` | `value` + `values` linked signal | number or array, normalized to the range; `setValues` skips equal arrays (use `equals`); touched at drag end |
|
|
330
|
-
| `segmented/segmented.ts` | `value` (`T\|null`) | passthrough; commit + touched on pick |
|
|
331
|
-
| `radio-group/radio-group.ts` | `value` (`T\|null`) | passthrough; `(focusout)` -> touched |
|
|
332
|
-
| `toggle/toggle-group.ts` | `value` (`T\|T[]\|null`) | passthrough; single or multiple |
|
|
333
|
-
| `checkbox/checkbox-group.ts` | `value` (`T[]`) | `value ?? []` (use `emptyValue` + `andesShallowArrayEquals`) |
|
|
334
|
-
| `select/select.ts` | `value` (`unknown`) | `value ?? ([] or null by mode)` (use `coerce`); commit, touched on select and on close |
|
|
335
|
-
| `tree-select/tree-select.ts` | `value` | `value ?? ([] or null by mode)`; touched on close |
|
|
336
|
-
| `cascader/cascader.ts` | `value` | passthrough; touched on close |
|
|
337
|
-
| `combobox/combobox.ts` | `value` (`T\|null`) | `value ?? null`, also resets the query text and backfill (use `onWrite`) |
|
|
338
|
-
| `color-picker/color-picker.ts` | `value` (`color\|null`) | `value ?? null`; touched on close |
|
|
339
|
-
| `date-picker/date-picker.ts` | single or range value | `applyValue`, clears both drafts (use `onWrite`); touched on close/blur |
|
|
340
|
-
| `transfer/transfer.ts` | `targetKeys` (`string[]`) | `Array.isArray(v) ? [...v] : []` (use `coerce`); array `equals` |
|
|
341
|
-
| `upload/upload-base.ts` (`upload`, dragger) | `fileList` (`File[]`) | `Array.isArray(v) ? v : []`; commits from many paths (add, remove, status change) |
|
|
342
|
-
| `checkbox/checkbox.ts` | `checked` (`boolean`) | `!!value`; also group participation and `indeterminate` stay in the component |
|
|
343
|
-
| `switch/switch.ts` | `checked` (`boolean`) | `!!value`; `loading` still gates the toggle |
|
|
344
|
-
| `toggle/toggle.ts` | `pressed` (`boolean`) | `!!value` |
|
|
345
|
-
|
|
346
|
-
The ones whose signal is not called `value` (`checkbox`, `switch`, `toggle`, `transfer`, `upload-base`) use `AndesValueAccessor` with the provider's second argument; the rest can extend `AndesFormControlBase`, except those that already extend a class (`input-base`, `upload-base`), which also use `AndesValueAccessor`.
|
|
347
|
-
|
|
348
|
-
Not covered by the primitive and left to the components: the form-field wrapper's `id` / `aria-describedby` forwarding (`form-field/form-control.ts`), which reads `NgControl` through the `andesFormControl` directive and can later read the same signals, and the page-level "submitted" rule.
|
|
349
|
-
|
|
350
|
-
### Decisions worth knowing
|
|
351
|
-
|
|
352
|
-
- `model()` cannot be written without emitting its `valueChange` output, so a form write also fires `valueChange` (it keeps a `[(value)]` binding next to a form control in sync). `commit()` and `onChange` are what tell a form about _user_ changes; use the component's own event output for user-only notifications.
|
|
353
|
-
- `commit()` does not check `disabled`. Whether an interaction may happen at all is the component's call, since only it knows which interactions count.
|
|
354
|
-
- Status updates need one change detection pass to attach. Reads before the first pass see the unbound defaults.
|
|
355
|
-
|
|
356
279
|
## Selection model
|
|
357
280
|
|
|
358
281
|
`AndesSelectionModel<T>` and `AndesCheckedTree<T, K>` hold the selection state that lists, tables, trees, transfer panes, cascaders and multi-selects each used to re-implement. They are plain signal-based classes: no DOM, no injection context, no teardown. Create one in a component field and bind its signals in the template.
|
|
@@ -410,27 +333,3 @@ readonly tree = new AndesCheckedTree<Folder, string>({
|
|
|
410
333
|
- Methods: `check`, `uncheck`, `toggle`, `checkAll`, `clear`, `setChecked(keys)` (programmatic), `prune()`.
|
|
411
334
|
- The value is a list of keys. Keys that match no node (a lazy branch not loaded yet, a page that is not rendered) are kept until `prune()`, and come back to life when the node reappears.
|
|
412
335
|
- The tree is indexed once per `nodes` change; every update is a single linear pass without recursion, so thousands of nodes (or a very deep chain) are fine. Treat nodes as immutable: replace the array or the node rather than mutating `children` in place.
|
|
413
|
-
|
|
414
|
-
### Which components should adopt this
|
|
415
|
-
|
|
416
|
-
Each item names the duplicated code in `packages/ui/src/lib/` and what the migration replaces.
|
|
417
|
-
|
|
418
|
-
| Component | Duplicated code today | Replace with |
|
|
419
|
-
| --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
420
|
-
| `table/table.ts`, `table/table-selection.ts` | `selectedKeys`, `selectedSet`, `selectAllState`, `toggleSelected` with its own `rangeAnchor` and Shift slice, `toggleAllSelected`, `selectAll`, `invertSelection`, `clearSelection`, `applySelection` | An `AndesSelectionModel<AndesTableRowKey>` (`mode` from `selectionType`; `isDisabled` from the disabled controls; `stateOf(enabledKeys())`, `selectRange`, `toggleAll`, `invert`, `clear`) |
|
|
421
|
-
| `table/table.ts` (`selectionTree`) | `partialSet`, `withDescendants`, `reconcileAncestors` and the depth sort | An `AndesCheckedTree<AndesTableRowKey>` fed with the existing `childrenOf` map (keys are the nodes) |
|
|
422
|
-
| `checkbox/checkbox-group-state.ts` | `_value`, `selectableValues`, `selectedCount`, `allSelected`, `someSelected`, `indeterminate`, `toggleItem`, `setAllSelected` | An `AndesSelectionModel<AndesCheckboxValue>`; items still register themselves, the group passes their values to `stateOf`/`toggleAll`. Its `order()` (template order instead of click order) is not part of the model: sort, then `setSelection` |
|
|
423
|
-
| `transfer/transfer.ts` | `selectedKeys`, `selectedSet`, the per-pane `allChecked`/`someChecked`, `toggleItem`, `setListSelection`, `toggleAll`, and the `anchors` record with `selectRange` | One model shared by both panes (or one per pane); `stateOf(pane.items())`, `selectRange`, `deselectAll(pane.items())`, `retainOnly` after items move |
|
|
424
|
-
| `list/list.ts` | `selected` with `Object.is` matching, `toggle` for single and multiple, `selectAll`, `clearSelection` | `AndesSelectionModel<unknown>` with `mode` from `selectable`; `select` for single, `toggle` for multiple |
|
|
425
|
-
| `select/select.ts`, `select/select-state.ts` | `selectedValues`, `compareWith` matching in `isSelected`/`isSelectedValue`, `maxCount` handling in the toggle path, removing a tag | `AndesSelectionModel<unknown>` with `compareWith` and `max: maxCount`; `toggle`, `deselect` for tag removal, `setSelection` for the value input |
|
|
426
|
-
| `tree-select/tree-select.ts` | `checkedSet`, `fromCheckState`, `strategy` and calls to `andesToggleCheck`/`andesCheckedKeysByStrategy` | The same `AndesCheckedTree` used by the embedded tree, `checkedKeysByStrategy(showCheckedStrategy)` |
|
|
427
|
-
| `tree/tree.ts`, `tree/tree-utils.ts` | `andesConductCheck`, `andesToggleCheck`, `andesCheckedKeysByStrategy`, `checkState`, `halfCheckedKeys`, `setAllChecked`, `toggleCheck` | `AndesCheckedTree` with `strict` bound to `checkStrictly`; `checked`/`halfChecked`/`checkedKeys` replace the derived sets. Note that a locked node here does not pass a cascade down even when it is itself pre-checked |
|
|
428
|
-
| `cascader/cascader.ts`, `cascader/cascader-tree.ts` | Multiple-value checking over leaves, branch aggregation and `showCheckedStrategy`/`collectTerminals` | An `AndesCheckedTree<AndesCascaderNode, string>` keyed by `pathKey`, `checkedKeysByStrategy` for `showCheckedStrategy` |
|
|
429
|
-
| `color-picker/color-picker.ts` | `isPresetSelected` (single preset selection) | Optional: a `mode: 'single'` model if presets gain multiple selection |
|
|
430
|
-
|
|
431
|
-
### Decisions and limits
|
|
432
|
-
|
|
433
|
-
- `setSelection` and `setChecked` ignore locking on purpose: a form must be able to pre-select a disabled option. Use the interaction methods for anything the user does.
|
|
434
|
-
- A parent aggregates only its unlocked children, so a partially locked parent can read as checked while a locked child is not. This matches how the trees behaved before; change `conduct` if a design needs locked children to count.
|
|
435
|
-
- With a `max` smaller than the number of selectable items, `stateOf` can never reach `all`. `toggleAll` handles it by clearing when nothing more can be added.
|
|
436
|
-
- Selection order is the order of selection, not the order of the source list; sort before `setSelection` when a stable order is needed.
|
package/package.json
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andes-ng/primitives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0-rc.1",
|
|
4
4
|
"description": "Andes NG's own behavior primitives: accessible, unstyled Angular directives.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"angular",
|
|
7
|
+
"headless",
|
|
8
|
+
"directives",
|
|
9
|
+
"accessibility",
|
|
10
|
+
"a11y",
|
|
11
|
+
"primitives"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://github.com/andes-ng/andes/tree/main/packages/primitives#readme",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/andes-ng/andes/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/andes-ng/andes.git",
|
|
20
|
+
"directory": "packages/primitives"
|
|
21
|
+
},
|
|
5
22
|
"license": "MIT",
|
|
6
23
|
"publishConfig": {
|
|
7
24
|
"access": "public"
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Andes NG contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|