@agnos-ui/core 0.5.0 → 0.6.0-next.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.
@@ -65,6 +65,8 @@ interface SelectCommonPropsAndState<Item> extends WidgetsCommonPropsAndState {
65
65
  }
66
66
  /**
67
67
  * Props for the Select component.
68
+ *
69
+ * @template Item - The type of the Select Items
68
70
  */
69
71
  export interface SelectProps<Item> extends SelectCommonPropsAndState<Item> {
70
72
  /**
@@ -136,6 +138,8 @@ export interface SelectProps<Item> extends SelectCommonPropsAndState<Item> {
136
138
  }
137
139
  /**
138
140
  * Item representation built from the items provided in parameters
141
+ *
142
+ * @template T - The type of the Select Items
139
143
  */
140
144
  export interface ItemContext<T> {
141
145
  /**
@@ -153,6 +157,8 @@ export interface ItemContext<T> {
153
157
  }
154
158
  /**
155
159
  * Represents the state of a Select component.
160
+ *
161
+ * @template Item - The type of the Select Items
156
162
  */
157
163
  export interface SelectState<Item> extends SelectCommonPropsAndState<Item> {
158
164
  /**
@@ -175,6 +181,8 @@ export interface SelectState<Item> extends SelectCommonPropsAndState<Item> {
175
181
  }
176
182
  /**
177
183
  * Interface representing the API for a Select component.
184
+ *
185
+ * @template Item - The type of the Select Items
178
186
  */
179
187
  export interface SelectApi<Item> {
180
188
  /**
@@ -240,6 +248,8 @@ export interface SelectApi<Item> {
240
248
  }
241
249
  /**
242
250
  * Interface representing the directives used in the Select component.
251
+ *
252
+ * @template Item - The type of the Select Items
243
253
  */
244
254
  export interface SelectDirectives<Item> {
245
255
  /**
@@ -281,6 +291,8 @@ export interface SelectDirectives<Item> {
281
291
  }
282
292
  /**
283
293
  * Represents a Select widget component.
294
+ *
295
+ * @template Item - The type of the Select Items
284
296
  */
285
297
  export type SelectWidget<Item> = Widget<SelectProps<Item>, SelectState<Item>, SelectApi<Item>, SelectDirectives<Item>>;
286
298
  /**
@@ -290,6 +302,8 @@ export type SelectWidget<Item> = Widget<SelectProps<Item>, SelectState<Item>, Se
290
302
  export declare function getSelectDefaultConfig(): SelectProps<any>;
291
303
  /**
292
304
  * Create a SelectWidget with given config props
305
+ *
306
+ * @template Item - The type of the Select Items
293
307
  * @param config - an optional alert config
294
308
  * @returns a SelectWidget
295
309
  */
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const tree = require("../../tree-Bh1WO63V.cjs");
4
+ exports.createTree = tree.createTree;
5
+ exports.getTreeDefaultConfig = tree.getTreeDefaultConfig;
@@ -0,0 +1 @@
1
+ export * from './tree';
@@ -0,0 +1,5 @@
1
+ import { c, g } from "../../tree-C4v9rTQl.js";
2
+ export {
3
+ c as createTree,
4
+ g as getTreeDefaultConfig
5
+ };
@@ -0,0 +1,149 @@
1
+ import type { Directive } from '../../types';
2
+ import { type PropsConfig, type Widget } from '../../types';
3
+ import type { WidgetsCommonPropsAndState } from '../commonProps';
4
+ /**
5
+ * Represents a tree item component.
6
+ */
7
+ export interface TreeItem {
8
+ /**
9
+ * Optional accessibility label for the node
10
+ */
11
+ ariaLabel?: string;
12
+ /**
13
+ * Optional array of children nodes
14
+ */
15
+ children?: TreeItem[];
16
+ /**
17
+ * If `true` the node is expanded
18
+ */
19
+ isExpanded?: boolean;
20
+ /**
21
+ * String title of the node
22
+ */
23
+ label: string;
24
+ }
25
+ /**
26
+ * Normalized TreeItem object
27
+ */
28
+ export interface NormalizedTreeItem extends TreeItem {
29
+ /**
30
+ * Accessibility label for the node
31
+ */
32
+ ariaLabel: string;
33
+ /**
34
+ * Level in the hierarchy, starts with 0 for a root node
35
+ */
36
+ level: number;
37
+ /**
38
+ * An array of children nodes
39
+ */
40
+ children: NormalizedTreeItem[];
41
+ }
42
+ interface TreeCommonPropsAndState extends WidgetsCommonPropsAndState {
43
+ /**
44
+ * Optional accessibility label for the tree if there is no explicit label
45
+ *
46
+ * @defaultValue `''`
47
+ */
48
+ ariaLabel?: string;
49
+ }
50
+ /**
51
+ * Interface representing the properties for the Tree component.
52
+ */
53
+ export interface TreeProps extends TreeCommonPropsAndState {
54
+ /**
55
+ * Array of the tree nodes to display
56
+ *
57
+ * @defaultValue `[]`
58
+ */
59
+ nodes: TreeItem[];
60
+ /**
61
+ * An event emitted when the user toggles the expand of the TreeItem.
62
+ *
63
+ * Event payload is equal to the TreeItem clicked.
64
+ *
65
+ * @defaultValue
66
+ * ```ts
67
+ * () => {}
68
+ * ```
69
+ */
70
+ onExpandToggle: (node: NormalizedTreeItem) => void;
71
+ /**
72
+ * Retrieves expand items of the TreeItem
73
+ *
74
+ * @param node - HTML element that is representing the expand item
75
+ *
76
+ * @defaultValue
77
+ * ```ts
78
+ * (node: HTMLElement) => node.querySelectorAll('button')
79
+ * ```
80
+ */
81
+ navSelector(node: HTMLElement): NodeListOf<HTMLButtonElement>;
82
+ /**
83
+ * Return the value for the 'aria-label' attribute of the toggle
84
+ * @param label - tree item label
85
+ *
86
+ * @defaultValue
87
+ * ```ts
88
+ * (label: string) => `Toggle ${label}`
89
+ * ```
90
+ */
91
+ ariaLabelToggleFn: (label: string) => string;
92
+ }
93
+ /**
94
+ * Represents the state of a Tree component.
95
+ */
96
+ export interface TreeState extends TreeCommonPropsAndState {
97
+ /**
98
+ * Array of normalized tree nodes
99
+ */
100
+ normalizedNodes: NormalizedTreeItem[];
101
+ /**
102
+ * Getter of expanded state for each tree node
103
+ */
104
+ expandedMap: {
105
+ get(item: NormalizedTreeItem): boolean | undefined;
106
+ };
107
+ }
108
+ /**
109
+ * Interface representing the API for a Tree component.
110
+ */
111
+ export interface TreeApi {
112
+ }
113
+ /**
114
+ * Interface representing various directives used in the Tree component.
115
+ */
116
+ export interface TreeDirectives {
117
+ /**
118
+ * Directive to attach navManager for the tree
119
+ */
120
+ navigationDirective: Directive;
121
+ /**
122
+ * Directive to handle toggle for the tree item
123
+ */
124
+ itemToggleDirective: Directive<{
125
+ item: NormalizedTreeItem;
126
+ }>;
127
+ /**
128
+ * Directive to handle attributes for the tree item
129
+ */
130
+ itemAttributesDirective: Directive<{
131
+ item: NormalizedTreeItem;
132
+ }>;
133
+ }
134
+ /**
135
+ * Represents a Tree widget component.
136
+ */
137
+ export type TreeWidget = Widget<TreeProps, TreeState, TreeApi, TreeDirectives>;
138
+ /**
139
+ * Retrieve a shallow copy of the default Tree config
140
+ * @returns the default Tree config
141
+ */
142
+ export declare function getTreeDefaultConfig(): TreeProps;
143
+ /**
144
+ * Create a tree widget with given config props
145
+ * @param config - an optional tree config
146
+ * @returns a TreeWidget
147
+ */
148
+ export declare function createTree(config?: PropsConfig<TreeProps>): TreeWidget;
149
+ export {};
package/config.d.ts CHANGED
@@ -8,6 +8,7 @@ import type { AccordionProps } from './components/accordion/accordion';
8
8
  import type { ProgressbarProps } from './components/progressbar/progressbar';
9
9
  import type { SliderProps } from './components/slider/slider';
10
10
  import type { ToastProps } from './components/toast/toast';
11
+ import type { TreeProps } from './components/tree/tree';
11
12
  /**
12
13
  * A utility type that makes all properties of an object type `T` optional,
13
14
  * and also makes all properties of the nested objects within `T` optional.
@@ -99,4 +100,8 @@ export type WidgetsConfig = {
99
100
  * toast widget config
100
101
  */
101
102
  toast: ToastProps;
103
+ /**
104
+ * tree widget config
105
+ */
106
+ tree: TreeProps;
102
107
  };
package/index.cjs CHANGED
@@ -10,6 +10,7 @@ const rating = require("./rating-DlfOuwuX.cjs");
10
10
  const select = require("./select-LBVhNdrd.cjs");
11
11
  const slider = require("./slider-DlOAawbZ.cjs");
12
12
  const toast = require("./toast-D_uSrRZL.cjs");
13
+ const tree = require("./tree-Bh1WO63V.cjs");
13
14
  const config = require("./config.cjs");
14
15
  const services_extendWidget = require("./services/extendWidget.cjs");
15
16
  const services_floatingUI = require("./services/floatingUI.cjs");
@@ -50,6 +51,8 @@ exports.createSlider = slider.createSlider;
50
51
  exports.getSliderDefaultConfig = slider.getSliderDefaultConfig;
51
52
  exports.createToast = toast.createToast;
52
53
  exports.getToastDefaultConfig = toast.getToastDefaultConfig;
54
+ exports.createTree = tree.createTree;
55
+ exports.getTreeDefaultConfig = tree.getTreeDefaultConfig;
53
56
  exports.createWidgetsConfig = config.createWidgetsConfig;
54
57
  exports.mergeInto = config.mergeInto;
55
58
  exports.extendWidgetProps = services_extendWidget.extendWidgetProps;
package/index.d.ts CHANGED
@@ -8,6 +8,7 @@ export * from './components/rating';
8
8
  export * from './components/select';
9
9
  export * from './components/slider';
10
10
  export * from './components/toast';
11
+ export * from './components/tree';
11
12
  export * from './config';
12
13
  export * from './services/extendWidget';
13
14
  export * from './services/floatingUI';
package/index.js CHANGED
@@ -8,6 +8,7 @@ import { c as c6, g as g6 } from "./rating-Da38uaZz.js";
8
8
  import { c as c7, g as g7 } from "./select-CAEPqdz1.js";
9
9
  import { c as c8, g as g8 } from "./slider-DYrwS7Mv.js";
10
10
  import { c as c9, g as g9 } from "./toast-CpvsibAI.js";
11
+ import { c as c10, g as g10 } from "./tree-C4v9rTQl.js";
11
12
  import { createWidgetsConfig, mergeInto } from "./config.js";
12
13
  import { extendWidgetProps } from "./services/extendWidget.js";
13
14
  import { createFloatingUI } from "./services/floatingUI.js";
@@ -22,15 +23,15 @@ import { hash$ } from "./services/hash.js";
22
23
  import { createTransition, noAnimation } from "./services/transitions/baseTransitions.js";
23
24
  import { createCSSTransition, getTransitionDurationMs, hasTransition } from "./services/transitions/cssTransitions.js";
24
25
  import { createSimpleClassTransition } from "./services/transitions/simpleClassTransition.js";
25
- import { o, a as a3, c as c10, b, p, n, g as g10, j, f as f2, h, q, d, e, i, m as m2, k, l, r, s } from "./directive-CKEccryv.js";
26
+ import { o, a as a3, c as c11, b, p, n, g as g11, j, f as f2, h, q, d, e, i, m as m2, k, l, r, s } from "./directive-CKEccryv.js";
26
27
  import { bindableDerived, bindableProp, createPatch, findChangedProperties, isStore, mergeConfigStores, normalizeConfigStores, stateStores, toReadableStore, toWritableStore, writableWithDefault, writablesForProps, writablesWithDefault } from "./utils/stores.js";
27
- import { i as i2, t, h as h2, c as c11, d as d2, f as f3, g as g11, a as a4, b as b2, e as e2 } from "./writables-DCiBdIBK.js";
28
+ import { i as i2, t, h as h2, c as c12, d as d2, f as f3, g as g12, a as a4, b as b2, e as e2 } from "./writables-DCiBdIBK.js";
28
29
  export {
29
30
  INVALID_VALUE,
30
31
  activeElement$,
31
32
  o as attributesData,
32
33
  a3 as bindDirective,
33
- c10 as bindDirectiveNoArg,
34
+ c11 as bindDirectiveNoArg,
34
35
  bindableDerived,
35
36
  bindableProp,
36
37
  b as browserDirective,
@@ -39,7 +40,7 @@ export {
39
40
  c as createAccordionItem,
40
41
  c2 as createAlert,
41
42
  n as createAttributesDirective,
42
- g10 as createBrowserStoreArrayDirective,
43
+ g11 as createBrowserStoreArrayDirective,
43
44
  j as createBrowserStoreDirective,
44
45
  createCSSTransition,
45
46
  createFloatingUI,
@@ -60,6 +61,7 @@ export {
60
61
  h as createStoreDirective,
61
62
  c9 as createToast,
62
63
  createTransition,
64
+ c10 as createTree,
63
65
  i2 as createTypeEnum,
64
66
  createWidgetsConfig,
65
67
  q as directiveAttributes,
@@ -79,6 +81,7 @@ export {
79
81
  g8 as getSliderDefaultConfig,
80
82
  g9 as getToastDefaultConfig,
81
83
  getTransitionDurationMs,
84
+ g10 as getTreeDefaultConfig,
82
85
  hasTransition,
83
86
  hash$,
84
87
  i as isBrowserHTMLElement,
@@ -102,10 +105,10 @@ export {
102
105
  toReadableStore,
103
106
  toWritableStore,
104
107
  h2 as typeArray,
105
- c11 as typeBoolean,
108
+ c12 as typeBoolean,
106
109
  d2 as typeBooleanOrNull,
107
110
  f3 as typeFunction,
108
- g11 as typeHTMLElementOrNull,
111
+ g12 as typeHTMLElementOrNull,
109
112
  a4 as typeNumber,
110
113
  b2 as typeNumberInRangeFactory,
111
114
  e2 as typeString,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agnos-ui/core",
3
3
  "description": "Framework-agnostic headless component library.",
4
- "version": "0.5.0",
4
+ "version": "0.6.0-next.1",
5
5
  "type": "module",
6
6
  "main": "./index.cjs",
7
7
  "module": "./index.js",
@@ -46,7 +46,7 @@
46
46
  "peerDependencies": {
47
47
  "@amadeus-it-group/tansu": "^1.0.0",
48
48
  "@floating-ui/dom": "^1.6.12",
49
- "esm-env": "^1.1.4"
49
+ "esm-env": "^1.2.1"
50
50
  },
51
51
  "sideEffects": false,
52
52
  "homepage": "https://www.agnosui.dev/latest/",
@@ -42,6 +42,9 @@ export interface FloatingUIState {
42
42
  */
43
43
  middlewareData: MiddlewareData | undefined;
44
44
  }
45
+ /**
46
+ * Interface representing the directives used to enable floating ui.
47
+ */
45
48
  export interface FloatingUIDirectives {
46
49
  /**
47
50
  * Directive to attach to the reference element
@@ -38,7 +38,6 @@ const createTransition = (config) => {
38
38
  const currentTransition$ = tansu.writable(
39
39
  null
40
40
  );
41
- const transitioning$ = tansu.computed(() => !!currentTransition$());
42
41
  const stop = () => {
43
42
  let context;
44
43
  currentTransition$.update((currentTransition) => {
@@ -80,8 +79,6 @@ const createTransition = (config) => {
80
79
  );
81
80
  return currentTransition;
82
81
  });
83
- const shown$ = tansu.computed(() => !transitioning$() && visible$() && elementPresent$());
84
- const hidden$ = tansu.computed(() => !transitioning$() && !visible$());
85
82
  const effectiveAnimation$ = tansu.computed(() => initDone$() ? animated$() : animatedOnInit$());
86
83
  const animationFromToggle$ = tansu.writable(null);
87
84
  let previousElement;
@@ -145,6 +142,14 @@ const createTransition = (config) => {
145
142
  }
146
143
  }
147
144
  };
145
+ const transitioning$ = tansu.computed(() => {
146
+ if (elementPresent$()) {
147
+ void visibleAction$();
148
+ }
149
+ return !!currentTransition$();
150
+ });
151
+ const shown$ = tansu.computed(() => !transitioning$() && visible$() && elementPresent$());
152
+ const hidden$ = tansu.computed(() => !transitioning$() && !visible$());
148
153
  const directive = utils_directive.mergeDirectives(storeDirective, utils_directive.directiveSubscribe(visibleAction$));
149
154
  return {
150
155
  ...utils_stores.stateStores({
@@ -36,7 +36,6 @@ const createTransition = (config) => {
36
36
  const currentTransition$ = writable(
37
37
  null
38
38
  );
39
- const transitioning$ = computed(() => !!currentTransition$());
40
39
  const stop = () => {
41
40
  let context;
42
41
  currentTransition$.update((currentTransition) => {
@@ -78,8 +77,6 @@ const createTransition = (config) => {
78
77
  );
79
78
  return currentTransition;
80
79
  });
81
- const shown$ = computed(() => !transitioning$() && visible$() && elementPresent$());
82
- const hidden$ = computed(() => !transitioning$() && !visible$());
83
80
  const effectiveAnimation$ = computed(() => initDone$() ? animated$() : animatedOnInit$());
84
81
  const animationFromToggle$ = writable(null);
85
82
  let previousElement;
@@ -143,6 +140,14 @@ const createTransition = (config) => {
143
140
  }
144
141
  }
145
142
  };
143
+ const transitioning$ = computed(() => {
144
+ if (elementPresent$()) {
145
+ void visibleAction$();
146
+ }
147
+ return !!currentTransition$();
148
+ });
149
+ const shown$ = computed(() => !transitioning$() && visible$() && elementPresent$());
150
+ const hidden$ = computed(() => !transitioning$() && !visible$());
146
151
  const directive = mergeDirectives(storeDirective, directiveSubscribe(visibleAction$));
147
152
  return {
148
153
  ...stateStores({
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ const tansu = require("@amadeus-it-group/tansu");
3
+ const services_navManager = require("./services/navManager.cjs");
4
+ const utils_directive = require("./directive-DdlwGFtf.cjs");
5
+ const func = require("./func-Qd3cD9a3.cjs");
6
+ const utils_stores = require("./utils/stores.cjs");
7
+ const utils_writables = require("./writables-BPAJvaL_.cjs");
8
+ function getTreeDefaultConfig() {
9
+ return {
10
+ ...defaultTreeConfig
11
+ };
12
+ }
13
+ const defaultTreeConfig = {
14
+ className: "",
15
+ nodes: [],
16
+ onExpandToggle: func.noop,
17
+ navSelector: (node) => node.querySelectorAll("button"),
18
+ ariaLabelToggleFn: (label) => `Toggle ${label}`
19
+ };
20
+ const configValidator = {
21
+ className: utils_writables.typeString,
22
+ nodes: utils_writables.typeArray,
23
+ onExpandToggle: utils_writables.typeFunction,
24
+ navSelector: utils_writables.typeFunction,
25
+ ariaLabelToggleFn: utils_writables.typeFunction
26
+ };
27
+ function createTree(config) {
28
+ const [{ nodes$, onExpandToggle$, navSelector$, ariaLabelToggleFn$, ...stateProps }, patch] = utils_stores.writablesForProps(
29
+ defaultTreeConfig,
30
+ config,
31
+ configValidator
32
+ );
33
+ const treeMap = /* @__PURE__ */ new Map();
34
+ const _expandedMap = {
35
+ get(item) {
36
+ return item.isExpanded;
37
+ }
38
+ };
39
+ const _toggleChange$ = tansu.writable({});
40
+ const expandedMap$ = tansu.computed(() => {
41
+ normalizedNodes$();
42
+ _toggleChange$();
43
+ return _expandedMap;
44
+ });
45
+ const {
46
+ elementsInDomOrder$,
47
+ directive: navDirective,
48
+ refreshElements,
49
+ focusIndex,
50
+ focusPrevious,
51
+ focusNext,
52
+ focusFirst,
53
+ focusLast
54
+ } = services_navManager.createNavManager();
55
+ const navManagerConfig$ = tansu.computed(() => ({
56
+ keys: {
57
+ ArrowUp: focusPrevious,
58
+ ArrowDown: focusNext,
59
+ Home: focusFirst,
60
+ End: focusLast
61
+ },
62
+ selector: navSelector$()
63
+ }));
64
+ const traverseTree = (node, level, parent) => {
65
+ var _a;
66
+ const copyNode = {
67
+ ...node,
68
+ ariaLabel: node.ariaLabel ?? node.label,
69
+ level,
70
+ children: [],
71
+ isExpanded: ((_a = node.children) == null ? void 0 : _a.length) ? node.isExpanded ?? false : void 0
72
+ };
73
+ treeMap.set(copyNode, {
74
+ parent
75
+ });
76
+ if (node.children) {
77
+ copyNode.children = node.children.map((child) => traverseTree(child, level + 1, copyNode));
78
+ }
79
+ return copyNode;
80
+ };
81
+ const normalizedNodes$ = tansu.computed(() => {
82
+ treeMap.clear();
83
+ return nodes$().map((node) => traverseTree(node, 0, void 0));
84
+ });
85
+ const _lastFocusedTreeItem$ = tansu.writable(normalizedNodes$()[0]);
86
+ const getTreeItemInfo = (item) => {
87
+ const treeItem = treeMap.get(item);
88
+ if (!treeItem) {
89
+ console.error(`Node ${item.label} doesn't exist in the map`);
90
+ }
91
+ return treeItem;
92
+ };
93
+ const treeItemElementDirective = utils_directive.browserDirective(
94
+ (toggleItem, args) => {
95
+ let treeItemInfo;
96
+ const destroy = () => {
97
+ if (treeItemInfo && treeItemInfo.htmlElement === toggleItem) {
98
+ treeItemInfo.htmlElement = void 0;
99
+ }
100
+ treeItemInfo = void 0;
101
+ };
102
+ const update = (args2) => {
103
+ destroy();
104
+ treeItemInfo = getTreeItemInfo(args2.item);
105
+ if (treeItemInfo) {
106
+ if (treeItemInfo.htmlElement) {
107
+ console.warn(`The tree item directive should be used once per element`);
108
+ }
109
+ treeItemInfo.htmlElement = toggleItem;
110
+ }
111
+ };
112
+ update(args);
113
+ return {
114
+ update,
115
+ destroy
116
+ };
117
+ }
118
+ );
119
+ const focusElementIfExists = (itemToFocus) => {
120
+ var _a;
121
+ if (itemToFocus) {
122
+ const mapItemHtml = (_a = getTreeItemInfo(itemToFocus)) == null ? void 0 : _a.htmlElement;
123
+ if (mapItemHtml) {
124
+ const index = elementsInDomOrder$().indexOf(mapItemHtml);
125
+ focusIndex(index, 0);
126
+ }
127
+ }
128
+ };
129
+ const itemToggleAttributesDirective = utils_directive.createAttributesDirective((treeItemContext$) => ({
130
+ events: {
131
+ focus: () => {
132
+ const { item } = treeItemContext$();
133
+ _lastFocusedTreeItem$.set(item);
134
+ },
135
+ click: () => {
136
+ const { item } = treeItemContext$();
137
+ toggleExpanded(item);
138
+ },
139
+ keydown: (event) => {
140
+ var _a, _b;
141
+ const { key } = event;
142
+ const { item } = treeItemContext$();
143
+ const isExpanded = item.isExpanded;
144
+ refreshElements();
145
+ switch (key) {
146
+ case "ArrowLeft":
147
+ if (isExpanded) {
148
+ toggleExpanded(item);
149
+ } else {
150
+ focusElementIfExists((_a = getTreeItemInfo(item)) == null ? void 0 : _a.parent);
151
+ }
152
+ break;
153
+ case "ArrowRight":
154
+ if (!isExpanded) {
155
+ toggleExpanded(item);
156
+ } else {
157
+ focusElementIfExists((_b = item.children) == null ? void 0 : _b[0]);
158
+ }
159
+ break;
160
+ default:
161
+ return;
162
+ }
163
+ event.preventDefault();
164
+ event.stopPropagation();
165
+ }
166
+ },
167
+ attributes: {
168
+ "aria-label": tansu.computed(() => {
169
+ const { item } = treeItemContext$();
170
+ return ariaLabelToggleFn$()(item.ariaLabel);
171
+ }),
172
+ tabindex: tansu.computed(() => {
173
+ const { item } = treeItemContext$();
174
+ return item === _lastFocusedTreeItem$() ? "0" : "-1";
175
+ }),
176
+ type: "button"
177
+ },
178
+ classNames: {
179
+ "au-tree-expand-icon": true,
180
+ "au-tree-expand-icon-expanded": tansu.computed(() => {
181
+ _toggleChange$();
182
+ const { item } = treeItemContext$();
183
+ return item.isExpanded ?? false;
184
+ })
185
+ }
186
+ }));
187
+ const toggleExpanded = (node) => {
188
+ const treeItemInfo = getTreeItemInfo(node);
189
+ if (treeItemInfo === void 0 || node.isExpanded === void 0) {
190
+ return;
191
+ }
192
+ node.isExpanded = !node.isExpanded;
193
+ _toggleChange$.set({});
194
+ onExpandToggle$()(node);
195
+ };
196
+ const widget = {
197
+ ...utils_stores.stateStores({ normalizedNodes$, expandedMap$, ...stateProps }),
198
+ patch,
199
+ api: {},
200
+ directives: {
201
+ navigationDirective: utils_directive.bindDirective(navDirective, navManagerConfig$),
202
+ itemToggleDirective: utils_directive.mergeDirectives(treeItemElementDirective, itemToggleAttributesDirective),
203
+ itemAttributesDirective: utils_directive.createAttributesDirective((treeItemContext$) => ({
204
+ attributes: {
205
+ role: "treeitem",
206
+ "aria-selected": "false",
207
+ // TODO: adapt aria-selected to the actual selected state
208
+ "aria-expanded": tansu.computed(() => {
209
+ var _a;
210
+ const { item } = treeItemContext$();
211
+ _toggleChange$();
212
+ return (_a = item.isExpanded) == null ? void 0 : _a.toString();
213
+ })
214
+ }
215
+ }))
216
+ }
217
+ };
218
+ return widget;
219
+ }
220
+ exports.createTree = createTree;
221
+ exports.getTreeDefaultConfig = getTreeDefaultConfig;
@@ -0,0 +1,222 @@
1
+ import { writable, computed } from "@amadeus-it-group/tansu";
2
+ import { createNavManager } from "./services/navManager.js";
3
+ import { a as bindDirective, k as mergeDirectives, n as createAttributesDirective, b as browserDirective } from "./directive-CKEccryv.js";
4
+ import { n as noop } from "./func-DR0n-ShK.js";
5
+ import { writablesForProps, stateStores } from "./utils/stores.js";
6
+ import { e as typeString, h as typeArray, f as typeFunction } from "./writables-DCiBdIBK.js";
7
+ function getTreeDefaultConfig() {
8
+ return {
9
+ ...defaultTreeConfig
10
+ };
11
+ }
12
+ const defaultTreeConfig = {
13
+ className: "",
14
+ nodes: [],
15
+ onExpandToggle: noop,
16
+ navSelector: (node) => node.querySelectorAll("button"),
17
+ ariaLabelToggleFn: (label) => `Toggle ${label}`
18
+ };
19
+ const configValidator = {
20
+ className: typeString,
21
+ nodes: typeArray,
22
+ onExpandToggle: typeFunction,
23
+ navSelector: typeFunction,
24
+ ariaLabelToggleFn: typeFunction
25
+ };
26
+ function createTree(config) {
27
+ const [{ nodes$, onExpandToggle$, navSelector$, ariaLabelToggleFn$, ...stateProps }, patch] = writablesForProps(
28
+ defaultTreeConfig,
29
+ config,
30
+ configValidator
31
+ );
32
+ const treeMap = /* @__PURE__ */ new Map();
33
+ const _expandedMap = {
34
+ get(item) {
35
+ return item.isExpanded;
36
+ }
37
+ };
38
+ const _toggleChange$ = writable({});
39
+ const expandedMap$ = computed(() => {
40
+ normalizedNodes$();
41
+ _toggleChange$();
42
+ return _expandedMap;
43
+ });
44
+ const {
45
+ elementsInDomOrder$,
46
+ directive: navDirective,
47
+ refreshElements,
48
+ focusIndex,
49
+ focusPrevious,
50
+ focusNext,
51
+ focusFirst,
52
+ focusLast
53
+ } = createNavManager();
54
+ const navManagerConfig$ = computed(() => ({
55
+ keys: {
56
+ ArrowUp: focusPrevious,
57
+ ArrowDown: focusNext,
58
+ Home: focusFirst,
59
+ End: focusLast
60
+ },
61
+ selector: navSelector$()
62
+ }));
63
+ const traverseTree = (node, level, parent) => {
64
+ var _a;
65
+ const copyNode = {
66
+ ...node,
67
+ ariaLabel: node.ariaLabel ?? node.label,
68
+ level,
69
+ children: [],
70
+ isExpanded: ((_a = node.children) == null ? void 0 : _a.length) ? node.isExpanded ?? false : void 0
71
+ };
72
+ treeMap.set(copyNode, {
73
+ parent
74
+ });
75
+ if (node.children) {
76
+ copyNode.children = node.children.map((child) => traverseTree(child, level + 1, copyNode));
77
+ }
78
+ return copyNode;
79
+ };
80
+ const normalizedNodes$ = computed(() => {
81
+ treeMap.clear();
82
+ return nodes$().map((node) => traverseTree(node, 0, void 0));
83
+ });
84
+ const _lastFocusedTreeItem$ = writable(normalizedNodes$()[0]);
85
+ const getTreeItemInfo = (item) => {
86
+ const treeItem = treeMap.get(item);
87
+ if (!treeItem) {
88
+ console.error(`Node ${item.label} doesn't exist in the map`);
89
+ }
90
+ return treeItem;
91
+ };
92
+ const treeItemElementDirective = browserDirective(
93
+ (toggleItem, args) => {
94
+ let treeItemInfo;
95
+ const destroy = () => {
96
+ if (treeItemInfo && treeItemInfo.htmlElement === toggleItem) {
97
+ treeItemInfo.htmlElement = void 0;
98
+ }
99
+ treeItemInfo = void 0;
100
+ };
101
+ const update = (args2) => {
102
+ destroy();
103
+ treeItemInfo = getTreeItemInfo(args2.item);
104
+ if (treeItemInfo) {
105
+ if (treeItemInfo.htmlElement) {
106
+ console.warn(`The tree item directive should be used once per element`);
107
+ }
108
+ treeItemInfo.htmlElement = toggleItem;
109
+ }
110
+ };
111
+ update(args);
112
+ return {
113
+ update,
114
+ destroy
115
+ };
116
+ }
117
+ );
118
+ const focusElementIfExists = (itemToFocus) => {
119
+ var _a;
120
+ if (itemToFocus) {
121
+ const mapItemHtml = (_a = getTreeItemInfo(itemToFocus)) == null ? void 0 : _a.htmlElement;
122
+ if (mapItemHtml) {
123
+ const index = elementsInDomOrder$().indexOf(mapItemHtml);
124
+ focusIndex(index, 0);
125
+ }
126
+ }
127
+ };
128
+ const itemToggleAttributesDirective = createAttributesDirective((treeItemContext$) => ({
129
+ events: {
130
+ focus: () => {
131
+ const { item } = treeItemContext$();
132
+ _lastFocusedTreeItem$.set(item);
133
+ },
134
+ click: () => {
135
+ const { item } = treeItemContext$();
136
+ toggleExpanded(item);
137
+ },
138
+ keydown: (event) => {
139
+ var _a, _b;
140
+ const { key } = event;
141
+ const { item } = treeItemContext$();
142
+ const isExpanded = item.isExpanded;
143
+ refreshElements();
144
+ switch (key) {
145
+ case "ArrowLeft":
146
+ if (isExpanded) {
147
+ toggleExpanded(item);
148
+ } else {
149
+ focusElementIfExists((_a = getTreeItemInfo(item)) == null ? void 0 : _a.parent);
150
+ }
151
+ break;
152
+ case "ArrowRight":
153
+ if (!isExpanded) {
154
+ toggleExpanded(item);
155
+ } else {
156
+ focusElementIfExists((_b = item.children) == null ? void 0 : _b[0]);
157
+ }
158
+ break;
159
+ default:
160
+ return;
161
+ }
162
+ event.preventDefault();
163
+ event.stopPropagation();
164
+ }
165
+ },
166
+ attributes: {
167
+ "aria-label": computed(() => {
168
+ const { item } = treeItemContext$();
169
+ return ariaLabelToggleFn$()(item.ariaLabel);
170
+ }),
171
+ tabindex: computed(() => {
172
+ const { item } = treeItemContext$();
173
+ return item === _lastFocusedTreeItem$() ? "0" : "-1";
174
+ }),
175
+ type: "button"
176
+ },
177
+ classNames: {
178
+ "au-tree-expand-icon": true,
179
+ "au-tree-expand-icon-expanded": computed(() => {
180
+ _toggleChange$();
181
+ const { item } = treeItemContext$();
182
+ return item.isExpanded ?? false;
183
+ })
184
+ }
185
+ }));
186
+ const toggleExpanded = (node) => {
187
+ const treeItemInfo = getTreeItemInfo(node);
188
+ if (treeItemInfo === void 0 || node.isExpanded === void 0) {
189
+ return;
190
+ }
191
+ node.isExpanded = !node.isExpanded;
192
+ _toggleChange$.set({});
193
+ onExpandToggle$()(node);
194
+ };
195
+ const widget = {
196
+ ...stateStores({ normalizedNodes$, expandedMap$, ...stateProps }),
197
+ patch,
198
+ api: {},
199
+ directives: {
200
+ navigationDirective: bindDirective(navDirective, navManagerConfig$),
201
+ itemToggleDirective: mergeDirectives(treeItemElementDirective, itemToggleAttributesDirective),
202
+ itemAttributesDirective: createAttributesDirective((treeItemContext$) => ({
203
+ attributes: {
204
+ role: "treeitem",
205
+ "aria-selected": "false",
206
+ // TODO: adapt aria-selected to the actual selected state
207
+ "aria-expanded": computed(() => {
208
+ var _a;
209
+ const { item } = treeItemContext$();
210
+ _toggleChange$();
211
+ return (_a = item.isExpanded) == null ? void 0 : _a.toString();
212
+ })
213
+ }
214
+ }))
215
+ }
216
+ };
217
+ return widget;
218
+ }
219
+ export {
220
+ createTree as c,
221
+ getTreeDefaultConfig as g
222
+ };
package/types.d.ts CHANGED
@@ -170,6 +170,8 @@ export type DirectivesAndOptParam<T extends any[], U extends SSRHTMLElement = SS
170
170
  * - `null`
171
171
  * - A `string`
172
172
  * - A function that takes `props` of type `Props` and returns a `string`
173
+ *
174
+ * @template Props - The type of the props when the slot is a function
173
175
  */
174
176
  export type SlotContent<Props extends object = object> = undefined | null | string | ((props: Props) => string);
175
177
  /**
@@ -49,6 +49,8 @@ export declare const isHTMLElement: (value: any) => value is HTMLElement;
49
49
  export declare const allowNull: <T>(isType: (value: any) => value is T) => (value: any) => value is T | null;
50
50
  /**
51
51
  * Builds a new type guard to check if an element belongs to an enum list
52
+ *
53
+ * @template T - the type of the enum
52
54
  * @param list - the list of all enum values
53
55
  * @returns the type guard
54
56
  */
@@ -12,6 +12,8 @@ export interface PromisePendingResult {
12
12
  export declare const promisePending: PromisePendingResult;
13
13
  /**
14
14
  * Represents the state of a promise, which can be either fulfilled, rejected, or pending.
15
+ *
16
+ * @template T - The type of the value promised
15
17
  */
16
18
  export type PromiseState<T> = PromiseFulfilledResult<T> | PromiseRejectedResult | PromisePendingResult;
17
19
  /**