@bccampus/ui-components 0.2.0 → 0.4.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.
Files changed (56) hide show
  1. package/dist/banner.js +14 -14
  2. package/dist/button.d.ts +1 -1
  3. package/dist/button.js +17 -17
  4. package/dist/caption.js +7 -8
  5. package/dist/card.js +34 -34
  6. package/dist/composite.d.ts +151 -0
  7. package/dist/composite.js +472 -0
  8. package/dist/generate-tiles-DuagGD1d.js +244 -0
  9. package/dist/horizontal-list.js +32 -32
  10. package/dist/icon-generator.js +43 -270
  11. package/dist/igenerate-tiles.d.ts +43 -0
  12. package/dist/igenerate-tiles.js +7 -0
  13. package/dist/index-U7DVCmS_.js +76 -0
  14. package/dist/input.js +4 -4
  15. package/dist/masked-image-generator.js +18 -18
  16. package/dist/navigation-menu.d.ts +27 -0
  17. package/dist/navigation-menu.js +1139 -0
  18. package/dist/overlay.js +2 -2
  19. package/dist/page-header.d.ts +5 -1
  20. package/dist/page-header.js +10 -9
  21. package/dist/page-section.js +14 -14
  22. package/dist/page.js +4 -4
  23. package/dist/search-input.js +11 -11
  24. package/dist/tag.js +8 -9
  25. package/dist/ui-components.d.ts +30 -2
  26. package/dist/ui-components.js +47 -37
  27. package/package.json +17 -1
  28. package/src/components/ui/banner.tsx +2 -2
  29. package/src/components/ui/button.tsx +8 -7
  30. package/src/components/ui/card.tsx +5 -5
  31. package/src/components/ui/composite/CompositeData.ts +215 -0
  32. package/src/components/ui/composite/CompositeDataItem.ts +144 -0
  33. package/src/components/ui/composite/composite-component-item.tsx +50 -0
  34. package/src/components/ui/composite/composite-component.tsx +100 -0
  35. package/src/components/ui/composite/composite-data-context.tsx +31 -0
  36. package/src/components/ui/composite/index.ts +4 -0
  37. package/src/components/ui/composite/types.ts +81 -0
  38. package/src/components/ui/horizontal-list.tsx +2 -2
  39. package/src/components/ui/index.ts +1 -0
  40. package/src/components/ui/navigation-menu.tsx +165 -0
  41. package/src/components/ui/page-header.tsx +13 -5
  42. package/src/components/ui/page-section.tsx +8 -8
  43. package/src/components/ui/page.tsx +3 -1
  44. package/src/components/ui/popover.tsx +46 -0
  45. package/src/hooks/use-effect-after-mount.ts +27 -0
  46. package/src/hooks/use-id.ts +5 -0
  47. package/src/hooks/use-keyboard-event.ts +144 -0
  48. package/src/lib/object.ts +48 -0
  49. package/src/lib/set-operations.ts +52 -0
  50. package/src/styles/theme.css +7 -7
  51. package/src/styles/typography.css +334 -341
  52. package/tsconfig.node.json +25 -25
  53. package/vite.config.ts +7 -3
  54. package/vite.ladle.config.ts +17 -0
  55. package/dist/index-DlfV3JTY.js +0 -70
  56. package/dist/jsx-runtime-BzflLqGi.js +0 -282
@@ -0,0 +1,215 @@
1
+ import { difference, union } from "@/lib/set-operations";
2
+ import { atom, type PreinitializedWritableAtom } from "nanostores";
3
+ import type { CompositeDataOptions, CompositeItemKey, CompositeOptions } from "./types";
4
+ import { CompositeDataItem } from "./CompositeDataItem";
5
+
6
+ export class CompositeData<T extends object> implements Iterable<CompositeDataItem<T>> {
7
+ #options: CompositeDataOptions<T>;
8
+
9
+ lookup: Map<CompositeItemKey, CompositeDataItem<T>> = new Map();
10
+
11
+ firstFocusableItem!: CompositeDataItem<T> | null;
12
+ focusedItem: PreinitializedWritableAtom<CompositeDataItem<T> | null> = atom(null);
13
+ disabledKeys: PreinitializedWritableAtom<Set<CompositeItemKey>> = atom(new Set());
14
+ selectedKeys: PreinitializedWritableAtom<Set<CompositeItemKey>> = atom(new Set());
15
+
16
+ root!: CompositeDataItem<T>;
17
+
18
+ constructor(items: T[], options?: CompositeOptions) {
19
+ this.#options = {
20
+ itemKeyProp: "key",
21
+ itemChildrenProp: "children",
22
+ disabledKeys: [],
23
+ selectedKeys: [],
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ getItemKey: (item: any) => item[this.#options.itemKeyProp] as CompositeItemKey,
26
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
+ getItemChildren: (item: any) => item[this.#options.itemChildrenProp] ? item[this.#options.itemChildrenProp] as T[] : undefined,
28
+
29
+ ...options
30
+ };
31
+
32
+ this.init(items);
33
+ }
34
+
35
+ *[Symbol.iterator](): Iterator<CompositeDataItem<T>> {
36
+ if (this.root.children) {
37
+ for (const node of this.root.children) {
38
+ yield node;
39
+ }
40
+ }
41
+ }
42
+
43
+ toJSON() {
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ const json: any = this.root.toJSON()
46
+
47
+ return json[this.#options.itemChildrenProp] as T[];
48
+ }
49
+
50
+
51
+ init(items: T[]) {
52
+ // this.itemAtoms = this.#setItemMap(items);
53
+ this.root = new CompositeDataItem(
54
+ { [this.#options.itemKeyProp]: "ALL", [this.#options.itemChildrenProp]: items } as T,
55
+ this.#options,
56
+ null
57
+ );
58
+
59
+ // Build item lookup table
60
+ this.lookup = new Map([...this.root].map(item => [item.key, item]));
61
+
62
+ // Set initially disabled items
63
+ this.#options.disabledKeys.forEach(disabledKey => this.disable(disabledKey));
64
+
65
+ // Set initially selected items : SelectionProvider
66
+ this.#options.selectedKeys.forEach(selectedKey => this.select(selectedKey));
67
+
68
+ // Set initially focused items : FocusProvider
69
+ this.firstFocusableItem = this.getFirstFocusableItem() ?? null;
70
+ if (this.firstFocusableItem) {
71
+ this.firstFocusableItem.state.setKey("focused", true);
72
+ this.focusedItem.set(this.firstFocusableItem);
73
+ }
74
+
75
+ // Set focus pointers: FocusProvider
76
+ const lookupData = [...this.lookup];
77
+ for (let index = 0; index < lookupData.length; index++) {
78
+ const [key, item] = lookupData[index];
79
+
80
+ if (!this.isFocusable(item)) continue;
81
+
82
+ if (index < lookupData.length - 1) {
83
+ let newIndex = index === lookupData.length - 1 ? 0 : index + 1;
84
+ while (newIndex < lookupData.length && !this.isFocusable(lookupData[newIndex][1])) {
85
+ newIndex = (newIndex + 1) % lookupData.length;
86
+ }
87
+
88
+ item.pointers.down = lookupData[newIndex][0];
89
+ lookupData[newIndex][1].pointers.up = key;
90
+ }
91
+ }
92
+ }
93
+
94
+
95
+ isFocusable(itemAtom: CompositeDataItem<T>) {
96
+ return !itemAtom.state.get().disabled;
97
+ }
98
+
99
+ getFirstFocusableItem() {
100
+ let iterItem = this.lookup.values().next();
101
+
102
+ while (iterItem.value && !this.isFocusable(iterItem.value)) {
103
+ iterItem = this.lookup.values().next();
104
+ }
105
+
106
+ return iterItem.value;
107
+ }
108
+
109
+ item(key: CompositeItemKey) { return this.lookup.get(key) };
110
+
111
+ descendants(itemKey: CompositeItemKey) {
112
+ const item = this.lookup.get(itemKey);
113
+ if (!item) return undefined;
114
+
115
+ return item.descendants();
116
+ }
117
+
118
+ ancestors(itemKey: CompositeItemKey) {
119
+ const item = this.lookup.get(itemKey);
120
+ if (!item) return undefined;
121
+
122
+ return item.ancestors();
123
+ }
124
+
125
+ focus(itemKey: CompositeItemKey): void;
126
+ focus(item: CompositeDataItem<T>): void;
127
+ focus(item: CompositeDataItem<T> | CompositeItemKey) {
128
+ const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
129
+ if (!_item) return;
130
+
131
+ if (this.focusedItem.get()) {
132
+ if (_item.key === this.focusedItem.get()?.key) return;
133
+
134
+ this.focusedItem.get()!.state.setKey("focused", false);
135
+ }
136
+
137
+ _item.state.setKey("focused", true);
138
+ this.focusedItem.set(_item);
139
+ }
140
+
141
+ toggleSelect(itemKey: CompositeItemKey, recursive?: boolean): void;
142
+ toggleSelect(item: CompositeDataItem<T>, recursive?: boolean): void;
143
+ toggleSelect(item: CompositeDataItem<T> | CompositeItemKey, recursive: boolean = false) {
144
+ const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
145
+ if (!_item) return;
146
+
147
+ if (_item.state.get().selected) this.deselect(_item, recursive);
148
+ else this.select(_item, recursive);
149
+ }
150
+
151
+ select(itemKey: CompositeItemKey, recursive?: boolean): void;
152
+ select(item: CompositeDataItem<T>, recursive?: boolean): void;
153
+ select(item: CompositeDataItem<T> | CompositeItemKey, recursive: boolean = false) {
154
+ const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
155
+ if (!_item) return;
156
+
157
+ const selectedKeys = _item.select(recursive);
158
+ this.selectedKeys.set(union(this.selectedKeys.get(), selectedKeys));
159
+ }
160
+
161
+ deselect(itemKey: CompositeItemKey, recursive?: boolean): void;
162
+ deselect(item: CompositeDataItem<T>, recursive?: boolean): void;
163
+ deselect(item: CompositeDataItem<T> | CompositeItemKey, recursive: boolean = false) {
164
+ const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
165
+ if (!_item) return;
166
+
167
+ const deselectedKeys = _item.deselect(recursive);
168
+ this.selectedKeys.set(difference(this.selectedKeys.get(), deselectedKeys));
169
+ }
170
+
171
+ disable(itemKey: CompositeItemKey, recursive?: boolean): void;
172
+ disable(item: CompositeDataItem<T>, recursive?: boolean): void;
173
+ disable(item: CompositeDataItem<T> | CompositeItemKey, recursive: boolean = false) {
174
+ const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
175
+ if (!_item) return;
176
+
177
+ const disabledKeys = _item.disable(recursive);
178
+ this.disabledKeys.set(union(this.selectedKeys.get(), disabledKeys));
179
+ }
180
+
181
+
182
+ enable(itemKey: CompositeItemKey, recursive?: boolean): void;
183
+ enable(item: CompositeDataItem<T>, recursive?: boolean): void;
184
+ enable(item: CompositeDataItem<T> | CompositeItemKey, recursive: boolean = false) {
185
+ const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
186
+ if (!_item) return;
187
+
188
+ const enabledKeys = _item.enable(recursive);
189
+ this.disabledKeys.set(union(this.selectedKeys.get(), enabledKeys));
190
+ }
191
+
192
+
193
+ updateItem(key: CompositeItemKey, data: Partial<T> | ((item: T) => Partial<T>)) {
194
+ const item = this.lookup.get(key);
195
+ if (item) {
196
+ const newData = {
197
+ ...item.data.get(), ...(
198
+ (typeof data === "function") ? data(item.data.get()) : data
199
+ )
200
+ };
201
+
202
+ const newKey = this.#options.getItemKey(newData);
203
+ if (item.key !== newKey) throw "Item key cannot be updated!";
204
+
205
+ this.#updateIfSelectedItem(key);
206
+ item.data.set(newData);
207
+ }
208
+ }
209
+
210
+ #updateIfSelectedItem(key: CompositeItemKey) {
211
+ if (this.selectedKeys.get().has(key)) {
212
+ this.selectedKeys.set(new Set([...this.selectedKeys.get()]));
213
+ }
214
+ }
215
+ }
@@ -0,0 +1,144 @@
1
+ import { omit } from "@/lib/object";
2
+ import { map, type PreinitializedMapStore, } from "nanostores";
3
+ import type { CompositeDataItemState, CompositeItemKey, CompositeDataItemOptions } from "./types";
4
+
5
+ export class CompositeDataItem<T extends object> implements Iterable<CompositeDataItem<T>> {
6
+ #key: CompositeItemKey;
7
+ parent: CompositeDataItem<T> | null = null;
8
+ children?: CompositeDataItem<T>[];
9
+ level: number;
10
+
11
+ data: PreinitializedMapStore<T>;
12
+ state: PreinitializedMapStore<CompositeDataItemState>;
13
+
14
+ pointers: {
15
+ left?: CompositeItemKey;
16
+ right?: CompositeItemKey;
17
+ up?: CompositeItemKey;
18
+ down?: CompositeItemKey;
19
+ };
20
+ childrenProp: string
21
+
22
+ constructor(item: T, options: CompositeDataItemOptions<T>, parent: CompositeDataItem<T> | null) {
23
+ this.#key = options.getItemKey(item);
24
+ this.data = map(omit(item, [options.itemChildrenProp]));
25
+ this.state = map({
26
+ focused: false,
27
+ selected: false,
28
+ disabled: false,
29
+ ...options.initialState
30
+ })
31
+ this.pointers = {};
32
+ this.parent = parent;
33
+ this.level = parent ? parent.level + 1 : 0;
34
+
35
+ this.childrenProp = options.itemChildrenProp;
36
+
37
+ const children = options.getItemChildren(item);
38
+ if (children) {
39
+ this.children = children.map(child => {
40
+ const childItem = new CompositeDataItem(child, options, this);
41
+ return childItem;
42
+ })
43
+ }
44
+ }
45
+
46
+ get key() {
47
+ return this.#key;
48
+ }
49
+
50
+ *[Symbol.iterator](): Iterator<CompositeDataItem<T>> {
51
+ if (this.key !== "ALL") yield this;
52
+ if (this.children)
53
+ for (const child of this.children)
54
+ for (const item of child) yield item;
55
+ }
56
+
57
+ toJSON() {
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ const json: any = { ...this.data.get() };
60
+
61
+ if (this.children) {
62
+ json[this.childrenProp] = [];
63
+ for (const child of this.children)
64
+ json[this.childrenProp].push(child.toJSON());
65
+ }
66
+
67
+ return json as T;
68
+ }
69
+
70
+ descendants() {
71
+ if (!this.children) return [];
72
+
73
+ const descendants: CompositeDataItem<T>[] = [];
74
+ this.children.forEach((child) => {
75
+ descendants.push(child);
76
+ const childDescendants = child.descendants();
77
+ if (childDescendants) descendants.push(...childDescendants);
78
+ }
79
+ )
80
+
81
+ return descendants;
82
+ }
83
+
84
+ ancestors() {
85
+ const ancestors: CompositeDataItem<T>[] = [];
86
+ let parent = this.parent;
87
+ while (parent) {
88
+ ancestors.push(parent);
89
+ parent = parent.parent;
90
+ }
91
+
92
+ return ancestors;
93
+ }
94
+
95
+
96
+ toggleSelect(recursive: boolean = false) {
97
+ if (this.state.get().selected) this.deselect(recursive);
98
+ else this.select(recursive);
99
+ }
100
+
101
+ select(recursive: boolean = false): CompositeItemKey[] {
102
+ this.state.setKey("selected", true);
103
+
104
+ if (recursive && this.children) {
105
+ const selectedChildKeys = this.children.map(child => child.select(true)).flat();
106
+ return [this.key, ...selectedChildKeys];
107
+ }
108
+
109
+ return [this.key]
110
+ }
111
+
112
+ deselect(recursive: boolean = false): CompositeItemKey[] {
113
+ this.state.setKey("selected", false);
114
+
115
+ if (recursive && this.children) {
116
+ const deselectedChildKeys = this.children.map(child => child.deselect(true)).flat();
117
+ return [this.key, ...deselectedChildKeys];
118
+ }
119
+
120
+ return [this.key];
121
+ }
122
+
123
+ disable(recursive: boolean = false): CompositeItemKey[] {
124
+ this.state.setKey("disabled", true);
125
+
126
+ if (recursive && this.children) {
127
+ const disabledChildKeys = this.children.map(child => child.disable(true)).flat();
128
+ return [this.key, ...disabledChildKeys];
129
+ }
130
+
131
+ return [this.key]
132
+ }
133
+
134
+ enable(recursive: boolean = false): CompositeItemKey[] {
135
+ this.state.setKey("disabled", false);
136
+
137
+ if (recursive && this.children) {
138
+ const enabledChildKeys = this.children.map(child => child.enable(true)).flat();
139
+ return [this.key, ...enabledChildKeys];
140
+ }
141
+
142
+ return [this.key];
143
+ }
144
+ }
@@ -0,0 +1,50 @@
1
+ import { useMemo } from "react";
2
+ import type { CompositeItemEventHandlers, CompositeItemProps } from "./types";
3
+ import { useStore } from "@nanostores/react";
4
+
5
+ export function CompositeComponentItem<T extends object>({
6
+ id,
7
+ className,
8
+ item,
9
+ mouseEventHandler,
10
+ keyboardEventHandler,
11
+ render,
12
+ }: CompositeItemProps<T>) {
13
+ const data = useStore(item.data);
14
+ const state = useStore(item.state);
15
+
16
+ const handlers: CompositeItemEventHandlers = useMemo(() => {
17
+ if (state.disabled) return {};
18
+
19
+ return {
20
+ mouseEventHandler: () => mouseEventHandler?.(item),
21
+ keyboardEventHandler: () => keyboardEventHandler?.(item),
22
+ };
23
+ }, [state.disabled, item, keyboardEventHandler, mouseEventHandler]);
24
+
25
+ return (
26
+ <div
27
+ id={id}
28
+ role="option"
29
+ aria-disabled={state.disabled}
30
+ data-key={item.key}
31
+ tabIndex={!state.disabled ? (state.focused ? 0 : -1) : undefined}
32
+ className={className}
33
+ >
34
+ {render({ data, key: item.key, level: item.level }, state, handlers)}
35
+ {item.children &&
36
+ item.children.length > 0 &&
37
+ [...item.children].map((child) => (
38
+ <CompositeComponentItem
39
+ id={`${id}-${item.key}`}
40
+ item={child}
41
+ key={child.key}
42
+ render={render}
43
+ className={className}
44
+ mouseEventHandler={mouseEventHandler}
45
+ keyboardEventHandler={keyboardEventHandler}
46
+ />
47
+ ))}
48
+ </div>
49
+ );
50
+ }
@@ -0,0 +1,100 @@
1
+ import { useCallback, useImperativeHandle, useRef } from "react";
2
+ import { CompositeDataItem } from "./CompositeDataItem";
3
+ import type { CompositeItemKey, CompositeProps } from "./types";
4
+ import { useKeyboardEvent } from "@/hooks/use-keyboard-event";
5
+ import { useCompositeContext } from "./composite-data-context";
6
+ import { CompositeComponentItem } from "./composite-component-item";
7
+ import { useId } from "@/hooks/use-id";
8
+
9
+ export function CompositeComponent<T extends object>({
10
+ renderItem,
11
+ className,
12
+ itemClassName,
13
+ ref,
14
+ id,
15
+ ...props
16
+ }: CompositeProps<T>) {
17
+ const compositeData = useCompositeContext<T>();
18
+ const compositeRef = useRef<HTMLDivElement>(null);
19
+ const compositeId = useId(id);
20
+
21
+ const focus = useCallback(
22
+ (itemKey: CompositeItemKey) => {
23
+ compositeData.focus(itemKey);
24
+ // const focusedItemEl = compositeRef.current?.querySelector<HTMLDivElement>(`[data-key="${itemKey}"]`);
25
+ // if (focusedItemEl) focusedItemEl.focus();
26
+ },
27
+ [compositeData]
28
+ );
29
+
30
+ const focusDown = useCallback(() => {
31
+ if (compositeData.focusedItem.get()?.pointers.down) {
32
+ focus(compositeData.focusedItem.get()!.pointers.down!);
33
+ }
34
+ }, [compositeData, focus]);
35
+
36
+ const focusUp = useCallback(() => {
37
+ if (compositeData.focusedItem.get()?.pointers.up) {
38
+ focus(compositeData.focusedItem.get()!.pointers.up!);
39
+ }
40
+ }, [compositeData, focus]);
41
+
42
+ const handleKeyDown = useKeyboardEvent({
43
+ ArrowUp: focusUp,
44
+ ArrowLeft: focusUp,
45
+ ArrowDown: focusDown,
46
+ ArrowRight: focusDown,
47
+ Space: () => {
48
+ if (compositeData.focusedItem.get()) compositeData.toggleSelect(compositeData.focusedItem.get()!);
49
+ },
50
+ });
51
+
52
+ const handleItemMouseEvent = useCallback(
53
+ (item: CompositeDataItem<T>) => {
54
+ focus(item.key);
55
+ compositeData.toggleSelect(item);
56
+ },
57
+ [compositeData, focus]
58
+ );
59
+
60
+ useImperativeHandle(
61
+ ref,
62
+ () => {
63
+ return {
64
+ focusDown() {
65
+ focusDown();
66
+ },
67
+ focusUp() {
68
+ focusUp();
69
+ },
70
+ select() {
71
+ if (compositeData.focusedItem.get()) compositeData.toggleSelect(compositeData.focusedItem.get()!);
72
+ },
73
+ };
74
+ },
75
+ [compositeData, focusDown, focusUp]
76
+ );
77
+
78
+ return (
79
+ <div
80
+ ref={compositeRef}
81
+ id={compositeId}
82
+ className={className}
83
+ tabIndex={-1}
84
+ role="listbox"
85
+ onKeyDown={handleKeyDown}
86
+ {...props}
87
+ >
88
+ {[...compositeData].map((item) => (
89
+ <CompositeComponentItem
90
+ className={itemClassName}
91
+ id={`${compositeId}-${item.key}`}
92
+ item={item}
93
+ key={item.key}
94
+ render={renderItem}
95
+ mouseEventHandler={handleItemMouseEvent}
96
+ />
97
+ ))}
98
+ </div>
99
+ );
100
+ }
@@ -0,0 +1,31 @@
1
+ import { createContext, useContext, useMemo } from "react";
2
+ import { CompositeData } from "./CompositeData";
3
+ import type { CompositeOptions } from "./types";
4
+
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ const CompositeDataContext = createContext<CompositeData<any> | undefined>(undefined);
7
+
8
+ export function useCompositeContext<T extends object>() {
9
+ const context = useContext(CompositeDataContext);
10
+
11
+ if (!context) {
12
+ throw new Error("No CompositeDataContext has been set, use CompositeDataContext to provide a context");
13
+ }
14
+
15
+ return context as CompositeData<T>;
16
+ }
17
+
18
+ interface CompositeComponentContextProps<T extends object> extends CompositeOptions {
19
+ data: T[];
20
+ children: React.ReactNode;
21
+ }
22
+
23
+ export function CompositeComponentContext<T extends object>({
24
+ data,
25
+ children,
26
+ ...compositeOptions
27
+ }: CompositeComponentContextProps<T>) {
28
+ const composite = useMemo(() => new CompositeData(data, compositeOptions), [data, compositeOptions]);
29
+
30
+ return <CompositeDataContext value={composite}>{children}</CompositeDataContext>;
31
+ }
@@ -0,0 +1,4 @@
1
+ export * from './composite-component.tsx'
2
+ export * from './composite-component-item.tsx'
3
+ export * from './composite-data-context.tsx'
4
+ export * from './types.ts'
@@ -0,0 +1,81 @@
1
+ import type { KeyboardEventHandler, MouseEventHandler, ReactNode } from "react";
2
+ import type { CompositeDataItem } from "./CompositeDataItem";
3
+
4
+ export type CompositeItemKey = string | number;
5
+
6
+ export interface CompositeOptions {
7
+ disabledKeys?: CompositeItemKey[];
8
+ selectedKeys?: CompositeItemKey[];
9
+
10
+ itemKeyProp?: string;
11
+ itemChildrenProp?: string;
12
+ }
13
+
14
+ interface CompositeDataPropGetters<T> {
15
+ getItemKey: (item: T) => CompositeItemKey;
16
+ getItemChildren: (item: T) => T[] | undefined;
17
+ }
18
+
19
+ export type CompositeDataOptions<T> = Required<CompositeOptions> & CompositeDataPropGetters<T>;
20
+
21
+ export type CompositeDataItemOptions<T> = CompositeDataPropGetters<T> & {
22
+ initialState?: CompositeDataItemState;
23
+ itemChildrenProp: string;
24
+ };
25
+
26
+ export interface CompositeDataItemState {
27
+ focused: boolean;
28
+ selected: boolean;
29
+ disabled: boolean;
30
+ }
31
+
32
+ export interface CompositeProps<T extends object> extends React.ComponentPropsWithoutRef<"div"> {
33
+ renderItem: CompositeItemRenderFn<T>;
34
+ className?: string;
35
+ itemClassName?: string;
36
+ ref?: React.Ref<CompositeHandle>
37
+ }
38
+
39
+
40
+ export interface CompositeHandle {
41
+ focusUp: () => void;
42
+ focusDown: () => void;
43
+ select: () => void;
44
+ }
45
+
46
+ export interface CompositeItemEventHandlers {
47
+ mouseEventHandler?: MouseEventHandler<HTMLElement>;
48
+ keyboardEventHandler?: KeyboardEventHandler<HTMLElement>;
49
+ }
50
+
51
+ export type CompositeItemRenderFn<T extends object> = (
52
+ item: { data: T; level: number; key: CompositeItemKey },
53
+ state: CompositeDataItemState,
54
+ eventHandlers: CompositeItemEventHandlers
55
+ ) => ReactNode;
56
+
57
+ export interface CompositeItemProps<T extends object> {
58
+ id: string;
59
+ className?: string;
60
+
61
+ item: CompositeDataItem<T>;
62
+
63
+ mouseEventHandler?: (itemAtom: CompositeDataItem<T>) => void;
64
+ keyboardEventHandler?: (itemAtom: CompositeDataItem<T>) => void;
65
+
66
+ remove?: () => void;
67
+ render: CompositeItemRenderFn<T>;
68
+ }
69
+
70
+ export interface CompositeFocusProvider {
71
+ setPointers: () => void;
72
+
73
+ goFirst: () => void;
74
+ goLast: () => void;
75
+
76
+ goUp: () => void;
77
+ goDown: () => void;
78
+ goLeft: () => void;
79
+ goRight: () => void;
80
+
81
+ }
@@ -5,12 +5,12 @@ import { useCallback, useRef } from "react";
5
5
  import { cva, type VariantProps } from "class-variance-authority";
6
6
 
7
7
  const horizontalListVariants = cva(
8
- "scrollbar-hidden overscroll-x-contain flex flex-row flex-nowrap py-1 gap-card overflow-x-auto snap-x snap-mandatory touch-pan-x scroll-smooth *:shrink-0 *:grow-0 *:snap-center *:select-none",
8
+ "scrollbar-hidden overscroll-x-contain flex flex-row flex-nowrap py-1 gap-(--gap-card) overflow-x-auto snap-x snap-mandatory touch-pan-x scroll-smooth *:shrink-0 *:grow-0 *:snap-center *:select-none",
9
9
  {
10
10
  variants: {
11
11
  variant: {
12
12
  contain: "w-full ",
13
- overflow: "w-screen px-section -ms-section scroll-px-(--section-p)",
13
+ overflow: "w-screen px-(--spacing-section) -ms-(--spacing-section) scroll-px-(--spacing-section)",
14
14
  },
15
15
  },
16
16
  defaultVariants: {
@@ -6,6 +6,7 @@ export * from './button.tsx'
6
6
  export * from './card.tsx'
7
7
  export * from './horizontal-list.tsx'
8
8
  export * from './input.tsx'
9
+ export * from './navigation-menu.tsx'
9
10
  export * from './overlay.tsx'
10
11
  export * from './page-header.tsx'
11
12
  export * from './page-section.tsx'