@bccampus/ui-components 0.4.0 → 0.4.2
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/dist/AbstractFocusProvider-CxvlcEki.js +29 -0
- package/dist/AbstractSelectionProvider-BtaROstC.js +30 -0
- package/dist/CompositeDataItem-DuHOHCWy.js +158 -0
- package/dist/ListboxFocusProvider.d.ts +149 -0
- package/dist/ListboxFocusProvider.js +53 -0
- package/dist/MultipleSelectionProvider.d.ts +141 -0
- package/dist/MultipleSelectionProvider.js +19 -0
- package/dist/SingleSelectionProvider.d.ts +141 -0
- package/dist/SingleSelectionProvider.js +23 -0
- package/dist/composite-component-DSUbd1XS.js +122 -0
- package/dist/composite.d.ts +108 -51
- package/dist/composite.js +57 -447
- package/dist/icon-generator-tuhuqdpL.js +76 -0
- package/dist/icon-generator.d.ts +11 -4
- package/dist/icon-generator.js +4 -74
- package/dist/listbox.d.ts +171 -0
- package/dist/listbox.js +76 -0
- package/dist/masked-image-generator.js +7 -7
- package/dist/ui-components.js +5 -5
- package/package.json +5 -1
- package/src/components/ui/composite/CompositeData.ts +22 -114
- package/src/components/ui/composite/FocusProvider/AbstractFocusProvider.ts +83 -0
- package/src/components/ui/composite/FocusProvider/ListboxFocusProvider.ts +74 -0
- package/src/components/ui/composite/SelectionProvider/AbstractSelectionProvider.ts +45 -0
- package/src/components/ui/composite/SelectionProvider/MultipleSelectionProvider.ts +28 -0
- package/src/components/ui/composite/SelectionProvider/SingleSelectionProvider.ts +37 -0
- package/src/components/ui/composite/composite-component-item.tsx +12 -10
- package/src/components/ui/composite/composite-component.tsx +39 -68
- package/src/components/ui/composite/index.ts +4 -1
- package/src/components/ui/composite/listbox.tsx +61 -0
- package/src/components/ui/composite/types.ts +51 -30
- package/src/components/ui/icon-generator/index.ts +2 -0
- package/src/hooks/use-keyboard-event.ts +31 -42
- package/vite.config.ts +6 -2
- package/src/components/ui/composite/composite-data-context.tsx +0 -31
- /package/dist/{igenerate-tiles.d.ts → generate-tiles.d.ts} +0 -0
- /package/dist/{igenerate-tiles.js → generate-tiles.js} +0 -0
package/dist/icon-generator.js
CHANGED
|
@@ -1,76 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { T as o, g as l } from "./generate-tiles-DuagGD1d.js";
|
|
4
|
-
import { c as m } from "./utils-CRiPKpXj.js";
|
|
5
|
-
const k = [
|
|
6
|
-
[o.PieRand, o.PieRand],
|
|
7
|
-
[o.PieRand, o.PieRand]
|
|
8
|
-
];
|
|
9
|
-
function b({
|
|
10
|
-
pattern: e = k,
|
|
11
|
-
tileSize: h = 64,
|
|
12
|
-
tileClassName: s,
|
|
13
|
-
tileBgClassName: c,
|
|
14
|
-
renderChildren: d,
|
|
15
|
-
fit: i = "none",
|
|
16
|
-
...g
|
|
17
|
-
}) {
|
|
18
|
-
const t = r(
|
|
19
|
-
() => ({
|
|
20
|
-
width: e.reduce((n, v) => Math.max(n, v.length), 0) * h,
|
|
21
|
-
height: e.length * h
|
|
22
|
-
}),
|
|
23
|
-
[e, h]
|
|
24
|
-
), a = r(() => {
|
|
25
|
-
const n = l(e, h, s);
|
|
26
|
-
return c && n.unshift(
|
|
27
|
-
/* @__PURE__ */ u(
|
|
28
|
-
"rect",
|
|
29
|
-
{
|
|
30
|
-
x: 0,
|
|
31
|
-
y: 0,
|
|
32
|
-
width: t.width,
|
|
33
|
-
height: t.height,
|
|
34
|
-
className: m("fill-black stroke-2 stroke-black", c)
|
|
35
|
-
},
|
|
36
|
-
"svg-mask-invert"
|
|
37
|
-
)
|
|
38
|
-
), n;
|
|
39
|
-
}, [e, t, s, c, h]), w = r(() => {
|
|
40
|
-
switch (i) {
|
|
41
|
-
case "fill":
|
|
42
|
-
return t.width <= t.height ? "100%" : void 0;
|
|
43
|
-
case "width":
|
|
44
|
-
return "100%";
|
|
45
|
-
case "none":
|
|
46
|
-
return t.width;
|
|
47
|
-
default:
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
}, [i, t]), f = r(() => {
|
|
51
|
-
switch (i) {
|
|
52
|
-
case "fill":
|
|
53
|
-
return t.height < t.width ? "100%" : void 0;
|
|
54
|
-
case "height":
|
|
55
|
-
return "100%";
|
|
56
|
-
case "none":
|
|
57
|
-
return t.height;
|
|
58
|
-
default:
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
}, [i, t]);
|
|
62
|
-
return /* @__PURE__ */ u(
|
|
63
|
-
"svg",
|
|
64
|
-
{
|
|
65
|
-
width: w,
|
|
66
|
-
height: f,
|
|
67
|
-
viewBox: `0 0 ${t.width} ${t.height}`,
|
|
68
|
-
overflow: "visible",
|
|
69
|
-
...g,
|
|
70
|
-
children: d ? d(a, t.width, t.height) : a
|
|
71
|
-
}
|
|
72
|
-
);
|
|
73
|
-
}
|
|
1
|
+
import { I as r } from "./icon-generator-tuhuqdpL.js";
|
|
2
|
+
import { T as p } from "./generate-tiles-DuagGD1d.js";
|
|
74
3
|
export {
|
|
75
|
-
|
|
4
|
+
r as IconGenerator,
|
|
5
|
+
p as TileShape
|
|
76
6
|
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { JSX } from 'react/jsx-runtime';
|
|
2
|
+
import { KeyboardEventHandler } from 'react';
|
|
3
|
+
import { MouseEventHandler } from 'react';
|
|
4
|
+
import { PreinitializedMapStore } from 'nanostores';
|
|
5
|
+
import { PreinitializedWritableAtom } from 'nanostores';
|
|
6
|
+
import { ReactNode } from 'react';
|
|
7
|
+
|
|
8
|
+
declare abstract class AbstractFocusProvider<T extends object> implements FocusProvider {
|
|
9
|
+
protected data: CompositeData<T>;
|
|
10
|
+
protected dataOptions: CompositeDataOptions<T>;
|
|
11
|
+
firstFocusableItem: CompositeDataItem<T> | null;
|
|
12
|
+
lastFocusableItem: CompositeDataItem<T> | null;
|
|
13
|
+
focusedItem: PreinitializedWritableAtom<CompositeDataItem<T> | null>;
|
|
14
|
+
init(data: CompositeData<T>, dataOptions: CompositeDataOptions<T>): void;
|
|
15
|
+
isFocusable(itemAtom: CompositeDataItem<T>): boolean;
|
|
16
|
+
focus(itemKey: CompositeItemKey): void;
|
|
17
|
+
focus(item: CompositeDataItem<T>): void;
|
|
18
|
+
abstract setFocusPointers(): readonly [first: CompositeDataItem<T> | null, last: CompositeDataItem<T> | null];
|
|
19
|
+
abstract focusUp(): void;
|
|
20
|
+
abstract focusDown(): void;
|
|
21
|
+
abstract focusLeft(): void;
|
|
22
|
+
abstract focusRight(): void;
|
|
23
|
+
abstract focusPageUp(): void;
|
|
24
|
+
abstract focesPageDown(): void;
|
|
25
|
+
abstract focusToFirst(): void;
|
|
26
|
+
abstract focusToLast(): void;
|
|
27
|
+
abstract focusToFirstInRow(): void;
|
|
28
|
+
abstract focusToLastInRow(): void;
|
|
29
|
+
abstract focusToTypeAheadMatch(): void;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
declare abstract class AbstractSelectionProvider<T extends object> {
|
|
33
|
+
protected data: CompositeData<T>;
|
|
34
|
+
protected dataOptions: CompositeDataOptions<T>;
|
|
35
|
+
selectedKeys: PreinitializedWritableAtom<Set<CompositeItemKey>>;
|
|
36
|
+
init(data: CompositeData<T>, dataOptions: CompositeDataOptions<T>): void;
|
|
37
|
+
abstract select(itemKey?: CompositeItemKey, recursive?: boolean): void;
|
|
38
|
+
abstract select(item: CompositeDataItem<T>, recursive?: boolean): void;
|
|
39
|
+
abstract select(item?: CompositeDataItem<T> | CompositeItemKey, recursive?: boolean): void;
|
|
40
|
+
abstract deselect(itemKey?: CompositeItemKey, recursive?: boolean): void;
|
|
41
|
+
abstract deselect(item: CompositeDataItem<T>, recursive?: boolean): void;
|
|
42
|
+
abstract deselect(item?: CompositeDataItem<T> | CompositeItemKey, recursive?: boolean): void;
|
|
43
|
+
toggleSelect(itemKey?: CompositeItemKey, recursive?: boolean): void;
|
|
44
|
+
toggleSelect(item: CompositeDataItem<T>, recursive?: boolean): void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
declare interface BaseCompositeProps<T extends object> extends React.ComponentPropsWithoutRef<"div"> {
|
|
48
|
+
data: CompositeData<T>;
|
|
49
|
+
className?: string;
|
|
50
|
+
ref?: React.Ref<HTMLDivElement>;
|
|
51
|
+
handleRef?: React.Ref<CompositeHandle>;
|
|
52
|
+
renderItem: CompositeItemRenderFn<T>;
|
|
53
|
+
itemClassName?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
declare class CompositeData<T extends object> implements Iterable<CompositeDataItem<T>> {
|
|
57
|
+
#private;
|
|
58
|
+
lookup: Map<CompositeItemKey, CompositeDataItem<T>>;
|
|
59
|
+
focusProvider: AbstractFocusProvider<T>;
|
|
60
|
+
selectionProvider?: AbstractSelectionProvider<T>;
|
|
61
|
+
disabledKeys: Set<CompositeItemKey>;
|
|
62
|
+
root: CompositeDataItem<T>;
|
|
63
|
+
constructor(items: T[], providerOptions: CompositeProviderOptions<T>, options?: CompositeOptions);
|
|
64
|
+
[Symbol.iterator](): Iterator<CompositeDataItem<T>>;
|
|
65
|
+
toJSON(): T[];
|
|
66
|
+
init(items: T[]): void;
|
|
67
|
+
item(key: CompositeItemKey): CompositeDataItem<T> | undefined;
|
|
68
|
+
descendants(itemKey: CompositeItemKey): CompositeDataItem<T>[] | undefined;
|
|
69
|
+
ancestors(itemKey: CompositeItemKey): CompositeDataItem<T>[] | undefined;
|
|
70
|
+
updateItem(key: CompositeItemKey, data: Partial<T> | ((item: T) => Partial<T>)): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
declare class CompositeDataItem<T extends object> implements Iterable<CompositeDataItem<T>> {
|
|
74
|
+
#private;
|
|
75
|
+
parent: CompositeDataItem<T> | null;
|
|
76
|
+
children?: CompositeDataItem<T>[];
|
|
77
|
+
level: number;
|
|
78
|
+
data: PreinitializedMapStore<T>;
|
|
79
|
+
state: PreinitializedMapStore<CompositeDataItemState>;
|
|
80
|
+
pointers: {
|
|
81
|
+
left?: CompositeItemKey;
|
|
82
|
+
right?: CompositeItemKey;
|
|
83
|
+
up?: CompositeItemKey;
|
|
84
|
+
down?: CompositeItemKey;
|
|
85
|
+
};
|
|
86
|
+
childrenProp: string;
|
|
87
|
+
constructor(item: T, options: CompositeDataItemOptions<T>, parent: CompositeDataItem<T> | null);
|
|
88
|
+
get key(): CompositeItemKey;
|
|
89
|
+
[Symbol.iterator](): Iterator<CompositeDataItem<T>>;
|
|
90
|
+
toJSON(): T;
|
|
91
|
+
descendants(): CompositeDataItem<T>[];
|
|
92
|
+
ancestors(): CompositeDataItem<T>[];
|
|
93
|
+
toggleSelect(recursive?: boolean): void;
|
|
94
|
+
select(recursive?: boolean): CompositeItemKey[];
|
|
95
|
+
deselect(recursive?: boolean): CompositeItemKey[];
|
|
96
|
+
disable(recursive?: boolean): CompositeItemKey[];
|
|
97
|
+
enable(recursive?: boolean): CompositeItemKey[];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
declare type CompositeDataItemOptions<T> = CompositeDataPropGetters<T> & {
|
|
101
|
+
initialState?: CompositeDataItemState;
|
|
102
|
+
itemChildrenProp: string;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
declare interface CompositeDataItemState {
|
|
106
|
+
focused: boolean;
|
|
107
|
+
selected: boolean;
|
|
108
|
+
disabled: boolean;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
declare type CompositeDataOptions<T> = Required<CompositeOptions> & CompositeDataPropGetters<T>;
|
|
112
|
+
|
|
113
|
+
declare interface CompositeDataPropGetters<T> {
|
|
114
|
+
getItemKey: (item: T) => CompositeItemKey;
|
|
115
|
+
getItemChildren: (item: T) => T[] | undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
declare interface CompositeEventHandlers {
|
|
119
|
+
mouseEventHandler?: MouseEventHandler<HTMLElement>;
|
|
120
|
+
keyboardEventHandler?: KeyboardEventHandler<HTMLElement>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
declare interface CompositeHandle {
|
|
124
|
+
focusProvider: FocusProvider;
|
|
125
|
+
selectionProvider?: SelectionProvider;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
declare type CompositeItemKey = string | number;
|
|
129
|
+
|
|
130
|
+
declare type CompositeItemRenderFn<T extends object> = (item: {
|
|
131
|
+
data: T;
|
|
132
|
+
level: number;
|
|
133
|
+
key: CompositeItemKey;
|
|
134
|
+
}, state: CompositeDataItemState, eventHandlers: CompositeEventHandlers) => ReactNode;
|
|
135
|
+
|
|
136
|
+
declare interface CompositeOptions {
|
|
137
|
+
disabledKeys?: CompositeItemKey[];
|
|
138
|
+
selectedKeys?: CompositeItemKey[];
|
|
139
|
+
itemKeyProp?: string;
|
|
140
|
+
itemChildrenProp?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
declare interface CompositeProviderOptions<T extends object> {
|
|
144
|
+
focusProvider: AbstractFocusProvider<T>;
|
|
145
|
+
selectionProvider?: AbstractSelectionProvider<T>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
declare interface FocusProvider {
|
|
149
|
+
focus(itemKey: CompositeItemKey): void;
|
|
150
|
+
focusUp(): void;
|
|
151
|
+
focusDown(): void;
|
|
152
|
+
focusLeft(): void;
|
|
153
|
+
focusRight(): void;
|
|
154
|
+
focusPageUp(): void;
|
|
155
|
+
focesPageDown(): void;
|
|
156
|
+
focusToFirst(): void;
|
|
157
|
+
focusToLast(): void;
|
|
158
|
+
focusToFirstInRow(): void;
|
|
159
|
+
focusToLastInRow(): void;
|
|
160
|
+
focusToTypeAheadMatch(): void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export declare function Listbox<T extends object>({ data, ...props }: BaseCompositeProps<T>): JSX.Element;
|
|
164
|
+
|
|
165
|
+
declare interface SelectionProvider {
|
|
166
|
+
select(itemKey?: CompositeItemKey, recursive?: boolean): void;
|
|
167
|
+
deselect(itemKey?: CompositeItemKey, recursive?: boolean): void;
|
|
168
|
+
toggleSelect(itemKey?: CompositeItemKey, recursive?: boolean): void;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export { }
|
package/dist/listbox.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { jsx as y } from "react/jsx-runtime";
|
|
2
|
+
import { C as d } from "./composite-component-DSUbd1XS.js";
|
|
3
|
+
import { useMemo as m, useRef as a, useCallback as u } from "react";
|
|
4
|
+
const h = /* @__PURE__ */ new Set(["ctrl", "shift", "alt", "meta"]), f = {
|
|
5
|
+
" ": "space"
|
|
6
|
+
};
|
|
7
|
+
function K(e) {
|
|
8
|
+
const r = [];
|
|
9
|
+
for (const [t, n] of Object.entries(e)) {
|
|
10
|
+
const o = t.toLowerCase().trim().split(/\s*\+\s*/g);
|
|
11
|
+
o.length === 1 && h.has(o[0]) ? console.error(`[useKeyboardEvent] '${t}': A key sequence cannot be only a modifier key.`) : o.includes("") ? console.error(`[useKeyboardEvent] '${t}': Invalid key sequence defined in the sequence.`) : r.push({
|
|
12
|
+
sequence: new RegExp("^" + o.join("\\+") + "$"),
|
|
13
|
+
handler: n
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return r;
|
|
17
|
+
}
|
|
18
|
+
const l = {
|
|
19
|
+
eventKeyProp: "key"
|
|
20
|
+
};
|
|
21
|
+
function v(e, r = l) {
|
|
22
|
+
const t = { ...r, ...l }, n = K(e);
|
|
23
|
+
return (o) => {
|
|
24
|
+
const c = o[t.eventKeyProp], s = [];
|
|
25
|
+
o.ctrlKey && s.push("ctrl"), o.shiftKey && s.push("shift"), o.altKey && s.push("alt"), o.metaKey && s.push("meta"), f[c] ? s.push(f[c]) : s.push(c.toLowerCase());
|
|
26
|
+
const i = n.find((p) => p.sequence.test(s.join("+")));
|
|
27
|
+
i && (o.preventDefault(), o.stopPropagation(), i.handler(o));
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function P(e, r) {
|
|
31
|
+
return m(() => v(e, r), [e, r]);
|
|
32
|
+
}
|
|
33
|
+
function k({ data: e, ...r }) {
|
|
34
|
+
const t = a(null), n = u(() => {
|
|
35
|
+
const s = e.focusProvider.focusedItem.get()?.key;
|
|
36
|
+
if (s && t.current) {
|
|
37
|
+
const i = t.current.querySelector(`[data-key="${s}"]`);
|
|
38
|
+
i && i.focus();
|
|
39
|
+
}
|
|
40
|
+
}, [e]), o = P({
|
|
41
|
+
ArrowUp: () => {
|
|
42
|
+
e.focusProvider.focusUp(), n();
|
|
43
|
+
},
|
|
44
|
+
ArrowDown: () => {
|
|
45
|
+
e.focusProvider.focusDown(), n();
|
|
46
|
+
},
|
|
47
|
+
Home: () => {
|
|
48
|
+
e.focusProvider.focusToFirst(), n();
|
|
49
|
+
},
|
|
50
|
+
End: () => {
|
|
51
|
+
e.focusProvider.focusToLast(), n();
|
|
52
|
+
},
|
|
53
|
+
Space: () => {
|
|
54
|
+
e.selectionProvider?.toggleSelect(), n();
|
|
55
|
+
}
|
|
56
|
+
}), c = u(
|
|
57
|
+
(s) => {
|
|
58
|
+
e.focusProvider.focus(s.key), e.selectionProvider?.toggleSelect(s), n();
|
|
59
|
+
},
|
|
60
|
+
[e.focusProvider, e.selectionProvider, n]
|
|
61
|
+
);
|
|
62
|
+
return /* @__PURE__ */ y(
|
|
63
|
+
d,
|
|
64
|
+
{
|
|
65
|
+
ref: t,
|
|
66
|
+
variant: "listbox",
|
|
67
|
+
data: e,
|
|
68
|
+
onKeyDown: o,
|
|
69
|
+
itemMouseEventHandler: c,
|
|
70
|
+
...r
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
k as Listbox
|
|
76
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx as o, jsxs as c, Fragment as d } from "react/jsx-runtime";
|
|
2
2
|
import { useId as p, useMemo as l } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import { I as M } from "./icon-generator-tuhuqdpL.js";
|
|
4
4
|
const h = { cover: "slice", contain: "meet", fill: "none" }, k = { top: "xMidYMin", center: "xMidYMid", bottom: "xMidYMax" };
|
|
5
|
-
function
|
|
6
|
-
src:
|
|
5
|
+
function g({
|
|
6
|
+
src: s,
|
|
7
7
|
imageFit: e = "cover",
|
|
8
8
|
imagePosition: r = "top",
|
|
9
|
-
maskType:
|
|
9
|
+
maskType: n = "alpha",
|
|
10
10
|
...a
|
|
11
11
|
}) {
|
|
12
12
|
const t = p(), i = l(
|
|
@@ -18,12 +18,12 @@ function u({
|
|
|
18
18
|
{
|
|
19
19
|
...a,
|
|
20
20
|
renderChildren: (m) => /* @__PURE__ */ c(d, { children: [
|
|
21
|
-
/* @__PURE__ */ o("mask", { id: `svg-mask${t}`, "mask-type":
|
|
22
|
-
/* @__PURE__ */ o("image", { href:
|
|
21
|
+
/* @__PURE__ */ o("mask", { id: `svg-mask${t}`, "mask-type": n, children: m }),
|
|
22
|
+
/* @__PURE__ */ o("image", { href: s, width: "100%", height: "100%", mask: `url(#svg-mask${t})`, preserveAspectRatio: i })
|
|
23
23
|
] })
|
|
24
24
|
}
|
|
25
25
|
);
|
|
26
26
|
}
|
|
27
27
|
export {
|
|
28
|
-
|
|
28
|
+
g as MaskedImageGenerator
|
|
29
29
|
};
|
package/dist/ui-components.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { I as o } from "./icon-generator-tuhuqdpL.js";
|
|
2
2
|
import { MaskedImageGenerator as e } from "./masked-image-generator.js";
|
|
3
3
|
import { Caption as i, captionVariants as p } from "./caption.js";
|
|
4
4
|
import { Banner as m } from "./banner.js";
|
|
5
5
|
import { Button as u, buttonVariants as C } from "./button.js";
|
|
6
|
-
import { Card as x, CardArea as M, CardCaption as v, CardContent as I, CardImage as N, CardItemGroup as
|
|
6
|
+
import { Card as x, CardArea as M, CardCaption as v, CardContent as I, CardImage as N, CardItemGroup as s, CardMedia as c, CardMeta as l, CardSubcaption as S, CardSubtitle as T, CardTitle as b, CardToolbar as V } from "./card.js";
|
|
7
7
|
import { HorizontalList as L } from "./horizontal-list.js";
|
|
8
8
|
import { Input as k } from "./input.js";
|
|
9
9
|
import { NavigationMenu as B, NavigationMenuContent as H, NavigationMenuIndicator as h, NavigationMenuItem as w, NavigationMenuLink as z, NavigationMenuList as A, NavigationMenuTrigger as O, NavigationMenuViewport as j, navigationMenuTriggerStyle as q } from "./navigation-menu.js";
|
|
@@ -22,9 +22,9 @@ export {
|
|
|
22
22
|
v as CardCaption,
|
|
23
23
|
I as CardContent,
|
|
24
24
|
N as CardImage,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
s as CardItemGroup,
|
|
26
|
+
c as CardMedia,
|
|
27
|
+
l as CardMeta,
|
|
28
28
|
S as CardSubcaption,
|
|
29
29
|
T as CardSubtitle,
|
|
30
30
|
b as CardTitle,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bccampus/ui-components",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "yarn@4.10.3",
|
|
6
6
|
"exports": {
|
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
"types": "./dist/composite.d.ts",
|
|
13
13
|
"import": "./dist/composite.js"
|
|
14
14
|
},
|
|
15
|
+
"./listbox": {
|
|
16
|
+
"types": "./dist/listbox.d.ts",
|
|
17
|
+
"import": "./dist/listbox.js"
|
|
18
|
+
},
|
|
15
19
|
"./icon-generator": {
|
|
16
20
|
"types": "./dist/icon-generator.d.ts",
|
|
17
21
|
"import": "./dist/icon-generator.js"
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { atom, type PreinitializedWritableAtom } from "nanostores";
|
|
3
|
-
import type { CompositeDataOptions, CompositeItemKey, CompositeOptions } from "./types";
|
|
1
|
+
import type { CompositeDataOptions, CompositeItemKey, CompositeOptions, CompositeProviderOptions } from "./types";
|
|
4
2
|
import { CompositeDataItem } from "./CompositeDataItem";
|
|
3
|
+
import type { AbstractFocusProvider } from "./FocusProvider/AbstractFocusProvider";
|
|
4
|
+
import type { AbstractSelectionProvider } from "./SelectionProvider/AbstractSelectionProvider";
|
|
5
|
+
import { union } from "@/lib/set-operations";
|
|
5
6
|
|
|
6
7
|
export class CompositeData<T extends object> implements Iterable<CompositeDataItem<T>> {
|
|
7
8
|
#options: CompositeDataOptions<T>;
|
|
8
9
|
|
|
9
10
|
lookup: Map<CompositeItemKey, CompositeDataItem<T>> = new Map();
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
focusProvider: AbstractFocusProvider<T>;
|
|
13
|
+
selectionProvider?: AbstractSelectionProvider<T>;
|
|
14
|
+
|
|
15
|
+
disabledKeys: Set<CompositeItemKey> = new Set();
|
|
15
16
|
|
|
16
17
|
root!: CompositeDataItem<T>;
|
|
17
18
|
|
|
18
|
-
constructor(items: T[], options?: CompositeOptions) {
|
|
19
|
+
constructor(items: T[], providerOptions: CompositeProviderOptions<T>, options?: CompositeOptions) {
|
|
19
20
|
this.#options = {
|
|
20
21
|
itemKeyProp: "key",
|
|
21
22
|
itemChildrenProp: "children",
|
|
@@ -29,6 +30,9 @@ export class CompositeData<T extends object> implements Iterable<CompositeDataIt
|
|
|
29
30
|
...options
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
this.focusProvider = providerOptions.focusProvider;
|
|
34
|
+
this.selectionProvider = providerOptions.selectionProvider;
|
|
35
|
+
|
|
32
36
|
this.init(items);
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -49,7 +53,6 @@ export class CompositeData<T extends object> implements Iterable<CompositeDataIt
|
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
init(items: T[]) {
|
|
52
|
-
// this.itemAtoms = this.#setItemMap(items);
|
|
53
56
|
this.root = new CompositeDataItem(
|
|
54
57
|
{ [this.#options.itemKeyProp]: "ALL", [this.#options.itemChildrenProp]: items } as T,
|
|
55
58
|
this.#options,
|
|
@@ -60,50 +63,12 @@ export class CompositeData<T extends object> implements Iterable<CompositeDataIt
|
|
|
60
63
|
this.lookup = new Map([...this.root].map(item => [item.key, item]));
|
|
61
64
|
|
|
62
65
|
// Set initially disabled items
|
|
63
|
-
this.#options.disabledKeys.forEach(disabledKey => this
|
|
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
|
-
}
|
|
66
|
+
this.#options.disabledKeys.forEach(disabledKey => this.#disable(disabledKey));
|
|
93
67
|
|
|
68
|
+
if (this.selectionProvider)
|
|
69
|
+
this.selectionProvider.init(this, this.#options);
|
|
94
70
|
|
|
95
|
-
|
|
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;
|
|
71
|
+
this.focusProvider.init(this, this.#options);
|
|
107
72
|
}
|
|
108
73
|
|
|
109
74
|
item(key: CompositeItemKey) { return this.lookup.get(key) };
|
|
@@ -122,71 +87,14 @@ export class CompositeData<T extends object> implements Iterable<CompositeDataIt
|
|
|
122
87
|
return item.ancestors();
|
|
123
88
|
}
|
|
124
89
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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) {
|
|
90
|
+
#disable(itemKey: CompositeItemKey, recursive?: boolean): void;
|
|
91
|
+
#disable(item: CompositeDataItem<T>, recursive?: boolean): void;
|
|
92
|
+
#disable(item: CompositeDataItem<T> | CompositeItemKey, recursive: boolean = false) {
|
|
174
93
|
const _item = item instanceof CompositeDataItem ? item : this.lookup.get(item);
|
|
175
94
|
if (!_item) return;
|
|
176
95
|
|
|
177
96
|
const disabledKeys = _item.disable(recursive);
|
|
178
|
-
this.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));
|
|
97
|
+
this.disabledKeys = union(this.disabledKeys, disabledKeys);
|
|
190
98
|
}
|
|
191
99
|
|
|
192
100
|
|
|
@@ -208,8 +116,8 @@ export class CompositeData<T extends object> implements Iterable<CompositeDataIt
|
|
|
208
116
|
}
|
|
209
117
|
|
|
210
118
|
#updateIfSelectedItem(key: CompositeItemKey) {
|
|
211
|
-
if (this.selectedKeys.get().has(key)) {
|
|
212
|
-
this.selectedKeys.set(new Set([...this.selectedKeys.get()]));
|
|
119
|
+
if (this.selectionProvider && this.selectionProvider.selectedKeys.get().has(key)) {
|
|
120
|
+
this.selectionProvider.selectedKeys.set(new Set([...this.selectionProvider.selectedKeys.get()]));
|
|
213
121
|
}
|
|
214
122
|
}
|
|
215
123
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { atom, type PreinitializedWritableAtom } from 'nanostores';
|
|
2
|
+
import type { CompositeData } from '../CompositeData';
|
|
3
|
+
import { CompositeDataItem } from '../CompositeDataItem';
|
|
4
|
+
import type { CompositeDataOptions, CompositeItemKey } from '../types';
|
|
5
|
+
|
|
6
|
+
export interface FocusProvider {
|
|
7
|
+
focus(itemKey: CompositeItemKey): void;
|
|
8
|
+
|
|
9
|
+
focusUp(): void;
|
|
10
|
+
focusDown(): void;
|
|
11
|
+
focusLeft(): void;
|
|
12
|
+
focusRight(): void;
|
|
13
|
+
focusPageUp(): void;
|
|
14
|
+
focesPageDown(): void;
|
|
15
|
+
focusToFirst(): void;
|
|
16
|
+
focusToLast(): void;
|
|
17
|
+
focusToFirstInRow(): void;
|
|
18
|
+
focusToLastInRow(): void;
|
|
19
|
+
focusToTypeAheadMatch(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export abstract class AbstractFocusProvider<T extends object> implements FocusProvider {
|
|
23
|
+
protected data!: CompositeData<T>;
|
|
24
|
+
protected dataOptions!: CompositeDataOptions<T>;
|
|
25
|
+
|
|
26
|
+
firstFocusableItem!: CompositeDataItem<T> | null;
|
|
27
|
+
lastFocusableItem!: CompositeDataItem<T> | null;
|
|
28
|
+
focusedItem: PreinitializedWritableAtom<CompositeDataItem<T> | null> = atom(null);
|
|
29
|
+
|
|
30
|
+
init(data: CompositeData<T>, dataOptions: CompositeDataOptions<T>) {
|
|
31
|
+
this.data = data;
|
|
32
|
+
this.dataOptions = dataOptions;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
// Set focus pointers
|
|
36
|
+
const [first, last] = this.setFocusPointers();
|
|
37
|
+
|
|
38
|
+
// Set initially focused items
|
|
39
|
+
this.firstFocusableItem = first;
|
|
40
|
+
if (this.firstFocusableItem) {
|
|
41
|
+
this.firstFocusableItem.state.setKey("focused", true);
|
|
42
|
+
this.focusedItem.set(this.firstFocusableItem);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.lastFocusableItem = last;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
isFocusable(itemAtom: CompositeDataItem<T>) {
|
|
50
|
+
return !itemAtom.state.get().disabled;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
focus(itemKey: CompositeItemKey): void;
|
|
54
|
+
focus(item: CompositeDataItem<T>): void;
|
|
55
|
+
focus(item: CompositeDataItem<T> | CompositeItemKey) {
|
|
56
|
+
const _item = item instanceof CompositeDataItem ? item : this.data.lookup.get(item);
|
|
57
|
+
if (!_item) return;
|
|
58
|
+
|
|
59
|
+
if (this.focusedItem.get()) {
|
|
60
|
+
if (_item.key === this.focusedItem.get()?.key) return;
|
|
61
|
+
|
|
62
|
+
this.focusedItem.get()!.state.setKey("focused", false);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_item.state.setKey("focused", true);
|
|
66
|
+
this.focusedItem.set(_item);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
abstract setFocusPointers(): readonly [first: CompositeDataItem<T> | null, last: CompositeDataItem<T> | null];
|
|
70
|
+
|
|
71
|
+
abstract focusUp(): void;
|
|
72
|
+
abstract focusDown(): void;
|
|
73
|
+
abstract focusLeft(): void;
|
|
74
|
+
abstract focusRight(): void;
|
|
75
|
+
abstract focusPageUp(): void;
|
|
76
|
+
abstract focesPageDown(): void;
|
|
77
|
+
abstract focusToFirst(): void;
|
|
78
|
+
abstract focusToLast(): void;
|
|
79
|
+
abstract focusToFirstInRow(): void;
|
|
80
|
+
abstract focusToLastInRow(): void;
|
|
81
|
+
abstract focusToTypeAheadMatch(): void;
|
|
82
|
+
|
|
83
|
+
}
|