@ariakit/components 0.1.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.
- package/CHANGELOG.md +19 -0
- package/dist/checkbox/checkbox-store.d.ts +47 -0
- package/dist/checkbox/checkbox-store.d.ts.map +1 -0
- package/dist/checkbox/checkbox-store.js +16 -0
- package/dist/checkbox/checkbox-store.js.map +1 -0
- package/dist/collection/collection-store.d.ts +2 -0
- package/dist/collection/collection-store.js +132 -0
- package/dist/collection/collection-store.js.map +1 -0
- package/dist/collection-store-yNe83BiS.d.ts +81 -0
- package/dist/collection-store-yNe83BiS.d.ts.map +1 -0
- package/dist/combobox/combobox-store.d.ts +150 -0
- package/dist/combobox/combobox-store.d.ts.map +1 -0
- package/dist/combobox/combobox-store.js +83 -0
- package/dist/combobox/combobox-store.js.map +1 -0
- package/dist/composite/composite-overflow-store.d.ts +16 -0
- package/dist/composite/composite-overflow-store.d.ts.map +1 -0
- package/dist/composite/composite-overflow-store.js +12 -0
- package/dist/composite/composite-overflow-store.js.map +1 -0
- package/dist/composite/composite-store.d.ts +2 -0
- package/dist/composite/composite-store.js +167 -0
- package/dist/composite/composite-store.js.map +1 -0
- package/dist/composite-store-B-iDEtZZ.d.ts +331 -0
- package/dist/composite-store-B-iDEtZZ.d.ts.map +1 -0
- package/dist/dialog/dialog-store.d.ts +2 -0
- package/dist/dialog/dialog-store.js +12 -0
- package/dist/dialog/dialog-store.js.map +1 -0
- package/dist/dialog-store-BOLvw2IX.d.ts +16 -0
- package/dist/dialog-store-BOLvw2IX.d.ts.map +1 -0
- package/dist/disclosure/disclosure-store.d.ts +2 -0
- package/dist/disclosure/disclosure-store.js +47 -0
- package/dist/disclosure/disclosure-store.js.map +1 -0
- package/dist/disclosure-store-xKlQffR0.d.ts +142 -0
- package/dist/disclosure-store-xKlQffR0.d.ts.map +1 -0
- package/dist/form/form-store.d.ts +247 -0
- package/dist/form/form-store.d.ts.map +1 -0
- package/dist/form/form-store.js +211 -0
- package/dist/form/form-store.js.map +1 -0
- package/dist/form/types.d.ts +37 -0
- package/dist/form/types.d.ts.map +1 -0
- package/dist/form/types.js +0 -0
- package/dist/hovercard/hovercard-store.d.ts +65 -0
- package/dist/hovercard/hovercard-store.d.ts.map +1 -0
- package/dist/hovercard/hovercard-store.js +31 -0
- package/dist/hovercard/hovercard-store.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/menu/menu-bar-store.d.ts +16 -0
- package/dist/menu/menu-bar-store.d.ts.map +1 -0
- package/dist/menu/menu-bar-store.js +12 -0
- package/dist/menu/menu-bar-store.js.map +1 -0
- package/dist/menu/menu-store.d.ts +100 -0
- package/dist/menu/menu-store.d.ts.map +1 -0
- package/dist/menu/menu-store.js +74 -0
- package/dist/menu/menu-store.js.map +1 -0
- package/dist/menubar/menubar-store.d.ts +2 -0
- package/dist/menubar/menubar-store.js +24 -0
- package/dist/menubar/menubar-store.js.map +1 -0
- package/dist/menubar-store-CD3YDYfW.d.ts +16 -0
- package/dist/menubar-store-CD3YDYfW.d.ts.map +1 -0
- package/dist/popover/popover-store.d.ts +2 -0
- package/dist/popover/popover-store.js +44 -0
- package/dist/popover/popover-store.js.map +1 -0
- package/dist/popover-store-DoCiTmUQ.d.ts +106 -0
- package/dist/popover-store-DoCiTmUQ.d.ts.map +1 -0
- package/dist/radio/radio-store.d.ts +42 -0
- package/dist/radio/radio-store.d.ts.map +1 -0
- package/dist/radio/radio-store.js +27 -0
- package/dist/radio/radio-store.js.map +1 -0
- package/dist/select/select-store.d.ts +116 -0
- package/dist/select/select-store.d.ts.map +1 -0
- package/dist/select/select-store.js +93 -0
- package/dist/select/select-store.js.map +1 -0
- package/dist/tab/tab-store.d.ts +127 -0
- package/dist/tab/tab-store.d.ts.map +1 -0
- package/dist/tab/tab-store.js +107 -0
- package/dist/tab/tab-store.js.map +1 -0
- package/dist/tag/tag-store.d.ts +2 -0
- package/dist/tag/tag-store.js +60 -0
- package/dist/tag/tag-store.js.map +1 -0
- package/dist/tag-store-D47X5_zA.d.ts +83 -0
- package/dist/tag-store-D47X5_zA.d.ts.map +1 -0
- package/dist/toolbar/toolbar-store.d.ts +21 -0
- package/dist/toolbar/toolbar-store.d.ts.map +1 -0
- package/dist/toolbar/toolbar-store.js +18 -0
- package/dist/toolbar/toolbar-store.js.map +1 -0
- package/dist/tooltip/tooltip-store.d.ts +35 -0
- package/dist/tooltip/tooltip-store.d.ts.map +1 -0
- package/dist/tooltip/tooltip-store.js +29 -0
- package/dist/tooltip/tooltip-store.js.map +1 -0
- package/license +21 -0
- package/package.json +121 -0
- package/readme.md +19 -0
- package/src/checkbox/checkbox-store.ts +93 -0
- package/src/collection/collection-store.ts +301 -0
- package/src/combobox/combobox-store.ts +382 -0
- package/src/composite/composite-overflow-store.ts +30 -0
- package/src/composite/composite-store.ts +711 -0
- package/src/dialog/dialog-store.ts +26 -0
- package/src/disclosure/disclosure-store.ts +226 -0
- package/src/form/form-store.ts +608 -0
- package/src/form/types.ts +44 -0
- package/src/hovercard/hovercard-store.ts +112 -0
- package/src/index.ts +1 -0
- package/src/menu/menu-bar-store.ts +28 -0
- package/src/menu/menu-store.ts +263 -0
- package/src/menubar/menubar-store.ts +51 -0
- package/src/popover/popover-store.ts +170 -0
- package/src/radio/radio-store.ts +80 -0
- package/src/select/select-store.ts +323 -0
- package/src/tab/tab-store.ts +330 -0
- package/src/tag/tag-store.ts +170 -0
- package/src/toolbar/toolbar-store.ts +47 -0
- package/src/tooltip/tooltip-store.ts +93 -0
package/package.json
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ariakit/components",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ariakit components",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ariakit",
|
|
7
|
+
"components"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://ariakit.com",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Diego Haz",
|
|
13
|
+
"email": "hazdiego@gmail.com",
|
|
14
|
+
"url": "https://github.com/diegohaz"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/ariakit/ariakit.git",
|
|
19
|
+
"directory": "packages/ariakit-components"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./checkbox/checkbox-store": {
|
|
29
|
+
"types": "./dist/checkbox/checkbox-store.d.ts",
|
|
30
|
+
"import": "./dist/checkbox/checkbox-store.js"
|
|
31
|
+
},
|
|
32
|
+
"./collection/collection-store": {
|
|
33
|
+
"types": "./dist/collection/collection-store.d.ts",
|
|
34
|
+
"import": "./dist/collection/collection-store.js"
|
|
35
|
+
},
|
|
36
|
+
"./combobox/combobox-store": {
|
|
37
|
+
"types": "./dist/combobox/combobox-store.d.ts",
|
|
38
|
+
"import": "./dist/combobox/combobox-store.js"
|
|
39
|
+
},
|
|
40
|
+
"./composite/composite-overflow-store": {
|
|
41
|
+
"types": "./dist/composite/composite-overflow-store.d.ts",
|
|
42
|
+
"import": "./dist/composite/composite-overflow-store.js"
|
|
43
|
+
},
|
|
44
|
+
"./composite/composite-store": {
|
|
45
|
+
"types": "./dist/composite/composite-store.d.ts",
|
|
46
|
+
"import": "./dist/composite/composite-store.js"
|
|
47
|
+
},
|
|
48
|
+
"./dialog/dialog-store": {
|
|
49
|
+
"types": "./dist/dialog/dialog-store.d.ts",
|
|
50
|
+
"import": "./dist/dialog/dialog-store.js"
|
|
51
|
+
},
|
|
52
|
+
"./disclosure/disclosure-store": {
|
|
53
|
+
"types": "./dist/disclosure/disclosure-store.d.ts",
|
|
54
|
+
"import": "./dist/disclosure/disclosure-store.js"
|
|
55
|
+
},
|
|
56
|
+
"./form/form-store": {
|
|
57
|
+
"types": "./dist/form/form-store.d.ts",
|
|
58
|
+
"import": "./dist/form/form-store.js"
|
|
59
|
+
},
|
|
60
|
+
"./form/types": {
|
|
61
|
+
"types": "./dist/form/types.d.ts",
|
|
62
|
+
"import": "./dist/form/types.js"
|
|
63
|
+
},
|
|
64
|
+
"./hovercard/hovercard-store": {
|
|
65
|
+
"types": "./dist/hovercard/hovercard-store.d.ts",
|
|
66
|
+
"import": "./dist/hovercard/hovercard-store.js"
|
|
67
|
+
},
|
|
68
|
+
"./menu/menu-bar-store": {
|
|
69
|
+
"types": "./dist/menu/menu-bar-store.d.ts",
|
|
70
|
+
"import": "./dist/menu/menu-bar-store.js"
|
|
71
|
+
},
|
|
72
|
+
"./menu/menu-store": {
|
|
73
|
+
"types": "./dist/menu/menu-store.d.ts",
|
|
74
|
+
"import": "./dist/menu/menu-store.js"
|
|
75
|
+
},
|
|
76
|
+
"./menubar/menubar-store": {
|
|
77
|
+
"types": "./dist/menubar/menubar-store.d.ts",
|
|
78
|
+
"import": "./dist/menubar/menubar-store.js"
|
|
79
|
+
},
|
|
80
|
+
"./popover/popover-store": {
|
|
81
|
+
"types": "./dist/popover/popover-store.d.ts",
|
|
82
|
+
"import": "./dist/popover/popover-store.js"
|
|
83
|
+
},
|
|
84
|
+
"./radio/radio-store": {
|
|
85
|
+
"types": "./dist/radio/radio-store.d.ts",
|
|
86
|
+
"import": "./dist/radio/radio-store.js"
|
|
87
|
+
},
|
|
88
|
+
"./select/select-store": {
|
|
89
|
+
"types": "./dist/select/select-store.d.ts",
|
|
90
|
+
"import": "./dist/select/select-store.js"
|
|
91
|
+
},
|
|
92
|
+
"./tab/tab-store": {
|
|
93
|
+
"types": "./dist/tab/tab-store.d.ts",
|
|
94
|
+
"import": "./dist/tab/tab-store.js"
|
|
95
|
+
},
|
|
96
|
+
"./tag/tag-store": {
|
|
97
|
+
"types": "./dist/tag/tag-store.d.ts",
|
|
98
|
+
"import": "./dist/tag/tag-store.js"
|
|
99
|
+
},
|
|
100
|
+
"./toolbar/toolbar-store": {
|
|
101
|
+
"types": "./dist/toolbar/toolbar-store.d.ts",
|
|
102
|
+
"import": "./dist/toolbar/toolbar-store.js"
|
|
103
|
+
},
|
|
104
|
+
"./tooltip/tooltip-store": {
|
|
105
|
+
"types": "./dist/tooltip/tooltip-store.d.ts",
|
|
106
|
+
"import": "./dist/tooltip/tooltip-store.js"
|
|
107
|
+
},
|
|
108
|
+
"./package.json": "./package.json"
|
|
109
|
+
},
|
|
110
|
+
"dependencies": {
|
|
111
|
+
"@ariakit/utils": "0.1.0",
|
|
112
|
+
"@ariakit/store": "0.1.0"
|
|
113
|
+
},
|
|
114
|
+
"devDependencies": {
|
|
115
|
+
"@ariakit/scripts": "0.0.0"
|
|
116
|
+
},
|
|
117
|
+
"scripts": {
|
|
118
|
+
"build": "ariakit build",
|
|
119
|
+
"clean": "ariakit clean"
|
|
120
|
+
}
|
|
121
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Ariakit Components
|
|
2
|
+
|
|
3
|
+
**Important:** This package is an internal dependency of Ariakit and does not follow semantic versioning, meaning breaking changes may occur in patch and minor versions. You probably want to use [`@ariakit/react`](https://npmjs.org/package/@ariakit/react) instead.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
npm i @ariakit/components
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Core Team
|
|
12
|
+
|
|
13
|
+
- [Diego Haz](https://bsky.app/profile/haz.dev)
|
|
14
|
+
- [Ben Rodri](https://bsky.app/profile/ben.ariakit.org)
|
|
15
|
+
- [Dani Guardiola](https://bsky.app/profile/dio.la)
|
|
16
|
+
|
|
17
|
+
## Contributing
|
|
18
|
+
|
|
19
|
+
Follow the instructions on the [contributing guide](https://github.com/ariakit/ariakit/blob/main/contributing.md).
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createStore, throwOnConflictingProps } from "@ariakit/store";
|
|
2
|
+
import type { Store, StoreOptions, StoreProps } from "@ariakit/store";
|
|
3
|
+
import { defaultValue } from "@ariakit/utils";
|
|
4
|
+
import type { PickRequired, SetState, ToPrimitive } from "@ariakit/utils";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a checkbox store.
|
|
8
|
+
*/
|
|
9
|
+
export function createCheckboxStore<
|
|
10
|
+
T extends CheckboxStoreValue = CheckboxStoreValue,
|
|
11
|
+
>(
|
|
12
|
+
props: PickRequired<CheckboxStoreProps<T>, "value" | "defaultValue">,
|
|
13
|
+
): CheckboxStore<T>;
|
|
14
|
+
|
|
15
|
+
export function createCheckboxStore(props?: CheckboxStoreProps): CheckboxStore;
|
|
16
|
+
|
|
17
|
+
export function createCheckboxStore(
|
|
18
|
+
props: CheckboxStoreProps = {},
|
|
19
|
+
): CheckboxStore {
|
|
20
|
+
throwOnConflictingProps(props, props.store);
|
|
21
|
+
|
|
22
|
+
const syncState = props.store?.getState();
|
|
23
|
+
const initialState: CheckboxStoreState = {
|
|
24
|
+
value: defaultValue(
|
|
25
|
+
props.value,
|
|
26
|
+
syncState?.value,
|
|
27
|
+
props.defaultValue,
|
|
28
|
+
false,
|
|
29
|
+
),
|
|
30
|
+
};
|
|
31
|
+
const checkbox = createStore(initialState, props.store);
|
|
32
|
+
return {
|
|
33
|
+
...checkbox,
|
|
34
|
+
setValue: (value) => checkbox.setState("value", value),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type CheckboxStoreValue =
|
|
39
|
+
| boolean
|
|
40
|
+
| string
|
|
41
|
+
| number
|
|
42
|
+
| ReadonlyArray<string | number>;
|
|
43
|
+
|
|
44
|
+
export interface CheckboxStoreState<
|
|
45
|
+
T extends CheckboxStoreValue = CheckboxStoreValue,
|
|
46
|
+
> {
|
|
47
|
+
/**
|
|
48
|
+
* The checked state of the checkbox.
|
|
49
|
+
*
|
|
50
|
+
* Live examples:
|
|
51
|
+
* - [Custom Checkbox](https://ariakit.com/examples/checkbox-custom)
|
|
52
|
+
*/
|
|
53
|
+
value: ToPrimitive<T>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface CheckboxStoreFunctions<
|
|
57
|
+
T extends CheckboxStoreValue = CheckboxStoreValue,
|
|
58
|
+
> {
|
|
59
|
+
/**
|
|
60
|
+
* Sets the [`value`](https://ariakit.com/reference/checkbox-provider#value)
|
|
61
|
+
* state.
|
|
62
|
+
* @example
|
|
63
|
+
* store.setValue(true);
|
|
64
|
+
* store.setValue((value) => !value);
|
|
65
|
+
*/
|
|
66
|
+
setValue: SetState<CheckboxStoreState<T>["value"]>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CheckboxStoreOptions<
|
|
70
|
+
T extends CheckboxStoreValue = CheckboxStoreValue,
|
|
71
|
+
> extends StoreOptions<CheckboxStoreState<T>, "value"> {
|
|
72
|
+
/**
|
|
73
|
+
* The default
|
|
74
|
+
* [`value`](https://ariakit.com/reference/checkbox-provider#value) state of
|
|
75
|
+
* the checkbox.
|
|
76
|
+
*
|
|
77
|
+
* Live examples:
|
|
78
|
+
* - [Custom Checkbox](https://ariakit.com/examples/checkbox-custom)
|
|
79
|
+
* - [Checkbox group](https://ariakit.com/examples/checkbox-group)
|
|
80
|
+
* @default false
|
|
81
|
+
*/
|
|
82
|
+
defaultValue?: CheckboxStoreState<T>["value"];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface CheckboxStoreProps<
|
|
86
|
+
T extends CheckboxStoreValue = CheckboxStoreValue,
|
|
87
|
+
>
|
|
88
|
+
extends CheckboxStoreOptions<T>, StoreProps<CheckboxStoreState<T>> {}
|
|
89
|
+
|
|
90
|
+
export interface CheckboxStore<
|
|
91
|
+
T extends CheckboxStoreValue = CheckboxStoreValue,
|
|
92
|
+
>
|
|
93
|
+
extends CheckboxStoreFunctions<T>, Store<CheckboxStoreState<T>> {}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batch,
|
|
3
|
+
createStore,
|
|
4
|
+
init,
|
|
5
|
+
setup,
|
|
6
|
+
throwOnConflictingProps,
|
|
7
|
+
} from "@ariakit/store";
|
|
8
|
+
import type { Store, StoreOptions, StoreProps } from "@ariakit/store";
|
|
9
|
+
import {
|
|
10
|
+
getDocument,
|
|
11
|
+
sortBasedOnDOMPosition,
|
|
12
|
+
chain,
|
|
13
|
+
defaultValue,
|
|
14
|
+
} from "@ariakit/utils";
|
|
15
|
+
import type { BivariantCallback } from "@ariakit/utils";
|
|
16
|
+
|
|
17
|
+
function getCommonParent(items: CollectionStoreItem[]) {
|
|
18
|
+
const firstItem = items.find((item) => !!item.element);
|
|
19
|
+
const lastElement = [...items]
|
|
20
|
+
.reverse()
|
|
21
|
+
.find((item) => !!item.element)?.element;
|
|
22
|
+
let parentElement = firstItem?.element?.parentElement;
|
|
23
|
+
if (!lastElement) {
|
|
24
|
+
return getDocument(parentElement).body;
|
|
25
|
+
}
|
|
26
|
+
while (parentElement) {
|
|
27
|
+
const parent = parentElement;
|
|
28
|
+
if (parent.contains(lastElement)) {
|
|
29
|
+
return parentElement;
|
|
30
|
+
}
|
|
31
|
+
parentElement = parentElement.parentElement;
|
|
32
|
+
}
|
|
33
|
+
return getDocument(parentElement).body;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function getPrivateStore<T extends CollectionStoreItem>(
|
|
37
|
+
store?: Store & {
|
|
38
|
+
__unstablePrivateStore?: Store<{
|
|
39
|
+
renderedItems: T[];
|
|
40
|
+
}>;
|
|
41
|
+
},
|
|
42
|
+
) {
|
|
43
|
+
return store?.__unstablePrivateStore;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a collection store.
|
|
48
|
+
*/
|
|
49
|
+
export function createCollectionStore<
|
|
50
|
+
T extends CollectionStoreItem = CollectionStoreItem,
|
|
51
|
+
>(props: CollectionStoreProps<T> = {}): CollectionStore<T> {
|
|
52
|
+
throwOnConflictingProps(props, props.store);
|
|
53
|
+
|
|
54
|
+
const syncState = props.store?.getState();
|
|
55
|
+
|
|
56
|
+
const items = defaultValue(
|
|
57
|
+
props.items,
|
|
58
|
+
syncState?.items,
|
|
59
|
+
props.defaultItems,
|
|
60
|
+
[],
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const itemsMap = new Map<string, T>(items.map((item) => [item.id, item]));
|
|
64
|
+
|
|
65
|
+
const initialState: CollectionStoreState<T> = {
|
|
66
|
+
items,
|
|
67
|
+
renderedItems: defaultValue(syncState?.renderedItems, []),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const syncPrivateStore = getPrivateStore<T>(props.store);
|
|
71
|
+
|
|
72
|
+
const privateStore = createStore(
|
|
73
|
+
{ items, renderedItems: initialState.renderedItems },
|
|
74
|
+
syncPrivateStore,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const collection = createStore(initialState, props.store);
|
|
78
|
+
|
|
79
|
+
const sortItems = (renderedItems: T[]) => {
|
|
80
|
+
const sortedItems = sortBasedOnDOMPosition(renderedItems, (i) => i.element);
|
|
81
|
+
privateStore.setState("renderedItems", sortedItems);
|
|
82
|
+
collection.setState("renderedItems", sortedItems);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
setup(collection, () => init(privateStore));
|
|
86
|
+
|
|
87
|
+
// Use the private store to register items and then batch the changes to the
|
|
88
|
+
// public store so we don't trigger multiple updates on the store when adding
|
|
89
|
+
// multiple items.
|
|
90
|
+
setup(privateStore, () => {
|
|
91
|
+
return batch(privateStore, ["items"], (state) => {
|
|
92
|
+
collection.setState("items", state.items);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
setup(privateStore, () => {
|
|
97
|
+
return batch(privateStore, ["renderedItems"], (state) => {
|
|
98
|
+
let firstRun = true;
|
|
99
|
+
|
|
100
|
+
let raf = requestAnimationFrame(() => {
|
|
101
|
+
const { renderedItems } = collection.getState();
|
|
102
|
+
// Bail out if the rendered items haven't changed. This is important
|
|
103
|
+
// because the following lines can cause this function to be called
|
|
104
|
+
// again.
|
|
105
|
+
if (state.renderedItems === renderedItems) return;
|
|
106
|
+
sortItems(state.renderedItems);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (typeof IntersectionObserver !== "function") {
|
|
110
|
+
return () => cancelAnimationFrame(raf);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const ioCallback: IntersectionObserverCallback = () => {
|
|
114
|
+
if (firstRun) {
|
|
115
|
+
// The IntersectionObserver callback is called synchronously the first
|
|
116
|
+
// time. We just ignore it.
|
|
117
|
+
firstRun = false;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
cancelAnimationFrame(raf);
|
|
121
|
+
raf = requestAnimationFrame(() => sortItems(state.renderedItems));
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const root = getCommonParent(state.renderedItems);
|
|
125
|
+
const observer = new IntersectionObserver(ioCallback, { root });
|
|
126
|
+
|
|
127
|
+
for (const item of state.renderedItems) {
|
|
128
|
+
if (!item.element) continue;
|
|
129
|
+
observer.observe(item.element);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return () => {
|
|
133
|
+
cancelAnimationFrame(raf);
|
|
134
|
+
observer.disconnect();
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const mergeItem = (
|
|
140
|
+
item: T,
|
|
141
|
+
setItems: (getItems: (items: T[]) => T[]) => void,
|
|
142
|
+
canDeleteFromMap = false,
|
|
143
|
+
) => {
|
|
144
|
+
let prevItem: T | undefined;
|
|
145
|
+
setItems((items) => {
|
|
146
|
+
const index = items.findIndex(({ id }) => id === item.id);
|
|
147
|
+
const nextItems = items.slice();
|
|
148
|
+
if (index !== -1) {
|
|
149
|
+
prevItem = items[index];
|
|
150
|
+
const nextItem = { ...prevItem, ...item };
|
|
151
|
+
nextItems[index] = nextItem;
|
|
152
|
+
itemsMap.set(item.id, nextItem);
|
|
153
|
+
} else {
|
|
154
|
+
nextItems.push(item);
|
|
155
|
+
itemsMap.set(item.id, item);
|
|
156
|
+
}
|
|
157
|
+
return nextItems;
|
|
158
|
+
});
|
|
159
|
+
const unmergeItem = () => {
|
|
160
|
+
setItems((items) => {
|
|
161
|
+
if (!prevItem) {
|
|
162
|
+
if (canDeleteFromMap) {
|
|
163
|
+
itemsMap.delete(item.id);
|
|
164
|
+
}
|
|
165
|
+
return items.filter(({ id }) => id !== item.id);
|
|
166
|
+
}
|
|
167
|
+
const index = items.findIndex(({ id }) => id === item.id);
|
|
168
|
+
if (index === -1) return items;
|
|
169
|
+
const nextItems = items.slice();
|
|
170
|
+
nextItems[index] = prevItem;
|
|
171
|
+
itemsMap.set(item.id, prevItem);
|
|
172
|
+
return nextItems;
|
|
173
|
+
});
|
|
174
|
+
};
|
|
175
|
+
return unmergeItem;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const registerItem: CollectionStore<T>["registerItem"] = (item) =>
|
|
179
|
+
mergeItem(
|
|
180
|
+
item,
|
|
181
|
+
(getItems) => privateStore.setState("items", getItems),
|
|
182
|
+
true,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...collection,
|
|
187
|
+
|
|
188
|
+
registerItem,
|
|
189
|
+
renderItem: (item) =>
|
|
190
|
+
chain(
|
|
191
|
+
registerItem(item),
|
|
192
|
+
mergeItem(item, (getItems) =>
|
|
193
|
+
privateStore.setState("renderedItems", getItems),
|
|
194
|
+
),
|
|
195
|
+
),
|
|
196
|
+
|
|
197
|
+
item: (id) => {
|
|
198
|
+
if (!id) return null;
|
|
199
|
+
let item = itemsMap.get(id);
|
|
200
|
+
if (!item) {
|
|
201
|
+
const { items } = privateStore.getState();
|
|
202
|
+
item = items.find((item) => item.id === id);
|
|
203
|
+
if (item) {
|
|
204
|
+
itemsMap.set(id, item);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return item || null;
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
// @ts-expect-error Internal
|
|
211
|
+
__unstablePrivateStore: privateStore,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface CollectionStoreItem {
|
|
216
|
+
/**
|
|
217
|
+
* The id of the item.
|
|
218
|
+
*/
|
|
219
|
+
id: string;
|
|
220
|
+
/**
|
|
221
|
+
* The item HTML element. This is automatically set when the item is rendered.
|
|
222
|
+
*/
|
|
223
|
+
element?: HTMLElement | null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export interface CollectionStoreState<
|
|
227
|
+
T extends CollectionStoreItem = CollectionStoreItem,
|
|
228
|
+
> {
|
|
229
|
+
/**
|
|
230
|
+
* Lists all items along with their metadata. This state is automatically
|
|
231
|
+
* updated when an item is registered or unregistered using the
|
|
232
|
+
* [`registerItem`](https://ariakit.com/reference/use-collection-store#registeritem)
|
|
233
|
+
* function.
|
|
234
|
+
*
|
|
235
|
+
* Live examples:
|
|
236
|
+
* - [Command Menu with
|
|
237
|
+
* Tabs](https://ariakit.com/examples/dialog-combobox-tab-command-menu)
|
|
238
|
+
*/
|
|
239
|
+
items: T[];
|
|
240
|
+
/**
|
|
241
|
+
* Lists all items, along with their metadata, in the exact order they appear in
|
|
242
|
+
* the DOM. This state is automatically updated when an item is rendered or
|
|
243
|
+
* unmounted using the
|
|
244
|
+
* [`renderItem`](https://ariakit.com/reference/use-collection-store#renderitem)
|
|
245
|
+
* function.
|
|
246
|
+
*/
|
|
247
|
+
renderedItems: T[];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface CollectionStoreFunctions<
|
|
251
|
+
T extends CollectionStoreItem = CollectionStoreItem,
|
|
252
|
+
> {
|
|
253
|
+
/**
|
|
254
|
+
* Registers an item in the collection. This function returns a cleanup
|
|
255
|
+
* function that unregisters the item.
|
|
256
|
+
* @example
|
|
257
|
+
* const unregisterItem = store.registerItem({ id: "item-1" });
|
|
258
|
+
* // on cleanup
|
|
259
|
+
* unregisterItem();
|
|
260
|
+
*/
|
|
261
|
+
registerItem: BivariantCallback<(item: T) => () => void>;
|
|
262
|
+
/**
|
|
263
|
+
* Renders an item in the collection. This function returns a cleanup function
|
|
264
|
+
* that unmounts the item.
|
|
265
|
+
* @example
|
|
266
|
+
* const unrenderItem = store.renderItem({ id: "item-1" });
|
|
267
|
+
* // on cleanup
|
|
268
|
+
* unrenderItem();
|
|
269
|
+
*/
|
|
270
|
+
renderItem: BivariantCallback<(item: T) => () => void>;
|
|
271
|
+
/**
|
|
272
|
+
* Gets an item by its id.
|
|
273
|
+
*
|
|
274
|
+
* Live examples:
|
|
275
|
+
* - [Animated TabPanel](https://ariakit.com/examples/tab-panel-animated)
|
|
276
|
+
* @example
|
|
277
|
+
* const item = store.item("item-1");
|
|
278
|
+
*/
|
|
279
|
+
item: (id: string | null | undefined) => T | null;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export interface CollectionStoreOptions<
|
|
283
|
+
T extends CollectionStoreItem = CollectionStoreItem,
|
|
284
|
+
> extends StoreOptions<CollectionStoreState<T>, "items"> {
|
|
285
|
+
/**
|
|
286
|
+
* The defaut value for the
|
|
287
|
+
* [`items`](https://ariakit.com/reference/collection-provider#items) state.
|
|
288
|
+
* @default []
|
|
289
|
+
*/
|
|
290
|
+
defaultItems?: CollectionStoreState<T>["items"];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface CollectionStoreProps<
|
|
294
|
+
T extends CollectionStoreItem = CollectionStoreItem,
|
|
295
|
+
>
|
|
296
|
+
extends CollectionStoreOptions<T>, StoreProps<CollectionStoreState<T>> {}
|
|
297
|
+
|
|
298
|
+
export interface CollectionStore<
|
|
299
|
+
T extends CollectionStoreItem = CollectionStoreItem,
|
|
300
|
+
>
|
|
301
|
+
extends CollectionStoreFunctions<T>, Store<CollectionStoreState<T>> {}
|