@bccampus/ui-components 0.7.2 → 0.8.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 (77) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/.yarnrc.yml +0 -2
  3. package/dist/_chunks/createLucideIcon.js +11 -10
  4. package/dist/_chunks/index.js +2 -2
  5. package/dist/_chunks/index4.js +2 -2
  6. package/dist/components/index.js +18 -13
  7. package/dist/components/ui/composite/CompositeData.d.ts +9 -8
  8. package/dist/components/ui/composite/CompositeData.js +20 -14
  9. package/dist/components/ui/composite/{FocusProvider/AbstractFocusProvider.d.ts → FocusManager/AbstractFocusManager.d.ts} +2 -19
  10. package/dist/components/ui/composite/{FocusProvider/AbstractFocusProvider.js → FocusManager/AbstractFocusManager.js} +16 -5
  11. package/dist/components/ui/composite/{FocusProvider/ListboxFocusProvider.d.ts → FocusManager/ListboxFocusManager.d.ts} +2 -2
  12. package/dist/components/ui/composite/{FocusProvider/ListboxFocusProvider.js → FocusManager/ListboxFocusManager.js} +3 -3
  13. package/dist/components/ui/composite/FocusManager/index.d.ts +3 -0
  14. package/dist/components/ui/composite/FocusManager/index.js +6 -0
  15. package/dist/components/ui/composite/FocusManager/types.d.ts +20 -0
  16. package/dist/components/ui/composite/FocusManager/types.js +1 -0
  17. package/dist/components/ui/composite/{SelectionProvider/AbstractSelectionProvider.d.ts → SelectionManager/AbstractSelectionManager.d.ts} +5 -20
  18. package/dist/components/ui/composite/{SelectionProvider/AbstractSelectionProvider.js → SelectionManager/AbstractSelectionManager.js} +8 -4
  19. package/dist/components/ui/composite/{SelectionProvider/MultipleSelectionProvider.d.ts → SelectionManager/MultipleSelectionManager.d.ts} +3 -2
  20. package/dist/components/ui/composite/{SelectionProvider/MultipleSelectionProvider.js → SelectionManager/MultipleSelectionManager.js} +4 -3
  21. package/dist/components/ui/composite/{SelectionProvider/SingleSelectionProvider.d.ts → SelectionManager/SingleSelectionManager.d.ts} +3 -2
  22. package/dist/components/ui/composite/{SelectionProvider/SingleSelectionProvider.js → SelectionManager/SingleSelectionManager.js} +6 -5
  23. package/dist/components/ui/composite/SelectionManager/index.d.ts +4 -0
  24. package/dist/components/ui/composite/SelectionManager/index.js +8 -0
  25. package/dist/components/ui/composite/SelectionManager/types.d.ts +19 -0
  26. package/dist/components/ui/composite/SelectionManager/types.js +1 -0
  27. package/dist/components/ui/composite/components/composite-component.d.ts +2 -0
  28. package/dist/components/ui/composite/{composite-component.js → components/composite-component.js} +16 -38
  29. package/dist/components/ui/composite/components/index.d.ts +5 -0
  30. package/dist/components/ui/composite/components/index.js +10 -0
  31. package/dist/components/ui/composite/{listbox.d.ts → components/listbox.d.ts} +1 -1
  32. package/dist/components/ui/composite/{listbox.js → components/listbox.js} +17 -12
  33. package/dist/components/ui/composite/components/types.d.ts +53 -0
  34. package/dist/components/ui/composite/components/types.js +1 -0
  35. package/dist/components/ui/composite/components/utils.d.ts +1 -0
  36. package/dist/components/ui/composite/components/utils.js +8 -0
  37. package/dist/components/ui/composite/create-composite-data.d.ts +80 -0
  38. package/dist/components/ui/composite/create-composite-data.js +22 -0
  39. package/dist/components/ui/composite/index.d.ts +4 -5
  40. package/dist/components/ui/composite/index.js +18 -13
  41. package/dist/components/ui/composite/types.d.ts +5 -67
  42. package/dist/components/ui/navigation-menu.js +1 -1
  43. package/dist/components/ui/popover.js +1 -1
  44. package/dist/hooks/index.d.ts +1 -0
  45. package/dist/hooks/index.js +3 -1
  46. package/eslint.config.js +1 -1
  47. package/package.json +17 -16
  48. package/src/components/ui/composite/CompositeData.ts +38 -30
  49. package/src/components/ui/composite/{FocusProvider/AbstractFocusProvider.ts → FocusManager/AbstractFocusManager.ts} +19 -26
  50. package/src/components/ui/composite/{FocusProvider/ListboxFocusProvider.ts → FocusManager/ListboxFocusManager.ts} +2 -2
  51. package/src/components/ui/composite/FocusManager/index.ts +3 -0
  52. package/src/components/ui/composite/FocusManager/types.ts +23 -0
  53. package/src/components/ui/composite/{SelectionProvider/AbstractSelectionProvider.ts → SelectionManager/AbstractSelectionManager.ts} +13 -28
  54. package/src/components/ui/composite/{SelectionProvider/MultipleSelectionProvider.ts → SelectionManager/MultipleSelectionManager.ts} +4 -2
  55. package/src/components/ui/composite/{SelectionProvider/SingleSelectionProvider.ts → SelectionManager/SingleSelectionManager.ts} +6 -4
  56. package/src/components/ui/composite/SelectionManager/index.ts +4 -0
  57. package/src/components/ui/composite/SelectionManager/types.ts +24 -0
  58. package/src/components/ui/composite/{composite-component.tsx → components/composite-component.tsx} +17 -42
  59. package/src/components/ui/composite/components/index.ts +5 -0
  60. package/src/components/ui/composite/{listbox.tsx → components/listbox.tsx} +16 -11
  61. package/src/components/ui/composite/components/types.ts +66 -0
  62. package/src/components/ui/composite/components/utils.ts +6 -0
  63. package/src/components/ui/composite/create-composite-data.ts +98 -0
  64. package/src/components/ui/composite/index.ts +7 -8
  65. package/src/components/ui/composite/types.ts +5 -86
  66. package/src/hooks/index.ts +4 -3
  67. package/.yarn/releases/yarn-4.10.3.cjs +0 -942
  68. package/dist/components/ui/composite/FocusProvider/index.d.ts +0 -2
  69. package/dist/components/ui/composite/FocusProvider/index.js +0 -6
  70. package/dist/components/ui/composite/SelectionProvider/index.d.ts +0 -3
  71. package/dist/components/ui/composite/SelectionProvider/index.js +0 -8
  72. package/dist/components/ui/composite/composite-component.d.ts +0 -2
  73. package/src/components/ui/composite/FocusProvider/index.ts +0 -2
  74. package/src/components/ui/composite/SelectionProvider/index.ts +0 -3
  75. /package/dist/components/ui/composite/{composite-component-item.d.ts → components/composite-component-item.d.ts} +0 -0
  76. /package/dist/components/ui/composite/{composite-component-item.js → components/composite-component-item.js} +0 -0
  77. /package/src/components/ui/composite/{composite-component-item.tsx → components/composite-component-item.tsx} +0 -0
@@ -1,29 +1,11 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { useCallback, useImperativeHandle, useMemo, useEffect } from "react";
2
+ import { useCallback, useImperativeHandle, useEffect } from "react";
3
3
  import { CompositeComponentItem } from "./composite-component-item.js";
4
- import { useId } from "../../../hooks/use-id.js";
5
- import { useRequiredRef } from "../../../hooks/use-required-ref.js";
6
- const defaultRoles = {
7
- listbox: {
8
- rootRole: "listbox",
9
- groupRole: "group",
10
- itemRole: "option"
11
- },
12
- grid: {
13
- rootRole: "grid",
14
- groupRole: "rowgroup",
15
- itemRole: "row"
16
- },
17
- custom: void 0
18
- };
19
- const isOutsideElement = (parent, el) => {
20
- if (parent === el) return false;
21
- const nodeId = !!el?.id && CSS.escape(el.id);
22
- return !nodeId || parent?.querySelector(`#${nodeId}`) === null;
23
- };
4
+ import { useId } from "../../../../hooks/use-id.js";
5
+ import { useRequiredRef } from "../../../../hooks/use-required-ref.js";
6
+ import { isOutsideElement } from "./utils.js";
24
7
  function CompositeComponent({
25
8
  data,
26
- variant,
27
9
  rootRole,
28
10
  itemRole,
29
11
  groupRole,
@@ -43,35 +25,31 @@ function CompositeComponent({
43
25
  const $rootRef = useRequiredRef(rootRef);
44
26
  const focusElement = useCallback(() => {
45
27
  if (softFocus) return;
46
- const itemKey = data.focusProvider.focusedItem.get()?.key;
28
+ const itemKey = data.focusManager.focusedItem.get()?.key;
47
29
  if (itemKey && $rootRef.current) {
48
30
  const focusedItemEl = $rootRef.current.querySelector(`[data-key="${itemKey}"]`);
49
31
  if (focusedItemEl) focusedItemEl.focus();
50
32
  }
51
- }, [softFocus, data.focusProvider.focusedItem, $rootRef]);
33
+ }, [softFocus, data.focusManager.focusedItem, $rootRef]);
52
34
  useImperativeHandle(handleRef, () => {
53
35
  return {
54
- focusProvider: data.focusProvider,
55
- selectionProvider: data.selectionProvider,
36
+ focusManager: data.focusManager,
37
+ selectionManager: data.selectionManager,
56
38
  focusElement
57
39
  };
58
40
  }, [data, focusElement]);
59
- const roles = useMemo(
60
- () => defaultRoles[variant] ?? { rootRole, groupRole, itemRole },
61
- [groupRole, itemRole, rootRole, variant]
62
- );
63
41
  const intiFocus = useCallback(
64
42
  (onlyData) => {
65
43
  let initialFocusItem = null;
66
- if (initialFocus === "FirstItem") initialFocusItem = data.focusProvider.firstFocusableItem;
44
+ if (initialFocus === "FirstItem") initialFocusItem = data.focusManager.firstFocusableItem;
67
45
  else if (initialFocus === "SelectedItem") {
68
- const selecedItem = data.selectionProvider?.selectedKeys.get().values().next().value;
69
- initialFocusItem = selecedItem ?? data.focusProvider.firstFocusableItem;
46
+ const selecedItem = data.selectionManager?.selectedKeys.get().values().next().value;
47
+ initialFocusItem = selecedItem ?? data.focusManager.firstFocusableItem;
70
48
  }
71
- data.focusProvider.focus(initialFocusItem);
49
+ data.focusManager.focus(initialFocusItem);
72
50
  if (!onlyData) focusElement();
73
51
  },
74
- [data.focusProvider, data.selectionProvider?.selectedKeys, focusElement, initialFocus]
52
+ [data.focusManager, data.selectionManager?.selectedKeys, focusElement, initialFocus]
75
53
  );
76
54
  const onFocusHandler = useCallback(
77
55
  (ev) => {
@@ -88,7 +66,7 @@ function CompositeComponent({
88
66
  id: compositeId,
89
67
  className,
90
68
  tabIndex: initialFocus === "None" ? 0 : -1,
91
- role: roles.rootRole,
69
+ role: rootRole,
92
70
  ...props,
93
71
  onFocus: onFocusHandler,
94
72
  children: [...data].map((item) => /* @__PURE__ */ jsx(
@@ -96,8 +74,8 @@ function CompositeComponent({
96
74
  {
97
75
  className: itemClassName,
98
76
  id: `${compositeId}-${item.key}`,
99
- role: roles.itemRole,
100
- groupRole: roles.groupRole,
77
+ role: itemRole,
78
+ groupRole,
101
79
  item,
102
80
  render: renderItem,
103
81
  itemMouseEventHandler,
@@ -0,0 +1,5 @@
1
+ export * from './composite-component.tsx';
2
+ export * from './composite-component-item.tsx';
3
+ export * from './listbox.tsx';
4
+ export * from './utils.ts';
5
+ export type * from './types';
@@ -0,0 +1,10 @@
1
+ import { CompositeComponent } from "./composite-component.js";
2
+ import { CompositeComponentItem } from "./composite-component-item.js";
3
+ import { Listbox } from "./listbox.js";
4
+ import { isOutsideElement } from "./utils.js";
5
+ export {
6
+ CompositeComponent,
7
+ CompositeComponentItem,
8
+ Listbox,
9
+ isOutsideElement
10
+ };
@@ -1,2 +1,2 @@
1
1
  import { BaseCompositeProps } from './types';
2
- export declare function Listbox<T extends object>({ data, rootRef, handleRef, initialFocus, ...props }: BaseCompositeProps<T>): import("react").JSX.Element;
2
+ export declare function Listbox<T extends object>({ data, rootRef, handleRef, initialFocus, className, ...props }: BaseCompositeProps<T>): import("react").JSX.Element;
@@ -2,59 +2,64 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { CompositeComponent } from "./composite-component.js";
3
3
  import { useCallback } from "react";
4
4
  import "nanostores";
5
- import { useKeyboardEvent } from "../../../hooks/use-keyboard-event.js";
6
- import { useRequiredRef } from "../../../hooks/use-required-ref.js";
5
+ import { useKeyboardEvent } from "../../../../hooks/use-keyboard-event.js";
6
+ import { useRequiredRef } from "../../../../hooks/use-required-ref.js";
7
+ import { c as cn } from "../../../../_chunks/utils.js";
7
8
  function Listbox({
8
9
  data,
9
10
  rootRef,
10
11
  handleRef,
11
12
  initialFocus = "SelectedItem",
13
+ className,
12
14
  ...props
13
15
  }) {
14
16
  const $handleRef = useRequiredRef(handleRef);
15
17
  const handleKeyboardEvent = useKeyboardEvent(
16
18
  {
17
19
  ArrowUp: () => {
18
- data.focusProvider.focusUp();
20
+ data.focusManager.focusUp();
19
21
  $handleRef.current?.focusElement();
20
22
  },
21
23
  ArrowDown: () => {
22
- data.focusProvider.focusDown();
24
+ data.focusManager.focusDown();
23
25
  $handleRef.current?.focusElement();
24
26
  },
25
27
  Home: () => {
26
- data.focusProvider.focusToFirst();
28
+ data.focusManager.focusToFirst();
27
29
  $handleRef.current?.focusElement();
28
30
  },
29
31
  End: () => {
30
- data.focusProvider.focusToLast();
32
+ data.focusManager.focusToLast();
31
33
  $handleRef.current?.focusElement();
32
34
  },
33
35
  Space: () => {
34
- data.selectionProvider?.toggleSelect();
36
+ data.selectionManager?.toggleSelect();
35
37
  $handleRef.current?.focusElement();
36
38
  }
37
39
  },
38
- [$handleRef, data.focusProvider]
40
+ [$handleRef, data.focusManager]
39
41
  );
40
42
  const handleItemMouseEvent = useCallback(
41
43
  (item) => {
42
- data.focusProvider.focus(item.key);
43
- data.selectionProvider?.toggleSelect(item);
44
+ data.focusManager.focus(item.key);
45
+ data.selectionManager?.toggleSelect(item);
44
46
  $handleRef.current?.focusElement();
45
47
  },
46
- [$handleRef, data.focusProvider, data.selectionProvider]
48
+ [$handleRef, data.focusManager, data.selectionManager]
47
49
  );
48
50
  return /* @__PURE__ */ jsx(
49
51
  CompositeComponent,
50
52
  {
51
53
  rootRef,
52
54
  handleRef: $handleRef,
53
- variant: "listbox",
55
+ rootRole: "listbox",
56
+ groupRole: "group",
57
+ itemRole: "option",
54
58
  data,
55
59
  initialFocus,
56
60
  onKeyDown: handleKeyboardEvent,
57
61
  itemMouseEventHandler: handleItemMouseEvent,
62
+ className: cn("flex flex-col gap-1", className),
58
63
  ...props
59
64
  }
60
65
  );
@@ -0,0 +1,53 @@
1
+ import { AriaRole, KeyboardEventHandler, MouseEventHandler, ReactNode } from 'react';
2
+ import { CompositeData } from '../CompositeData';
3
+ import { CompositeDataItemState, CompositeItemKey } from '../types';
4
+ import { IFocusManager } from '../FocusManager';
5
+ import { SelectionManager } from '../SelectionManager';
6
+ import { CompositeDataItem } from '../CompositeDataItem';
7
+ export type InitialFocusTarget = "None" | "LastFocusedItem" | "SelectedItem" | "FirstItem";
8
+ export interface BaseCompositeProps<T extends object> extends React.ComponentPropsWithoutRef<"div"> {
9
+ data: CompositeData<T>;
10
+ className?: string;
11
+ rootRef?: React.RefObject<HTMLDivElement | null>;
12
+ handleRef?: React.RefObject<CompositeHandle<T> | null>;
13
+ renderItem: CompositeItemRenderFn<T>;
14
+ itemClassName?: string;
15
+ initialFocus?: InitialFocusTarget;
16
+ /**
17
+ * Set `item.focused = true`, but not focus on the HTMLElement
18
+ */
19
+ softFocus?: boolean;
20
+ }
21
+ export interface CompositeProps<T extends object> extends BaseCompositeProps<T>, CompositeItemEventHandlerFunctions<T> {
22
+ rootRole: AriaRole;
23
+ itemRole: AriaRole;
24
+ groupRole: AriaRole;
25
+ }
26
+ export interface CompositeHandle<T extends object> {
27
+ focusManager: IFocusManager<T>;
28
+ focusElement: () => void;
29
+ selectionManager?: SelectionManager<T>;
30
+ }
31
+ export type CompositeItemRenderFn<T extends object> = (item: {
32
+ data: T;
33
+ level: number;
34
+ key: CompositeItemKey;
35
+ }, state: CompositeDataItemState, eventHandlers: CompositeEventHandlers) => ReactNode;
36
+ export interface CompositeItemProps<T extends object> extends CompositeItemEventHandlerFunctions<T> {
37
+ id: string;
38
+ className?: string;
39
+ role?: AriaRole;
40
+ groupRole?: AriaRole;
41
+ item: CompositeDataItem<T>;
42
+ remove?: () => void;
43
+ render: CompositeItemRenderFn<T>;
44
+ softFocus?: boolean;
45
+ }
46
+ export interface CompositeEventHandlers {
47
+ mouseEventHandler?: MouseEventHandler<HTMLElement>;
48
+ keyboardEventHandler?: KeyboardEventHandler<HTMLElement>;
49
+ }
50
+ export interface CompositeItemEventHandlerFunctions<T extends object> {
51
+ itemMouseEventHandler?: (itemAtom: CompositeDataItem<T>) => void;
52
+ itemKeyboardEventHandler?: (itemAtom: CompositeDataItem<T>) => void;
53
+ }
@@ -0,0 +1 @@
1
+ export declare const isOutsideElement: (parent: Element | null, el: Element | null) => boolean;
@@ -0,0 +1,8 @@
1
+ const isOutsideElement = (parent, el) => {
2
+ if (parent === el) return false;
3
+ const nodeId = !!el?.id && CSS.escape(el.id);
4
+ return !nodeId || parent?.querySelector(`#${nodeId}`) === null;
5
+ };
6
+ export {
7
+ isOutsideElement
8
+ };
@@ -0,0 +1,80 @@
1
+ import { CompositeData } from './CompositeData';
2
+ import { CompositeOptions, CompositeManagerOptions } from './types';
3
+ /**
4
+ * Returns nanostores object with `CompositeData` instance,
5
+ *
6
+ * @example
7
+ * ```
8
+ * const items = [...];
9
+ * const compositeData = createCompositeData(
10
+ * items,
11
+ * {
12
+ * focusManager: new ListboxFocusManager(),
13
+ * selectionManager: new SingleSelectionManager(),
14
+ * },
15
+ * {
16
+ * disabledKeys: ["key 02", "key 04", "key 3023"],
17
+ * selectedKeys: ["key 207", "key 101", "key 3021"],
18
+ * itemChildrenProp: "nodes",
19
+ * },
20
+ * );
21
+ *
22
+ * export function CompositeSelectedItems() {
23
+ * const composite = useStore(compositeData);
24
+ * const selections = useStore(composite.data.selectionManager!.selectedKeys);
25
+ * return (
26
+ * <div className="py-4 flex gap-2 items-center justify-star ">
27
+ * <span className="text-lg font-semibold text-accent-foreground">Selected Items:</span>
28
+ * <div className="flex gap-2 flex-wrap items-center justify-start *:bg-blue-100 *:p-2">
29
+ * {[...selections].map((selectedKey) => {
30
+ * return <span key={selectedKey}>{composite.data.item(selectedKey)?.data.get().name}</span>;
31
+ * })}
32
+ * </div>
33
+ * </div>
34
+ * );
35
+ * }
36
+ * ```
37
+ */
38
+ export declare const createCompositeData: <T extends object>(items: T[], managerOptions: CompositeManagerOptions<T>, options?: CompositeOptions) => import('nanostores').ReadableAtom<{
39
+ data: CompositeData<T>;
40
+ version: number;
41
+ }>;
42
+ /**
43
+ * Returns nanostores object with `CompositeData` instance.
44
+ *
45
+ * Intialize the store by calling the `fetch` function without any arguments
46
+ *
47
+ * @example
48
+ * ```
49
+ * const compositeAsyncData = createAsyncCompositeData(
50
+ * async (url?: string) => {
51
+ * if (!url) return undefined;
52
+ *
53
+ * const response = await fetch(url);
54
+ * if (response.ok) {
55
+ * const data = await response.json();
56
+ * return data.results as NamedListItem[];
57
+ * }
58
+ *
59
+ * return null;
60
+ * },
61
+ * {
62
+ * focusManager: new ListboxFocusManager(),
63
+ * selectionManager: new MultipleSelectionManager(),
64
+ * },
65
+ * {
66
+ * itemKeyProp: "name",
67
+ * },
68
+ * );
69
+ *
70
+ * export function CompositeSomethign() {
71
+ * const composite = useStore(compositeAsyncData);
72
+ * return <Listbox data={composite.data} ... />
73
+ * }
74
+ * ```
75
+ */
76
+ export declare const createAsyncCompositeData: <T extends object, FnArgs = never>(fetch: (options?: FnArgs) => Promise<T[] | undefined | null>, managerOptions: CompositeManagerOptions<T>, options?: CompositeOptions) => import('nanostores').ReadableAtom<{
77
+ data: CompositeData<T>;
78
+ version: number;
79
+ load: (options?: FnArgs) => void;
80
+ }>;
@@ -0,0 +1,22 @@
1
+ import { computed } from "nanostores";
2
+ import { CompositeData } from "./CompositeData.js";
3
+ const createCompositeData = (items, managerOptions, options) => {
4
+ const $store = new CompositeData(items, managerOptions, options);
5
+ return computed($store.version, (version) => {
6
+ return { data: $store, version };
7
+ });
8
+ };
9
+ const createAsyncCompositeData = (fetch, managerOptions, options) => {
10
+ const load = (options2) => {
11
+ fetch(options2).then((items) => items && $store.setItems(items));
12
+ };
13
+ const $store = new CompositeData(null, managerOptions, options);
14
+ load();
15
+ return computed($store.version, (version) => {
16
+ return { data: $store, version, load };
17
+ });
18
+ };
19
+ export {
20
+ createAsyncCompositeData,
21
+ createCompositeData
22
+ };
@@ -1,8 +1,7 @@
1
1
  export * from './CompositeData.ts';
2
2
  export * from './CompositeDataItem.ts';
3
- export * from './FocusProvider';
4
- export * from './SelectionProvider';
5
- export * from './composite-component.tsx';
6
- export * from './composite-component-item.tsx';
7
- export * from './listbox.tsx';
3
+ export * from './FocusManager/index.ts';
4
+ export * from './SelectionManager/index.ts';
5
+ export * from './components';
6
+ export * from './create-composite-data.ts';
8
7
  export type * from './types';
@@ -1,22 +1,27 @@
1
1
  import { CompositeData } from "./CompositeData.js";
2
2
  import { CompositeDataItem } from "./CompositeDataItem.js";
3
- import { AbstractFocusProvider } from "./FocusProvider/AbstractFocusProvider.js";
4
- import { ListboxFocusProvider } from "./FocusProvider/ListboxFocusProvider.js";
5
- import { AbstractSelectionProvider } from "./SelectionProvider/AbstractSelectionProvider.js";
6
- import { SingleSelectionProvider } from "./SelectionProvider/SingleSelectionProvider.js";
7
- import { MultipleSelectionProvider } from "./SelectionProvider/MultipleSelectionProvider.js";
8
- import { CompositeComponent } from "./composite-component.js";
9
- import { CompositeComponentItem } from "./composite-component-item.js";
10
- import { Listbox } from "./listbox.js";
3
+ import { AbstractFocusManager } from "./FocusManager/AbstractFocusManager.js";
4
+ import { ListboxFocusManager } from "./FocusManager/ListboxFocusManager.js";
5
+ import { AbstractSelectionManager } from "./SelectionManager/AbstractSelectionManager.js";
6
+ import { SingleSelectionManager } from "./SelectionManager/SingleSelectionManager.js";
7
+ import { MultipleSelectionManager } from "./SelectionManager/MultipleSelectionManager.js";
8
+ import { CompositeComponent } from "./components/composite-component.js";
9
+ import { CompositeComponentItem } from "./components/composite-component-item.js";
10
+ import { Listbox } from "./components/listbox.js";
11
+ import { isOutsideElement } from "./components/utils.js";
12
+ import { createAsyncCompositeData, createCompositeData } from "./create-composite-data.js";
11
13
  export {
12
- AbstractFocusProvider,
13
- AbstractSelectionProvider,
14
+ AbstractFocusManager,
15
+ AbstractSelectionManager,
14
16
  CompositeComponent,
15
17
  CompositeComponentItem,
16
18
  CompositeData,
17
19
  CompositeDataItem,
18
20
  Listbox,
19
- ListboxFocusProvider,
20
- MultipleSelectionProvider,
21
- SingleSelectionProvider
21
+ ListboxFocusManager,
22
+ MultipleSelectionManager,
23
+ SingleSelectionManager,
24
+ createAsyncCompositeData,
25
+ createCompositeData,
26
+ isOutsideElement
22
27
  };
@@ -1,26 +1,6 @@
1
- import { AriaRole, KeyboardEventHandler, MouseEventHandler, ReactNode } from 'react';
2
- import { CompositeDataItem } from './CompositeDataItem';
3
- import { AbstractFocusProvider, FocusProvider } from './FocusProvider/AbstractFocusProvider';
4
- import { AbstractSelectionProvider, SelectionProvider } from './SelectionProvider/AbstractSelectionProvider';
5
- import { CompositeData } from './CompositeData';
1
+ import { AbstractFocusManager } from './FocusManager/AbstractFocusManager';
2
+ import { AbstractSelectionManager } from './SelectionManager/AbstractSelectionManager';
6
3
  export type CompositeItemKey = string | number;
7
- export type CompositeRoles = {
8
- variant: "listbox";
9
- rootRole?: never;
10
- itemRole?: never;
11
- groupRole?: never;
12
- } | {
13
- variant: "grid";
14
- rootRole?: never;
15
- itemRole?: never;
16
- groupRole?: never;
17
- } | {
18
- variant: "custom";
19
- rootRole: AriaRole;
20
- itemRole: AriaRole;
21
- groupRole: AriaRole;
22
- };
23
- export type InitialFocusTarget = "None" | "LastFocusedItem" | "SelectedItem" | "FirstItem";
24
4
  export interface CompositeOptions {
25
5
  disabledKeys?: CompositeItemKey[];
26
6
  selectedKeys?: CompositeItemKey[];
@@ -31,9 +11,9 @@ interface CompositeDataPropGetters<T> {
31
11
  getItemKey: (item: T) => CompositeItemKey;
32
12
  getItemChildren: (item: T) => T[] | undefined;
33
13
  }
34
- export interface CompositeProviderOptions<T extends object> {
35
- focusProvider: AbstractFocusProvider<T>;
36
- selectionProvider?: AbstractSelectionProvider<T>;
14
+ export interface CompositeManagerOptions<T extends object> {
15
+ focusManager: AbstractFocusManager<T>;
16
+ selectionManager?: AbstractSelectionManager<T>;
37
17
  }
38
18
  export type CompositeDataOptions<T> = Required<CompositeOptions> & CompositeDataPropGetters<T>;
39
19
  export type CompositeDataItemOptions<T> = CompositeDataPropGetters<T> & {
@@ -45,46 +25,4 @@ export interface CompositeDataItemState {
45
25
  selected: boolean;
46
26
  disabled: boolean;
47
27
  }
48
- export interface CompositeEventHandlers {
49
- mouseEventHandler?: MouseEventHandler<HTMLElement>;
50
- keyboardEventHandler?: KeyboardEventHandler<HTMLElement>;
51
- }
52
- export interface CompositeItemEventHandlerFunctions<T extends object> {
53
- itemMouseEventHandler?: (itemAtom: CompositeDataItem<T>) => void;
54
- itemKeyboardEventHandler?: (itemAtom: CompositeDataItem<T>) => void;
55
- }
56
- export interface BaseCompositeProps<T extends object> extends React.ComponentPropsWithoutRef<"div"> {
57
- data: CompositeData<T>;
58
- className?: string;
59
- rootRef?: React.RefObject<HTMLDivElement | null>;
60
- handleRef?: React.RefObject<CompositeHandle<T> | null>;
61
- renderItem: CompositeItemRenderFn<T>;
62
- itemClassName?: string;
63
- initialFocus?: InitialFocusTarget;
64
- /**
65
- * Set `item.focused = true`, but not focus on the HTMLElement
66
- */
67
- softFocus?: boolean;
68
- }
69
- export type CompositeProps<T extends object> = BaseCompositeProps<T> & CompositeItemEventHandlerFunctions<T> & CompositeRoles;
70
- export interface CompositeHandle<T extends object> {
71
- focusProvider: FocusProvider<T>;
72
- focusElement: () => void;
73
- selectionProvider?: SelectionProvider<T>;
74
- }
75
- export type CompositeItemRenderFn<T extends object> = (item: {
76
- data: T;
77
- level: number;
78
- key: CompositeItemKey;
79
- }, state: CompositeDataItemState, eventHandlers: CompositeEventHandlers) => ReactNode;
80
- export interface CompositeItemProps<T extends object> extends CompositeItemEventHandlerFunctions<T> {
81
- id: string;
82
- className?: string;
83
- role?: AriaRole;
84
- groupRole?: AriaRole;
85
- item: CompositeDataItem<T>;
86
- remove?: () => void;
87
- render: CompositeItemRenderFn<T>;
88
- softFocus?: boolean;
89
- }
90
28
  export {};
@@ -55,7 +55,7 @@ function createSlotClone(ownerName) {
55
55
  SlotClone.displayName = `${ownerName}.SlotClone`;
56
56
  return SlotClone;
57
57
  }
58
- var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
58
+ var SLOTTABLE_IDENTIFIER = /* @__PURE__ */ Symbol("radix.slottable");
59
59
  function isSlottable(child) {
60
60
  return React.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
61
61
  }
@@ -2478,7 +2478,7 @@ function createSlotClone(ownerName) {
2478
2478
  SlotClone.displayName = `${ownerName}.SlotClone`;
2479
2479
  return SlotClone;
2480
2480
  }
2481
- var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
2481
+ var SLOTTABLE_IDENTIFIER = /* @__PURE__ */ Symbol("radix.slottable");
2482
2482
  function isSlottable(child) {
2483
2483
  return React.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
2484
2484
  }
@@ -1,3 +1,4 @@
1
1
  export * from './use-effect-after-mount';
2
2
  export * from './use-id';
3
3
  export * from './use-keyboard-event';
4
+ export * from './use-required-ref';
@@ -1,9 +1,11 @@
1
1
  import { useEffectAfterMount } from "./use-effect-after-mount.js";
2
2
  import { useId } from "./use-id.js";
3
3
  import { keyboardEventHandler, useKeyboardEvent } from "./use-keyboard-event.js";
4
+ import { useRequiredRef } from "./use-required-ref.js";
4
5
  export {
5
6
  keyboardEventHandler,
6
7
  useEffectAfterMount,
7
8
  useId,
8
- useKeyboardEvent
9
+ useKeyboardEvent,
10
+ useRequiredRef
9
11
  };
package/eslint.config.js CHANGED
@@ -12,7 +12,7 @@ export default defineConfig([
12
12
  extends: [
13
13
  js.configs.recommended,
14
14
  tseslint.configs.recommended,
15
- reactHooks.configs['recommended-latest'],
15
+ reactHooks.configs.flat['recommended-latest'],
16
16
  reactRefresh.configs.vite,
17
17
  ],
18
18
  languageOptions: {
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@bccampus/ui-components",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
- "packageManager": "yarn@4.10.3",
6
5
  "exports": {
7
6
  ".": {
8
7
  "types": "./dist/components/index.d.ts",
@@ -24,6 +23,7 @@
24
23
  },
25
24
  "scripts": {
26
25
  "dev": "vite",
26
+ "check": "tsc -b && eslint ./src",
27
27
  "build": "tsc -b && vite build",
28
28
  "lint": "eslint .",
29
29
  "publish": "yarn build && npm publish --access public --scope=@bccampus",
@@ -39,10 +39,10 @@
39
39
  "@radix-ui/react-slot": "^1.2.4",
40
40
  "class-variance-authority": "^0.7.1",
41
41
  "clsx": "^2.1.1",
42
- "lucide-react": "^0.560.0",
42
+ "lucide-react": "^0.563.0",
43
43
  "nanostores": "^1.1.0",
44
- "react": "^19.2.1",
45
- "react-dom": "^19.2.1",
44
+ "react": "^19.2.3",
45
+ "react-dom": "^19.2.3",
46
46
  "tailwind-merge": "^3.4.0",
47
47
  "tailwindcss": "^4.1.18",
48
48
  "tw-animate-css": "^1.4.0"
@@ -56,21 +56,22 @@
56
56
  "tw-animate-css": "^1.4.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@eslint/js": "^9.39.1",
59
+ "@eslint/js": "^9.39.2",
60
60
  "@ladle/react": "^5.1.1",
61
61
  "@tailwindcss/vite": "^4.1.18",
62
- "@types/node": "^24.10.3",
63
- "@types/react": "^19.2.7",
62
+ "@types/node": "^24.10.9",
63
+ "@types/react": "^19.2.9",
64
64
  "@types/react-dom": "^19.2.3",
65
65
  "@vitejs/plugin-react-swc": "^4.2.2",
66
- "eslint": "^9.39.1",
67
- "eslint-plugin-react-hooks": "^5.2.0",
68
- "eslint-plugin-react-refresh": "^0.4.24",
66
+ "baseline-browser-mapping": "^2.9.17",
67
+ "eslint": "^9.39.2",
68
+ "eslint-plugin-react-hooks": "^7.0.1",
69
+ "eslint-plugin-react-refresh": "^0.4.26",
69
70
  "glob": "^13.0.0",
70
- "globals": "^16.5.0",
71
- "typescript": "~5.8.3",
72
- "typescript-eslint": "^8.49.0",
73
- "vite": "^7.2.7",
71
+ "globals": "^17.1.0",
72
+ "typescript": "~5.9.3",
73
+ "typescript-eslint": "^8.53.1",
74
+ "vite": "^7.3.1",
74
75
  "vite-plugin-dts": "^4.5.4"
75
76
  }
76
- }
77
+ }