fluid-primitives 0.17.2 → 0.18.0-next.8.a9be2d6
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/README.md +2 -6
- package/dist/Client-C51NSHxg.js +248 -0
- package/dist/accordion.d.ts +3 -3
- package/dist/accordion.js +45 -1
- package/dist/checkbox-group.d.ts +99 -0
- package/dist/checkbox-group.js +117 -0
- package/dist/checkbox-group.registry-CGwuF7SF.js +112 -0
- package/dist/checkbox.d.ts +26 -3
- package/dist/checkbox.js +148 -1
- package/dist/client.d.ts +2 -2
- package/dist/client.js +3 -1
- package/dist/clipboard.d.ts +3 -3
- package/dist/clipboard.js +32 -1
- package/dist/collapsible.d.ts +3 -3
- package/dist/collapsible.js +28 -1
- package/dist/dialog.d.ts +3 -3
- package/dist/dialog.js +34 -1
- package/dist/field.d.ts +2 -2
- package/dist/field.dom-CJQXpQbZ.js +11 -0
- package/dist/field.js +211 -1
- package/dist/form.d.ts +3 -3
- package/dist/form.js +432 -1
- package/dist/{form.registry-DdLr3dbX.d.ts → form.registry-Cb8Pvoe8.d.ts} +2 -2
- package/dist/form.registry-CmpTny_s.js +51 -0
- package/dist/{index-BpcuNDMI.d.ts → index-B8JCdyld.d.ts} +13 -1
- package/dist/navigation-menu.d.ts +15 -0
- package/dist/navigation-menu.js +71 -0
- package/dist/number-input.d.ts +3 -3
- package/dist/number-input.js +55 -1
- package/dist/popover.d.ts +3 -3
- package/dist/popover.js +42 -1
- package/dist/radio-group.d.ts +3 -3
- package/dist/radio-group.js +67 -1
- package/dist/scroll-area.d.ts +1 -1
- package/dist/scroll-area.js +32 -1
- package/dist/select.d.ts +5 -5
- package/dist/select.js +84 -1
- package/dist/switch.d.ts +16 -0
- package/dist/switch.js +61 -0
- package/dist/tabs.d.ts +3 -3
- package/dist/tabs.js +33 -1
- package/dist/tooltip.d.ts +3 -3
- package/dist/tooltip.js +36 -1
- package/package.json +44 -22
- package/dist/Client-CygooKu8.js +0 -1
- package/dist/field.dom-etI2JxSW.js +0 -1
- package/dist/form.registry-Bv4jZGjo.js +0 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@ Unstyled, flexible and accessible UI Primitives that provide a foundation for bu
|
|
|
6
6
|
|
|
7
7
|
Fluid Primitives brings modern component patterns to TYPO3. Build accessible, composable UI components with the same developer experience you'd expect from React libraries like Radix or Base UI - but for Fluid templates.
|
|
8
8
|
|
|
9
|
+
## Documentation
|
|
10
|
+
|
|
9
11
|
Full documentation can be found at [fluid-primitives.com](https://fluid-primitives.com).
|
|
10
12
|
|
|
11
13
|
## What You Get
|
|
@@ -39,17 +41,11 @@ Fluid Primitives solves this by bringing proven patterns from the modern fronten
|
|
|
39
41
|
|
|
40
42
|
## Acknowledgments
|
|
41
43
|
|
|
42
|
-
Built on the shoulders of giants:
|
|
43
|
-
|
|
44
44
|
- [Zag.js](https://zagjs.com/) - The state machine foundation
|
|
45
45
|
- [Radix UI](https://www.radix-ui.com/primitives) - API design inspiration
|
|
46
46
|
- [Base UI](https://base-ui.com/) - Component behavior patterns
|
|
47
47
|
- [Ark UI](https://ark-ui.com/) - Zag.js integration patterns
|
|
48
48
|
|
|
49
|
-
## Documentation
|
|
50
|
-
|
|
51
|
-
The full documentation can be found at [fluid-primitives.com](https://fluid-primitives.com).
|
|
52
|
-
|
|
53
49
|
## Development
|
|
54
50
|
|
|
55
51
|
See [github.com/jramke/fluid-primitives.com](https://github.com/jramke/fluid-primitives.com) monorepo for the development setup.
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { ListCollection } from "@zag-js/collection";
|
|
2
|
+
import { VanillaMachine, VanillaMachine as Machine, mergeProps, normalizeProps, spreadProps } from "@zag-js/vanilla";
|
|
3
|
+
import { uuid } from "@zag-js/utils";
|
|
4
|
+
|
|
5
|
+
//#region Resources/Private/Client/src/lib/component.ts
|
|
6
|
+
var Component = class {
|
|
7
|
+
document;
|
|
8
|
+
machine;
|
|
9
|
+
api;
|
|
10
|
+
hydrator = null;
|
|
11
|
+
userProps;
|
|
12
|
+
static name;
|
|
13
|
+
get doc() {
|
|
14
|
+
return this.document;
|
|
15
|
+
}
|
|
16
|
+
constructor(props, userDocument = document) {
|
|
17
|
+
this.document = userDocument;
|
|
18
|
+
this.userProps = this.transformProps(props);
|
|
19
|
+
this.hydrator = this.initHydrator(props);
|
|
20
|
+
this.machine = this.initMachine(props);
|
|
21
|
+
this.api = this.initApi();
|
|
22
|
+
}
|
|
23
|
+
initHydrator(props) {
|
|
24
|
+
const id = props.id;
|
|
25
|
+
if (!id) throw new Error("ComponentHydrator requires an id prop to initialize.");
|
|
26
|
+
return new ComponentHydrator(this.getName(), id, props.ids, this.doc);
|
|
27
|
+
}
|
|
28
|
+
init() {
|
|
29
|
+
this.render();
|
|
30
|
+
this.machine.subscribe(() => {
|
|
31
|
+
this.api = this.initApi();
|
|
32
|
+
this.render();
|
|
33
|
+
});
|
|
34
|
+
this.machine.start();
|
|
35
|
+
}
|
|
36
|
+
getName() {
|
|
37
|
+
return this.constructor.name;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Override in consumer for example when a getter is used for collection
|
|
41
|
+
* Needs to be used manually inside the initMachine method
|
|
42
|
+
*/
|
|
43
|
+
transformProps(props) {
|
|
44
|
+
return props;
|
|
45
|
+
}
|
|
46
|
+
updateProps(newProps) {
|
|
47
|
+
this.machine.updateProps(newProps);
|
|
48
|
+
}
|
|
49
|
+
destroy() {
|
|
50
|
+
this.machine.stop();
|
|
51
|
+
this.hydrator?.destroy();
|
|
52
|
+
}
|
|
53
|
+
spreadProps(node, attrs) {
|
|
54
|
+
spreadProps(node, attrs, this.machine.scope.id);
|
|
55
|
+
}
|
|
56
|
+
getElement(part, parent) {
|
|
57
|
+
return this.hydrator?.getElement(part, parent) || null;
|
|
58
|
+
}
|
|
59
|
+
getElements(part, parent) {
|
|
60
|
+
return this.hydrator?.getElements(part, parent) || [];
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region Resources/Private/Primitives/Field/src/field.registry.ts
|
|
66
|
+
const registry = /* @__PURE__ */ new WeakMap();
|
|
67
|
+
function registerFieldMachine(field, service) {
|
|
68
|
+
if (!field) return;
|
|
69
|
+
registry.set(field, service);
|
|
70
|
+
field.dispatchEvent(new CustomEvent("fluid-primitives:field:registered", { bubbles: true }));
|
|
71
|
+
}
|
|
72
|
+
function getFieldMachineFor(el) {
|
|
73
|
+
if (!el) return;
|
|
74
|
+
return registry.get(el);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region Resources/Private/Client/src/lib/field-aware-component.ts
|
|
79
|
+
const fieldProps = [
|
|
80
|
+
"invalid",
|
|
81
|
+
"disabled",
|
|
82
|
+
"readOnly",
|
|
83
|
+
"required"
|
|
84
|
+
];
|
|
85
|
+
const fieldAccessors = {
|
|
86
|
+
disabled: (s) => s.context.get("disabled"),
|
|
87
|
+
readOnly: (s) => s.context.get("readOnly"),
|
|
88
|
+
required: (s) => s.context.get("required"),
|
|
89
|
+
invalid: (s) => s.context.get("invalid")
|
|
90
|
+
};
|
|
91
|
+
var FieldAwareComponent = class extends Component {
|
|
92
|
+
subscribedToField = false;
|
|
93
|
+
fieldMachine;
|
|
94
|
+
closestField = null;
|
|
95
|
+
getClosestField() {
|
|
96
|
+
return this.closestField || this.getElement("root")?.closest("[data-scope=\"field\"][data-part=\"root\"]") || null;
|
|
97
|
+
}
|
|
98
|
+
withFieldProps(props) {
|
|
99
|
+
this.closestField = this.getClosestField();
|
|
100
|
+
if (!this.closestField) return props;
|
|
101
|
+
this.fieldMachine = getFieldMachineFor(this.closestField);
|
|
102
|
+
if (this.fieldMachine) return this.propsWithField(props, this.fieldMachine);
|
|
103
|
+
else {
|
|
104
|
+
const handler = () => {
|
|
105
|
+
this.fieldMachine = getFieldMachineFor(this.closestField);
|
|
106
|
+
this.updateProps(this.propsWithField(this.userProps, this.fieldMachine));
|
|
107
|
+
this.closestField?.removeEventListener("fluid-primitives:field:registered", handler);
|
|
108
|
+
};
|
|
109
|
+
this.closestField.addEventListener("fluid-primitives:field:registered", handler);
|
|
110
|
+
}
|
|
111
|
+
return props;
|
|
112
|
+
}
|
|
113
|
+
subscribeToFieldService() {
|
|
114
|
+
if (this.subscribedToField) return;
|
|
115
|
+
this.closestField = this.getClosestField();
|
|
116
|
+
if (!this.closestField) return;
|
|
117
|
+
if (!this.fieldMachine) this.fieldMachine = getFieldMachineFor(this.closestField);
|
|
118
|
+
if (this.fieldMachine) {
|
|
119
|
+
this.fieldMachine.subscribe((snapshot) => {
|
|
120
|
+
queueMicrotask(() => {
|
|
121
|
+
let propsToUpdate = {};
|
|
122
|
+
for (const prop of fieldProps) {
|
|
123
|
+
const newValue = !!fieldAccessors[prop](snapshot);
|
|
124
|
+
if (newValue !== !!this.machine.prop(prop)) propsToUpdate[prop] = newValue;
|
|
125
|
+
}
|
|
126
|
+
if (Object.keys(propsToUpdate).length > 0) this.updateProps(propsToUpdate);
|
|
127
|
+
else this.machine.notify();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
this.subscribedToField = true;
|
|
131
|
+
} else {
|
|
132
|
+
const handler = () => {
|
|
133
|
+
this.subscribeToFieldService();
|
|
134
|
+
this.closestField.removeEventListener("fluid-primitives:field:registered", handler);
|
|
135
|
+
};
|
|
136
|
+
this.closestField.addEventListener("fluid-primitives:field:registered", handler);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region Resources/Private/Client/src/lib/hydration.ts
|
|
143
|
+
function getHydrationData(component, id) {
|
|
144
|
+
const hydrationData = window.FluidPrimitives?.hydrationData;
|
|
145
|
+
if (!hydrationData || typeof hydrationData !== "object") return null;
|
|
146
|
+
if (!component) return hydrationData;
|
|
147
|
+
if (!hydrationData[component]) return null;
|
|
148
|
+
if (!id) return hydrationData[component];
|
|
149
|
+
return hydrationData[component][id] || null;
|
|
150
|
+
}
|
|
151
|
+
function mount(componentName, callback) {
|
|
152
|
+
const hydrationInstances = getHydrationData(componentName);
|
|
153
|
+
if (!hydrationInstances) return;
|
|
154
|
+
Object.keys(hydrationInstances).forEach((id) => {
|
|
155
|
+
if (hydrationInstances[id].controlled) return;
|
|
156
|
+
const instance = callback({
|
|
157
|
+
...hydrationInstances[id],
|
|
158
|
+
createHydrator: () => new ComponentHydrator(componentName, id, hydrationInstances[id].props.ids)
|
|
159
|
+
});
|
|
160
|
+
if (!instance) return;
|
|
161
|
+
if (!window.FluidPrimitives.uncontrolledInstances[componentName]) window.FluidPrimitives.uncontrolledInstances[componentName] = {};
|
|
162
|
+
window.FluidPrimitives.uncontrolledInstances[componentName][id] = instance;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function mountControlled(componentName, rootId, callback) {
|
|
166
|
+
const hydrationData = getHydrationData(componentName, rootId);
|
|
167
|
+
if (!hydrationData) return;
|
|
168
|
+
return callback({
|
|
169
|
+
...hydrationData,
|
|
170
|
+
createHydrator: () => new ComponentHydrator(componentName, rootId, hydrationData.props.ids)
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
var ComponentHydrator = class {
|
|
174
|
+
componentName;
|
|
175
|
+
doc;
|
|
176
|
+
rootId;
|
|
177
|
+
ids;
|
|
178
|
+
elementRefs = /* @__PURE__ */ new Map();
|
|
179
|
+
constructor(componentName, rootId, ids = {}, doc = document) {
|
|
180
|
+
this.componentName = componentName;
|
|
181
|
+
this.doc = doc;
|
|
182
|
+
if (!rootId) throw new Error(`Root ID is required for component hydration: ${componentName}`);
|
|
183
|
+
this.rootId = rootId;
|
|
184
|
+
this.ids = ids;
|
|
185
|
+
}
|
|
186
|
+
getElement(part, parent = this.doc) {
|
|
187
|
+
if (this.elementRefs.has(part)) return this.elementRefs.get(part) || null;
|
|
188
|
+
let element = null;
|
|
189
|
+
if (this.ids[part]) element = parent.querySelector(`#${this.ids[part]}`);
|
|
190
|
+
else element = parent.querySelector(`[data-hydrate-${this.componentName}="${this.rootId}"][data-part="${part}"][data-scope="${this.componentName}"]`);
|
|
191
|
+
if (element) {
|
|
192
|
+
if (parent === this.doc) this.elementRefs.set(part, element);
|
|
193
|
+
element.removeAttribute(`data-hydrate-${this.componentName}`);
|
|
194
|
+
element.__rootId = this.rootId;
|
|
195
|
+
}
|
|
196
|
+
return element;
|
|
197
|
+
}
|
|
198
|
+
getElements(part, parent = this.doc) {
|
|
199
|
+
if (this.elementRefs.has(part)) return this.elementRefs.get(part);
|
|
200
|
+
let elements = [];
|
|
201
|
+
if (this.ids[part]) elements = Array.from(parent.querySelectorAll(`#${this.ids[part]}`));
|
|
202
|
+
else elements = Array.from(parent.querySelectorAll(`[data-hydrate-${this.componentName}="${this.rootId}"][data-part="${part}"][data-scope="${this.componentName}"]`));
|
|
203
|
+
if (parent === this.doc) this.elementRefs.set(part, elements);
|
|
204
|
+
elements.forEach((el) => el.removeAttribute(`data-hydrate-${this.componentName}`));
|
|
205
|
+
return elements;
|
|
206
|
+
}
|
|
207
|
+
generateRefAttributesString(part) {
|
|
208
|
+
const id = this.ids[part] || `${this.rootId}-${part}`;
|
|
209
|
+
return `data-scope="${this.componentName}" data-part="${part}" data-hydrate-${this.componentName}="${id}"`;
|
|
210
|
+
}
|
|
211
|
+
setRefAttributes(element, part) {
|
|
212
|
+
this.generateRefAttributesString(part).split(" ").map((attr) => attr.trim()).forEach((attr) => {
|
|
213
|
+
const [key, value] = attr.split("=");
|
|
214
|
+
element.setAttribute(key, value.replace(/"/g, ""));
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
destroy() {
|
|
218
|
+
this.elementRefs.forEach((el) => {
|
|
219
|
+
if (el instanceof Element) el.setAttribute(`data-hydrate-${this.componentName}`, this.rootId);
|
|
220
|
+
else el.forEach((e) => e.setAttribute(`data-hydrate-${this.componentName}`, this.rootId));
|
|
221
|
+
});
|
|
222
|
+
this.elementRefs.clear();
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
function getListCollectionFromHydrationData(hydrationCollection) {
|
|
226
|
+
if (hydrationCollection instanceof ListCollection) return hydrationCollection;
|
|
227
|
+
return new ListCollection({
|
|
228
|
+
items: hydrationCollection.items,
|
|
229
|
+
itemToValue: hydrationCollection.itemToValueKey ? (item) => item?.[hydrationCollection.itemToValueKey] : void 0,
|
|
230
|
+
itemToString: hydrationCollection.itemToStringKey ? (item) => item?.[hydrationCollection.itemToStringKey] : void 0,
|
|
231
|
+
isItemDisabled: hydrationCollection.isItemDisabledKey ? (item) => item?.[hydrationCollection.isItemDisabledKey] : void 0,
|
|
232
|
+
groupBy: hydrationCollection.groupByKey ? (item) => {
|
|
233
|
+
const key = hydrationCollection.groupByKey;
|
|
234
|
+
if (!key) return void 0;
|
|
235
|
+
if (key.includes(".")) return key.split(".").reduce((obj, k) => obj ? obj[k] : void 0, item);
|
|
236
|
+
return item?.[key];
|
|
237
|
+
} : void 0
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region Resources/Private/Client/src/lib/uid.ts
|
|
243
|
+
function uid(prefix = "f") {
|
|
244
|
+
return "«" + prefix + uuid() + "»";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
export { Machine as a, getListCollectionFromHydrationData as c, FieldAwareComponent as d, registerFieldMachine as f, mergeProps as i, mount as l, spreadProps as n, ComponentHydrator as o, Component as p, normalizeProps as r, getHydrationData as s, uid as t, mountControlled as u };
|
package/dist/accordion.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as Machine, f as Component } from "./index-B8JCdyld.js";
|
|
2
2
|
import * as accordion from "@zag-js/accordion";
|
|
3
|
-
import * as
|
|
3
|
+
import * as _$_zag_js_types0 from "@zag-js/types";
|
|
4
4
|
|
|
5
5
|
//#region Resources/Private/Primitives/Accordion/Accordion.d.ts
|
|
6
6
|
declare class Accordion extends Component<accordion.Props, accordion.Api> {
|
|
7
7
|
static name: string;
|
|
8
8
|
initMachine(props: accordion.Props): Machine<any>;
|
|
9
|
-
initApi(): accordion.Api<
|
|
9
|
+
initApi(): accordion.Api<_$_zag_js_types0.PropTypes<{
|
|
10
10
|
[x: string]: any;
|
|
11
11
|
}>>;
|
|
12
12
|
render(): void;
|
package/dist/accordion.js
CHANGED
|
@@ -1 +1,45 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import { a as Machine, p as Component, r as normalizeProps } from "./Client-C51NSHxg.js";
|
|
2
|
+
import * as accordion from "@zag-js/accordion";
|
|
3
|
+
|
|
4
|
+
//#region Resources/Private/Primitives/Accordion/Accordion.ts
|
|
5
|
+
var Accordion = class extends Component {
|
|
6
|
+
static name = "accordion";
|
|
7
|
+
initMachine(props) {
|
|
8
|
+
return new Machine(accordion.machine, { ...props });
|
|
9
|
+
}
|
|
10
|
+
initApi() {
|
|
11
|
+
return accordion.connect(this.machine.service, normalizeProps);
|
|
12
|
+
}
|
|
13
|
+
render() {
|
|
14
|
+
const rootEl = this.getElement("root");
|
|
15
|
+
if (rootEl) this.spreadProps(rootEl, this.api.getRootProps());
|
|
16
|
+
this.getElements("item").forEach((itemEl) => {
|
|
17
|
+
this.spreadProps(itemEl, this.api.getItemProps({
|
|
18
|
+
value: itemEl.getAttribute("data-value"),
|
|
19
|
+
disabled: itemEl.hasAttribute("data-disabled")
|
|
20
|
+
}));
|
|
21
|
+
});
|
|
22
|
+
this.getElements("item-trigger").forEach((trigger) => {
|
|
23
|
+
this.spreadProps(trigger, this.api.getItemTriggerProps({
|
|
24
|
+
value: trigger.getAttribute("data-value"),
|
|
25
|
+
disabled: trigger.hasAttribute("data-disabled")
|
|
26
|
+
}));
|
|
27
|
+
});
|
|
28
|
+
this.getElements("item-content").forEach((contentEl) => {
|
|
29
|
+
this.spreadProps(contentEl, this.api.getItemContentProps({
|
|
30
|
+
value: contentEl.getAttribute("data-value"),
|
|
31
|
+
disabled: contentEl.hasAttribute("data-disabled")
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
34
|
+
this.getElements("item-indicator").forEach((indicatorEl) => {
|
|
35
|
+
this.spreadProps(indicatorEl, this.api.getItemIndicatorProps({
|
|
36
|
+
value: indicatorEl.getAttribute("data-value"),
|
|
37
|
+
disabled: indicatorEl.hasAttribute("data-disabled")
|
|
38
|
+
}));
|
|
39
|
+
});
|
|
40
|
+
this.getElements("item-header");
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
//#endregion
|
|
45
|
+
export { Accordion };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { a as Machine, u as FieldAwareComponent } from "./index-B8JCdyld.js";
|
|
2
|
+
import { t as FieldMachine } from "./form.registry-Cb8Pvoe8.js";
|
|
3
|
+
import { EventObject } from "@zag-js/core";
|
|
4
|
+
import { PropTypes } from "@zag-js/types";
|
|
5
|
+
|
|
6
|
+
//#region Resources/Private/Primitives/CheckboxGroup/src/checkbox-group.types.d.ts
|
|
7
|
+
interface CheckboxGroupProps {
|
|
8
|
+
id: string;
|
|
9
|
+
ids?: Record<string, string>;
|
|
10
|
+
/** The initial value of the checkbox group (uncontrolled) */
|
|
11
|
+
defaultValue?: string[];
|
|
12
|
+
/** The controlled value of the checkbox group */
|
|
13
|
+
value?: string[];
|
|
14
|
+
/** The name of the input fields in the checkbox group (for form submission) */
|
|
15
|
+
name?: string;
|
|
16
|
+
/** The form id the checkbox group belongs to */
|
|
17
|
+
form?: string;
|
|
18
|
+
/** If true, the checkbox group is disabled */
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/** If true, the checkbox group is read-only */
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
/** If true, the checkbox group is required */
|
|
23
|
+
required?: boolean;
|
|
24
|
+
/** If true, the checkbox group is invalid */
|
|
25
|
+
invalid?: boolean;
|
|
26
|
+
/** The maximum number of selected values */
|
|
27
|
+
maxSelectedValues?: number;
|
|
28
|
+
/** Called when the value changes */
|
|
29
|
+
onValueChange?: (details: {
|
|
30
|
+
value: string[];
|
|
31
|
+
}) => void;
|
|
32
|
+
}
|
|
33
|
+
interface CheckboxGroupSchema {
|
|
34
|
+
props: CheckboxGroupProps;
|
|
35
|
+
context: {
|
|
36
|
+
value: string[];
|
|
37
|
+
};
|
|
38
|
+
computed: {
|
|
39
|
+
isAtMax: boolean;
|
|
40
|
+
isInteractive: boolean;
|
|
41
|
+
};
|
|
42
|
+
state: 'ready';
|
|
43
|
+
event: EventObject;
|
|
44
|
+
action: string;
|
|
45
|
+
effect: string;
|
|
46
|
+
}
|
|
47
|
+
interface CheckboxGroupItemProps {
|
|
48
|
+
value: string;
|
|
49
|
+
}
|
|
50
|
+
/** API returned by getItemProps for checkbox items */
|
|
51
|
+
interface CheckboxGroupItemState {
|
|
52
|
+
checked: boolean;
|
|
53
|
+
onCheckedChange: () => void;
|
|
54
|
+
name: string | undefined;
|
|
55
|
+
disabled: boolean;
|
|
56
|
+
readOnly: boolean;
|
|
57
|
+
invalid: boolean;
|
|
58
|
+
}
|
|
59
|
+
/** Public API for CheckboxGroup - used by Checkbox to get item props */
|
|
60
|
+
interface CheckboxGroupApi {
|
|
61
|
+
/** The current value of the checkbox group */
|
|
62
|
+
value: string[];
|
|
63
|
+
/** The name for form submission */
|
|
64
|
+
name: string | undefined;
|
|
65
|
+
/** Whether the checkbox group is disabled */
|
|
66
|
+
disabled: boolean;
|
|
67
|
+
/** Whether the checkbox group is read-only */
|
|
68
|
+
readOnly: boolean;
|
|
69
|
+
/** Whether the checkbox group is invalid */
|
|
70
|
+
invalid: boolean;
|
|
71
|
+
/** Check if a value is selected */
|
|
72
|
+
isChecked(value: string): boolean;
|
|
73
|
+
/** Set the entire value array */
|
|
74
|
+
setValue(value: string[]): void;
|
|
75
|
+
/** Add a value to the selection */
|
|
76
|
+
addValue(value: string): void;
|
|
77
|
+
/** Remove a value from the selection */
|
|
78
|
+
removeValue(value: string): void;
|
|
79
|
+
/** Toggle a value */
|
|
80
|
+
toggleValue(value: string): void;
|
|
81
|
+
/** Get props to merge into a checkbox item */
|
|
82
|
+
getItemProps(props: CheckboxGroupItemProps): CheckboxGroupItemState;
|
|
83
|
+
/** Get root element props */
|
|
84
|
+
getRootProps(): PropTypes['element'];
|
|
85
|
+
/** Get label element props */
|
|
86
|
+
getLabelProps(): PropTypes['element'];
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region Resources/Private/Primitives/CheckboxGroup/CheckboxGroup.d.ts
|
|
90
|
+
declare class CheckboxGroup extends FieldAwareComponent<CheckboxGroupProps, CheckboxGroupApi> {
|
|
91
|
+
static name: string;
|
|
92
|
+
propsWithField(props: CheckboxGroupProps, fieldMachine: FieldMachine): CheckboxGroupProps;
|
|
93
|
+
initMachine(props: CheckboxGroupProps): Machine<CheckboxGroupSchema>;
|
|
94
|
+
initApi(): CheckboxGroupApi;
|
|
95
|
+
render(): void;
|
|
96
|
+
destroy(): void;
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
99
|
+
export { CheckboxGroup };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { a as Machine, d as FieldAwareComponent, i as mergeProps, r as normalizeProps } from "./Client-C51NSHxg.js";
|
|
2
|
+
import { i as connect, n as registerCheckboxGroup, r as unregisterCheckboxGroup } from "./checkbox-group.registry-CGwuF7SF.js";
|
|
3
|
+
import { a as getLabelId } from "./field.dom-CJQXpQbZ.js";
|
|
4
|
+
import { createMachine } from "@zag-js/core";
|
|
5
|
+
|
|
6
|
+
//#region Resources/Private/Primitives/CheckboxGroup/src/checkbox-group.machine.ts
|
|
7
|
+
const machine = createMachine({
|
|
8
|
+
initialState() {
|
|
9
|
+
return "ready";
|
|
10
|
+
},
|
|
11
|
+
context({ bindable, prop }) {
|
|
12
|
+
return { value: bindable(() => ({
|
|
13
|
+
defaultValue: prop("defaultValue") ?? [],
|
|
14
|
+
value: prop("value"),
|
|
15
|
+
onChange(value) {
|
|
16
|
+
prop("onValueChange")?.({ value });
|
|
17
|
+
}
|
|
18
|
+
})) };
|
|
19
|
+
},
|
|
20
|
+
states: { ready: { on: {
|
|
21
|
+
"VALUE.SET": { actions: ["setValue"] },
|
|
22
|
+
"VALUE.ADD": { actions: ["addValue"] },
|
|
23
|
+
"VALUE.REMOVE": { actions: ["removeValue"] },
|
|
24
|
+
"VALUE.TOGGLE": { actions: ["toggleValue"] }
|
|
25
|
+
} } },
|
|
26
|
+
computed: {
|
|
27
|
+
isAtMax({ prop, context }) {
|
|
28
|
+
const max = prop("maxSelectedValues");
|
|
29
|
+
if (max == null) return false;
|
|
30
|
+
return context.get("value").length >= max;
|
|
31
|
+
},
|
|
32
|
+
isInteractive({ prop }) {
|
|
33
|
+
return !prop("disabled") && !prop("readOnly");
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
implementations: { actions: {
|
|
37
|
+
setValue({ context, event }) {
|
|
38
|
+
const newValue = event.value;
|
|
39
|
+
context.set("value", newValue);
|
|
40
|
+
},
|
|
41
|
+
addValue({ context, event, computed }) {
|
|
42
|
+
if (!computed("isInteractive")) return;
|
|
43
|
+
const val = event.value;
|
|
44
|
+
const currentValue = context.get("value");
|
|
45
|
+
if (currentValue.includes(val)) return;
|
|
46
|
+
if (computed("isAtMax")) return;
|
|
47
|
+
const newValue = [...currentValue, val];
|
|
48
|
+
context.set("value", newValue);
|
|
49
|
+
},
|
|
50
|
+
removeValue({ context, event, computed }) {
|
|
51
|
+
if (!computed("isInteractive")) return;
|
|
52
|
+
const val = event.value;
|
|
53
|
+
const newValue = context.get("value").filter((v) => v !== val);
|
|
54
|
+
context.set("value", newValue);
|
|
55
|
+
},
|
|
56
|
+
toggleValue({ context, event, computed }) {
|
|
57
|
+
const val = event.value;
|
|
58
|
+
const currentValue = context.get("value");
|
|
59
|
+
if (currentValue.includes(val)) {
|
|
60
|
+
if (!computed("isInteractive")) return;
|
|
61
|
+
const newValue = currentValue.filter((v) => v !== val);
|
|
62
|
+
context.set("value", newValue);
|
|
63
|
+
} else {
|
|
64
|
+
if (!computed("isInteractive")) return;
|
|
65
|
+
if (computed("isAtMax")) return;
|
|
66
|
+
const newValue = [...currentValue, val];
|
|
67
|
+
context.set("value", newValue);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} }
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
//#region Resources/Private/Primitives/CheckboxGroup/CheckboxGroup.ts
|
|
75
|
+
var CheckboxGroup = class extends FieldAwareComponent {
|
|
76
|
+
static name = "checkbox-group";
|
|
77
|
+
propsWithField(props, fieldMachine) {
|
|
78
|
+
return {
|
|
79
|
+
...props,
|
|
80
|
+
disabled: props.disabled ?? fieldMachine.context.get("disabled"),
|
|
81
|
+
readOnly: props.readOnly ?? fieldMachine.context.get("readOnly"),
|
|
82
|
+
required: props.required ?? fieldMachine.context.get("required"),
|
|
83
|
+
invalid: props.invalid ?? fieldMachine.context.get("invalid"),
|
|
84
|
+
name: props.name ?? fieldMachine.prop("name"),
|
|
85
|
+
ids: {
|
|
86
|
+
...props.ids,
|
|
87
|
+
label: getLabelId(fieldMachine.scope)
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
initMachine(props) {
|
|
92
|
+
props = this.withFieldProps(props);
|
|
93
|
+
const createdMachine = new Machine(machine, props);
|
|
94
|
+
registerCheckboxGroup(this.getElement("root"), createdMachine);
|
|
95
|
+
return createdMachine;
|
|
96
|
+
}
|
|
97
|
+
initApi() {
|
|
98
|
+
return connect(this.machine.service, normalizeProps);
|
|
99
|
+
}
|
|
100
|
+
render() {
|
|
101
|
+
this.subscribeToFieldService();
|
|
102
|
+
const rootEl = this.getElement("root");
|
|
103
|
+
if (rootEl) {
|
|
104
|
+
const mergedProps = mergeProps(this.api.getRootProps(), { "aria-describedby": this.fieldMachine?.context.get("describeIds") || void 0 });
|
|
105
|
+
this.spreadProps(rootEl, mergedProps);
|
|
106
|
+
}
|
|
107
|
+
const labelEl = this.getElement("label");
|
|
108
|
+
if (labelEl) this.spreadProps(labelEl, this.api.getLabelProps());
|
|
109
|
+
}
|
|
110
|
+
destroy() {
|
|
111
|
+
unregisterCheckboxGroup(this.getElement("root"));
|
|
112
|
+
super.destroy();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
//#endregion
|
|
117
|
+
export { CheckboxGroup };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
//#region Resources/Private/Primitives/CheckboxGroup/src/checkbox-group.dom.ts
|
|
2
|
+
const getRootId = (scope) => scope.ids?.root ?? `checkbox-group:${scope.id}:root`;
|
|
3
|
+
const getLabelId = (scope) => scope.ids?.label ?? `checkbox-group:${scope.id}:label`;
|
|
4
|
+
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region Resources/Private/Primitives/CheckboxGroup/src/checkbox-group.connect.ts
|
|
7
|
+
function connect(service, normalize) {
|
|
8
|
+
const { scope, context, computed, send, prop } = service;
|
|
9
|
+
const disabled = !!prop("disabled");
|
|
10
|
+
const readOnly = !!prop("readOnly");
|
|
11
|
+
const invalid = !!prop("invalid");
|
|
12
|
+
const required = !!prop("required");
|
|
13
|
+
const name = prop("name");
|
|
14
|
+
return {
|
|
15
|
+
get value() {
|
|
16
|
+
return context.get("value");
|
|
17
|
+
},
|
|
18
|
+
name,
|
|
19
|
+
disabled,
|
|
20
|
+
readOnly,
|
|
21
|
+
invalid,
|
|
22
|
+
isChecked(val) {
|
|
23
|
+
return context.get("value").includes(val);
|
|
24
|
+
},
|
|
25
|
+
setValue(val) {
|
|
26
|
+
send({
|
|
27
|
+
type: "VALUE.SET",
|
|
28
|
+
value: val
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
addValue(val) {
|
|
32
|
+
send({
|
|
33
|
+
type: "VALUE.ADD",
|
|
34
|
+
value: val
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
removeValue(val) {
|
|
38
|
+
send({
|
|
39
|
+
type: "VALUE.REMOVE",
|
|
40
|
+
value: val
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
toggleValue(val) {
|
|
44
|
+
send({
|
|
45
|
+
type: "VALUE.TOGGLE",
|
|
46
|
+
value: val
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
getItemProps(props) {
|
|
50
|
+
const checked = context.get("value").includes(props.value);
|
|
51
|
+
const isAtMax = computed("isAtMax");
|
|
52
|
+
return {
|
|
53
|
+
checked,
|
|
54
|
+
onCheckedChange: () => send({
|
|
55
|
+
type: "VALUE.TOGGLE",
|
|
56
|
+
value: props.value
|
|
57
|
+
}),
|
|
58
|
+
name,
|
|
59
|
+
disabled: disabled || isAtMax && !checked,
|
|
60
|
+
readOnly,
|
|
61
|
+
invalid
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
getRootProps() {
|
|
65
|
+
return normalize.element({
|
|
66
|
+
id: getRootId(scope),
|
|
67
|
+
role: "group",
|
|
68
|
+
"data-scope": "checkbox-group",
|
|
69
|
+
"data-part": "root",
|
|
70
|
+
"data-disabled": disabled ? "" : void 0,
|
|
71
|
+
"data-readonly": readOnly ? "" : void 0,
|
|
72
|
+
"data-invalid": invalid ? "" : void 0,
|
|
73
|
+
"aria-labelledby": getLabelId(scope),
|
|
74
|
+
"aria-disabled": disabled || void 0,
|
|
75
|
+
"aria-invalid": invalid || void 0,
|
|
76
|
+
"aria-required": required || void 0
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
getLabelProps() {
|
|
80
|
+
return normalize.element({
|
|
81
|
+
id: getLabelId(scope),
|
|
82
|
+
"data-scope": "checkbox-group",
|
|
83
|
+
"data-part": "label",
|
|
84
|
+
"data-disabled": disabled ? "" : void 0,
|
|
85
|
+
"data-readonly": readOnly ? "" : void 0,
|
|
86
|
+
"data-invalid": invalid ? "" : void 0
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
//#endregion
|
|
93
|
+
//#region Resources/Private/Primitives/CheckboxGroup/src/checkbox-group.registry.ts
|
|
94
|
+
const registry = /* @__PURE__ */ new WeakMap();
|
|
95
|
+
function registerCheckboxGroup(root, machine) {
|
|
96
|
+
if (!root) return;
|
|
97
|
+
registry.set(root, machine);
|
|
98
|
+
root.dispatchEvent(new CustomEvent("fluid-primitives:checkbox-group:registered", { bubbles: true }));
|
|
99
|
+
}
|
|
100
|
+
function unregisterCheckboxGroup(root) {
|
|
101
|
+
if (!root) return;
|
|
102
|
+
registry.delete(root);
|
|
103
|
+
}
|
|
104
|
+
function getCheckboxGroupMachineFor(el) {
|
|
105
|
+
if (!el) return void 0;
|
|
106
|
+
const root = el.closest("[data-scope=\"checkbox-group\"][data-part=\"root\"]");
|
|
107
|
+
if (!root) return void 0;
|
|
108
|
+
return registry.get(root);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
export { connect as i, registerCheckboxGroup as n, unregisterCheckboxGroup as r, getCheckboxGroupMachineFor as t };
|