@dxos/react-ui-attention 0.8.4-main.b97322e → 0.8.4-main.bc674ce
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/lib/browser/chunk-HJRZWOEX.mjs +705 -0
- package/dist/lib/browser/chunk-HJRZWOEX.mjs.map +7 -0
- package/dist/lib/browser/chunk-PAZBZOO2.mjs +7 -0
- package/dist/lib/browser/chunk-PAZBZOO2.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +75 -8
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +10 -4
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types.mjs +7 -0
- package/dist/lib/browser/types.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-7TQWDE4M.mjs +706 -0
- package/dist/lib/node-esm/chunk-7TQWDE4M.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-PMGNPL4M.mjs +9 -0
- package/dist/lib/node-esm/chunk-PMGNPL4M.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +75 -8
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +10 -4
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types.mjs +8 -0
- package/dist/lib/node-esm/types.mjs.map +7 -0
- package/dist/types/src/attention.d.ts +22 -17
- package/dist/types/src/attention.d.ts.map +1 -1
- package/dist/types/src/components/AttentionGlyph.d.ts.map +1 -1
- package/dist/types/src/components/AttentionGlyph.stories.d.ts +9 -20
- package/dist/types/src/components/AttentionGlyph.stories.d.ts.map +1 -1
- package/dist/types/src/components/AttentionProvider.d.ts +7 -6
- package/dist/types/src/components/AttentionProvider.d.ts.map +1 -1
- package/dist/types/src/components/AttentionProvider.stories.d.ts +6 -4
- package/dist/types/src/components/AttentionProvider.stories.d.ts.map +1 -1
- package/dist/types/src/components/SelectionProvider.d.ts +6 -5
- package/dist/types/src/components/SelectionProvider.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/selection.d.ts +24 -4
- package/dist/types/src/selection.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withAttention.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +10 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +38 -24
- package/src/attention.test.ts +13 -8
- package/src/attention.ts +64 -41
- package/src/components/AttentionGlyph.stories.tsx +16 -13
- package/src/components/AttentionGlyph.tsx +2 -2
- package/src/components/AttentionProvider.stories.tsx +11 -7
- package/src/components/AttentionProvider.tsx +75 -65
- package/src/components/SelectionProvider.tsx +64 -19
- package/src/index.ts +1 -0
- package/src/selection.ts +113 -72
- package/src/testing/decorators/withAttention.ts +9 -3
- package/src/types.ts +16 -0
- package/dist/lib/browser/chunk-3K54PTHR.mjs +0 -650
- package/dist/lib/browser/chunk-3K54PTHR.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-LVRGGYEI.mjs +0 -652
- package/dist/lib/node-esm/chunk-LVRGGYEI.mjs.map +0 -7
|
@@ -1,650 +0,0 @@
|
|
|
1
|
-
// src/attention.ts
|
|
2
|
-
import { untracked } from "@preact/signals-core";
|
|
3
|
-
import { Schema } from "effect";
|
|
4
|
-
import { live } from "@dxos/live-object";
|
|
5
|
-
import { ComplexMap } from "@dxos/util";
|
|
6
|
-
var ATTENDABLE_PATH_SEPARATOR = "~";
|
|
7
|
-
var AttentionSchema = Schema.mutable(Schema.Struct({
|
|
8
|
-
hasAttention: Schema.Boolean,
|
|
9
|
-
isAncestor: Schema.Boolean,
|
|
10
|
-
isRelated: Schema.Boolean
|
|
11
|
-
}));
|
|
12
|
-
var stringKey = (key) => key.join(",");
|
|
13
|
-
var AttentionManager = class {
|
|
14
|
-
constructor(initial = []) {
|
|
15
|
-
// Each attention path is associated with an attention object.
|
|
16
|
-
// The lookup is not a reactive object to ensure that attention for each path is subscribable independently.
|
|
17
|
-
this._map = new ComplexMap(stringKey);
|
|
18
|
-
this._state = live({
|
|
19
|
-
current: []
|
|
20
|
-
});
|
|
21
|
-
if (initial.length > 0) {
|
|
22
|
-
this.update(initial);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Currently attended element.
|
|
27
|
-
*
|
|
28
|
-
* @reactive
|
|
29
|
-
*/
|
|
30
|
-
get current() {
|
|
31
|
-
return this._state.current;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* All attention paths.
|
|
35
|
-
*/
|
|
36
|
-
keys() {
|
|
37
|
-
return Array.from(this._map.keys());
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get the attention state for a given path.
|
|
41
|
-
*/
|
|
42
|
-
get(key) {
|
|
43
|
-
const object = this._map.get(key);
|
|
44
|
-
if (object) {
|
|
45
|
-
return object;
|
|
46
|
-
}
|
|
47
|
-
const newObject = live(AttentionSchema, {
|
|
48
|
-
hasAttention: false,
|
|
49
|
-
isAncestor: false,
|
|
50
|
-
isRelated: false
|
|
51
|
-
});
|
|
52
|
-
this._map.set(key, newObject);
|
|
53
|
-
return newObject;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Update the currently attended element.
|
|
57
|
-
*
|
|
58
|
-
* @internal
|
|
59
|
-
*/
|
|
60
|
-
update(nextKey) {
|
|
61
|
-
const currentKey = untracked(() => this.current);
|
|
62
|
-
const currentRelatedTo = currentKey[0];
|
|
63
|
-
const nextRelatedTo = nextKey[0];
|
|
64
|
-
this.get([
|
|
65
|
-
nextRelatedTo
|
|
66
|
-
]);
|
|
67
|
-
const currentRelatedKeys = this.keys().filter((key) => key[0] === currentRelatedTo);
|
|
68
|
-
const nextRelatedKeys = this.keys().filter((key) => key[0] === nextRelatedTo && stringKey(key) !== stringKey(nextKey));
|
|
69
|
-
const prevRelatedKeys = currentRelatedKeys.filter((curr) => !nextRelatedKeys.find((next) => stringKey(curr) === stringKey(next)));
|
|
70
|
-
const currentAncestorKeys = currentKey.slice(1).map((_, i) => currentKey.slice(i + 1, currentKey.length));
|
|
71
|
-
const nextAncestorKeys = nextKey.slice(1).map((_, i) => nextKey.slice(i + 1, nextKey.length));
|
|
72
|
-
const prevAncestorKeys = currentAncestorKeys.filter((curr) => !nextAncestorKeys.find((next) => stringKey(curr) === stringKey(next)));
|
|
73
|
-
this._state.current = nextKey;
|
|
74
|
-
prevRelatedKeys.forEach((key) => this._set(key, {}));
|
|
75
|
-
prevAncestorKeys.forEach((key) => this._set(key, {}));
|
|
76
|
-
nextRelatedKeys.forEach((key) => this._set(key, {
|
|
77
|
-
isRelated: true
|
|
78
|
-
}));
|
|
79
|
-
nextAncestorKeys.forEach((key) => this._set(key, {
|
|
80
|
-
isAncestor: true
|
|
81
|
-
}));
|
|
82
|
-
this._set(nextKey, {
|
|
83
|
-
hasAttention: true
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
_set(key, attention) {
|
|
87
|
-
const object = this.get(key);
|
|
88
|
-
object.hasAttention = attention.hasAttention ?? false;
|
|
89
|
-
object.isAncestor = attention.isAncestor ?? false;
|
|
90
|
-
object.isRelated = attention.isRelated ?? false;
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
var getAttendables = (selector, cursor, acc = []) => {
|
|
94
|
-
const closestAttendable = cursor.closest(selector);
|
|
95
|
-
if (closestAttendable) {
|
|
96
|
-
const attendableId = closestAttendable.getAttribute("data-attendable-id");
|
|
97
|
-
if (!attendableId) {
|
|
98
|
-
const trigger = document.querySelector(`[aria-controls="${closestAttendable.getAttribute("id")}"]`);
|
|
99
|
-
if (!trigger) {
|
|
100
|
-
return acc;
|
|
101
|
-
} else {
|
|
102
|
-
return getAttendables(selector, trigger, acc);
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
acc.push(...attendableId.split(ATTENDABLE_PATH_SEPARATOR));
|
|
106
|
-
return !closestAttendable.parentElement ? acc : getAttendables(selector, closestAttendable.parentElement, acc);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return [
|
|
110
|
-
...new Set(acc)
|
|
111
|
-
];
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// src/components/AttentionGlyph.tsx
|
|
115
|
-
import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
|
|
116
|
-
import React, { forwardRef, useMemo } from "react";
|
|
117
|
-
import { Icon } from "@dxos/react-ui";
|
|
118
|
-
import { mx } from "@dxos/react-ui-theme";
|
|
119
|
-
var attentionGlyphStyles = "inline-block rounded-sm is-3 bs-3 bg-transparent text-accentText transition-colors [[data-contains-attended=true]_&]:bg-attentionRelated [[data-attention=true]_&]:bg-accentSurface [[data-attention=true]_&]:text-accentSurfaceText [[aria-current][data-attention=true]_&]:bg-accentSurface [[aria-current][data-attention=true]_&]:text-accentSurfaceText";
|
|
120
|
-
var presenceIconStyles = "is-3 bs-3 group-[[aria-current]_&:hover]/attentionGlyphButton:hidden group-[[aria-current]_&:focus]/attentionGlyphButton:hidden group-[[data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[data-attention=true]_&:focus]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:hover]/attentionGlyphButton:hidden group-[[aria-current][data-attention=true]_&:focus]/attentionGlyphButton:hidden";
|
|
121
|
-
var PresenceOne = () => {
|
|
122
|
-
var _effect = _useSignals();
|
|
123
|
-
try {
|
|
124
|
-
return /* @__PURE__ */ React.createElement("svg", {
|
|
125
|
-
width: "12",
|
|
126
|
-
height: "12",
|
|
127
|
-
viewBox: "0 0 12 12",
|
|
128
|
-
className: presenceIconStyles,
|
|
129
|
-
fill: "none",
|
|
130
|
-
xmlns: "http://www.w3.org/2000/svg"
|
|
131
|
-
}, /* @__PURE__ */ React.createElement("circle", {
|
|
132
|
-
cx: "6",
|
|
133
|
-
cy: "6",
|
|
134
|
-
r: "2.5",
|
|
135
|
-
fill: "currentColor"
|
|
136
|
-
}));
|
|
137
|
-
} finally {
|
|
138
|
-
_effect.f();
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
var PresenceMany = () => {
|
|
142
|
-
var _effect = _useSignals();
|
|
143
|
-
try {
|
|
144
|
-
return /* @__PURE__ */ React.createElement("svg", {
|
|
145
|
-
width: "12",
|
|
146
|
-
height: "12",
|
|
147
|
-
viewBox: "0 0 12 12",
|
|
148
|
-
className: presenceIconStyles,
|
|
149
|
-
fill: "none",
|
|
150
|
-
xmlns: "http://www.w3.org/2000/svg"
|
|
151
|
-
}, /* @__PURE__ */ React.createElement("path", {
|
|
152
|
-
d: "M6.75 8.27311C7.38815 7.72296 7.79212 6.90866 7.79212 6C7.79212 5.09134 7.38815 4.27704 6.75 3.72689C7.06722 3.58122 7.42019 3.5 7.79212 3.5C9.17283 3.5 10.2921 4.61929 10.2921 6C10.2921 7.38071 9.17283 8.5 7.79212 8.5C7.42019 8.5 7.06723 8.41878 6.75 8.27311Z",
|
|
153
|
-
fill: "currentColor"
|
|
154
|
-
}), /* @__PURE__ */ React.createElement("circle", {
|
|
155
|
-
cx: "4.25",
|
|
156
|
-
cy: "6",
|
|
157
|
-
r: "2.5",
|
|
158
|
-
fill: "currentColor"
|
|
159
|
-
}));
|
|
160
|
-
} finally {
|
|
161
|
-
_effect.f();
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
var Syncing = () => {
|
|
165
|
-
var _effect = _useSignals();
|
|
166
|
-
try {
|
|
167
|
-
const animationProps = useMemo(() => ({
|
|
168
|
-
// Synchronize animations.
|
|
169
|
-
animationDelay: `-${Date.now() % 2e3}ms`
|
|
170
|
-
}), []);
|
|
171
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
172
|
-
role: "status",
|
|
173
|
-
className: "flex items-center"
|
|
174
|
-
}, /* @__PURE__ */ React.createElement(Icon, {
|
|
175
|
-
icon: "ph--circle-notch--bold",
|
|
176
|
-
size: 3,
|
|
177
|
-
style: animationProps,
|
|
178
|
-
classNames: "text-subdued animate-[spin_2s_linear_infinite]"
|
|
179
|
-
}));
|
|
180
|
-
} finally {
|
|
181
|
-
_effect.f();
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
var AttentionGlyph = /* @__PURE__ */ forwardRef(({ presence, attended, syncing, containsAttended, classNames, ...props }, forwardedRef) => {
|
|
185
|
-
var _effect = _useSignals();
|
|
186
|
-
try {
|
|
187
|
-
const icon = syncing ? /* @__PURE__ */ React.createElement(Syncing, null) : presence === "many" ? /* @__PURE__ */ React.createElement(PresenceMany, null) : presence === "one" ? /* @__PURE__ */ React.createElement(PresenceOne, null) : null;
|
|
188
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
189
|
-
role: "none",
|
|
190
|
-
className: "flex",
|
|
191
|
-
"data-attention": attended,
|
|
192
|
-
"data-contains-attended": containsAttended
|
|
193
|
-
}, /* @__PURE__ */ React.createElement("span", {
|
|
194
|
-
role: "none",
|
|
195
|
-
...props,
|
|
196
|
-
className: mx(attentionGlyphStyles, classNames),
|
|
197
|
-
ref: forwardedRef
|
|
198
|
-
}, icon));
|
|
199
|
-
} finally {
|
|
200
|
-
_effect.f();
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// src/components/AttentionProvider.tsx
|
|
205
|
-
import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
|
|
206
|
-
import { createContext } from "@radix-ui/react-context";
|
|
207
|
-
import { Primitive } from "@radix-ui/react-primitive";
|
|
208
|
-
import { Slot } from "@radix-ui/react-slot";
|
|
209
|
-
import React2, { useMemo as useMemo2, forwardRef as forwardRef2 } from "react";
|
|
210
|
-
import { useDefaultValue } from "@dxos/react-ui";
|
|
211
|
-
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
212
|
-
var ATTENTION_NAME = "Attention";
|
|
213
|
-
var ATTENABLE_ATTRIBUTE = "data-attendable-id";
|
|
214
|
-
var ATTENTION_SOURCE_ATTRIBUTE = "data-is-attention-source";
|
|
215
|
-
var [AttentionContextProvider, useAttentionContext] = createContext(ATTENTION_NAME, {
|
|
216
|
-
attention: new AttentionManager(),
|
|
217
|
-
path: []
|
|
218
|
-
});
|
|
219
|
-
var UNKNOWN_ATTENDABLE = {
|
|
220
|
-
hasAttention: false,
|
|
221
|
-
isAncestor: false,
|
|
222
|
-
isRelated: false
|
|
223
|
-
};
|
|
224
|
-
var useAttention = (attendableId) => {
|
|
225
|
-
const { attention, path } = useAttentionContext(ATTENTION_NAME);
|
|
226
|
-
if (!attendableId) {
|
|
227
|
-
return UNKNOWN_ATTENDABLE;
|
|
228
|
-
}
|
|
229
|
-
const current = [
|
|
230
|
-
...attendableId.split(ATTENDABLE_PATH_SEPARATOR),
|
|
231
|
-
...path
|
|
232
|
-
];
|
|
233
|
-
return attention.get(current);
|
|
234
|
-
};
|
|
235
|
-
var useAttended = () => {
|
|
236
|
-
const { attention } = useAttentionContext(ATTENTION_NAME);
|
|
237
|
-
return attention.current;
|
|
238
|
-
};
|
|
239
|
-
var useAttentionAttributes = (attendableId) => {
|
|
240
|
-
const { hasAttention } = useAttention(attendableId);
|
|
241
|
-
return useMemo2(() => {
|
|
242
|
-
const attributes = {
|
|
243
|
-
[ATTENABLE_ATTRIBUTE]: attendableId
|
|
244
|
-
};
|
|
245
|
-
if (hasAttention) {
|
|
246
|
-
attributes[ATTENTION_SOURCE_ATTRIBUTE] = "true";
|
|
247
|
-
}
|
|
248
|
-
return attributes;
|
|
249
|
-
}, [
|
|
250
|
-
attendableId,
|
|
251
|
-
hasAttention
|
|
252
|
-
]);
|
|
253
|
-
};
|
|
254
|
-
var useAttentionPath = () => {
|
|
255
|
-
const { path } = useAttentionContext(ATTENTION_NAME);
|
|
256
|
-
return path;
|
|
257
|
-
};
|
|
258
|
-
var RootAttentionProvider = ({ children, attention: propsAttention, onChange }) => {
|
|
259
|
-
var _effect = _useSignals2();
|
|
260
|
-
try {
|
|
261
|
-
const attention = useDefaultValue(propsAttention, () => new AttentionManager());
|
|
262
|
-
const handleFocus = (event) => {
|
|
263
|
-
const selector = [
|
|
264
|
-
"[data-attendable-id]",
|
|
265
|
-
...Array.from(document.querySelectorAll("[aria-controls]")).map((el) => `[id="${el.getAttribute("aria-controls")}"]`)
|
|
266
|
-
].join(",");
|
|
267
|
-
const prev = attention.current;
|
|
268
|
-
const next = getAttendables(selector, event.target);
|
|
269
|
-
if (next.length > 0 && (prev.length !== next.length || !!prev.find((id, index) => next[index] !== id))) {
|
|
270
|
-
attention.update(next);
|
|
271
|
-
onChange?.(next);
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
return /* @__PURE__ */ React2.createElement(AttentionContextProvider, {
|
|
275
|
-
attention,
|
|
276
|
-
path: []
|
|
277
|
-
}, /* @__PURE__ */ React2.createElement("div", {
|
|
278
|
-
role: "none",
|
|
279
|
-
className: "contents",
|
|
280
|
-
onFocusCapture: handleFocus
|
|
281
|
-
}, children));
|
|
282
|
-
} finally {
|
|
283
|
-
_effect.f();
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
var AttentionProvider = ({ id, children }) => {
|
|
287
|
-
var _effect = _useSignals2();
|
|
288
|
-
try {
|
|
289
|
-
const { attention, path } = useAttentionContext(ATTENTION_NAME);
|
|
290
|
-
const nextPath = useMemo2(() => [
|
|
291
|
-
id,
|
|
292
|
-
...path
|
|
293
|
-
], [
|
|
294
|
-
id,
|
|
295
|
-
path
|
|
296
|
-
]);
|
|
297
|
-
return /* @__PURE__ */ React2.createElement(AttentionContextProvider, {
|
|
298
|
-
attention,
|
|
299
|
-
path: nextPath
|
|
300
|
-
}, children);
|
|
301
|
-
} finally {
|
|
302
|
-
_effect.f();
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
var AttendableContainer = /* @__PURE__ */ forwardRef2(({ id, classNames, children, asChild, ...props }, forwardedRef) => {
|
|
306
|
-
var _effect = _useSignals2();
|
|
307
|
-
try {
|
|
308
|
-
const attentionAttrs = useAttentionAttributes(id);
|
|
309
|
-
const Root = asChild ? Slot : Primitive.div;
|
|
310
|
-
return /* @__PURE__ */ React2.createElement(Root, {
|
|
311
|
-
role: "none",
|
|
312
|
-
...attentionAttrs,
|
|
313
|
-
...props,
|
|
314
|
-
className: mx2("attention-surface", props.tabIndex === 0 && "dx-focus-ring-inset-over-all", classNames),
|
|
315
|
-
ref: forwardedRef
|
|
316
|
-
}, children);
|
|
317
|
-
} finally {
|
|
318
|
-
_effect.f();
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
// src/selection.ts
|
|
323
|
-
import { untracked as untracked2 } from "@preact/signals-core";
|
|
324
|
-
import { Match, Schema as Schema2 } from "effect";
|
|
325
|
-
import { invariant } from "@dxos/invariant";
|
|
326
|
-
import { live as live2 } from "@dxos/live-object";
|
|
327
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/ui/react-ui-attention/src/selection.ts";
|
|
328
|
-
var SelectionSchema = Schema2.Union(Schema2.Struct({
|
|
329
|
-
mode: Schema2.Literal("single"),
|
|
330
|
-
id: Schema2.optional(Schema2.String)
|
|
331
|
-
}).pipe(Schema2.mutable), Schema2.Struct({
|
|
332
|
-
mode: Schema2.Literal("multi"),
|
|
333
|
-
ids: Schema2.Array(Schema2.String).pipe(Schema2.mutable)
|
|
334
|
-
}).pipe(Schema2.mutable), Schema2.Struct({
|
|
335
|
-
mode: Schema2.Literal("range"),
|
|
336
|
-
from: Schema2.optional(Schema2.String),
|
|
337
|
-
to: Schema2.optional(Schema2.String)
|
|
338
|
-
}).pipe(Schema2.mutable), Schema2.Struct({
|
|
339
|
-
mode: Schema2.Literal("multi-range"),
|
|
340
|
-
ranges: Schema2.Array(Schema2.Struct({
|
|
341
|
-
from: Schema2.String,
|
|
342
|
-
to: Schema2.String
|
|
343
|
-
})).pipe(Schema2.mutable)
|
|
344
|
-
}).pipe(Schema2.mutable)).pipe(Schema2.mutable);
|
|
345
|
-
var defaultSelection = Match.type().pipe(Match.withReturnType(), Match.when("single", () => ({
|
|
346
|
-
mode: "single"
|
|
347
|
-
})), Match.when("multi", () => ({
|
|
348
|
-
mode: "multi",
|
|
349
|
-
ids: []
|
|
350
|
-
})), Match.when("range", () => ({
|
|
351
|
-
mode: "range"
|
|
352
|
-
})), Match.when("multi-range", () => ({
|
|
353
|
-
mode: "multi-range",
|
|
354
|
-
ranges: []
|
|
355
|
-
})), Match.exhaustive);
|
|
356
|
-
var getSelectionSet = (selectionManager, contextId) => {
|
|
357
|
-
const ids = new Set(contextId ? [
|
|
358
|
-
contextId
|
|
359
|
-
] : []);
|
|
360
|
-
for (const context of selectionManager.getSelectionContexts()) {
|
|
361
|
-
const selection = selectionManager.getSelection(context);
|
|
362
|
-
if (selection?.mode === "multi") {
|
|
363
|
-
for (const id of selection.ids) {
|
|
364
|
-
ids.add(id);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
return ids;
|
|
369
|
-
};
|
|
370
|
-
var SelectionManager = class {
|
|
371
|
-
constructor(initial = {}) {
|
|
372
|
-
this._state = live2({
|
|
373
|
-
selections: {}
|
|
374
|
-
});
|
|
375
|
-
if (Object.keys(initial).length > 0) {
|
|
376
|
-
untracked2(() => {
|
|
377
|
-
this._state.selections = initial;
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
getSelectionContexts() {
|
|
382
|
-
return Object.keys(this._state.selections);
|
|
383
|
-
}
|
|
384
|
-
getSelection(contextId, mode = void 0) {
|
|
385
|
-
const selection = untracked2(() => this._state.selections[contextId]);
|
|
386
|
-
if (!mode || selection) {
|
|
387
|
-
return selection;
|
|
388
|
-
}
|
|
389
|
-
return untracked2(() => {
|
|
390
|
-
this._state.selections[contextId] = defaultSelection(mode);
|
|
391
|
-
return this._state.selections[contextId];
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
// TODO(burdon): Disambiguate with getSelection?
|
|
395
|
-
getSelected(contextId, mode = "multi") {
|
|
396
|
-
const selection = this.getSelection(contextId, mode);
|
|
397
|
-
invariant(selection?.mode === mode, "Selection mode mismatch", {
|
|
398
|
-
F: __dxlog_file,
|
|
399
|
-
L: 104,
|
|
400
|
-
S: this,
|
|
401
|
-
A: [
|
|
402
|
-
"selection?.mode === mode",
|
|
403
|
-
"'Selection mode mismatch'"
|
|
404
|
-
]
|
|
405
|
-
});
|
|
406
|
-
return Match.type().pipe(Match.when({
|
|
407
|
-
mode: "single"
|
|
408
|
-
}, (s) => s.id), Match.when({
|
|
409
|
-
mode: "multi"
|
|
410
|
-
}, (s) => s.ids), Match.when({
|
|
411
|
-
mode: "range"
|
|
412
|
-
}, (s) => s.from && s.to ? {
|
|
413
|
-
from: s.from,
|
|
414
|
-
to: s.to
|
|
415
|
-
} : void 0), Match.when({
|
|
416
|
-
mode: "multi-range"
|
|
417
|
-
}, (s) => s.ranges), Match.exhaustive)(selection);
|
|
418
|
-
}
|
|
419
|
-
updateSingle(contextId, id) {
|
|
420
|
-
untracked2(() => {
|
|
421
|
-
const selection = this.getSelection(contextId, "single");
|
|
422
|
-
invariant(selection?.mode === "single", "Selection mode is not single", {
|
|
423
|
-
F: __dxlog_file,
|
|
424
|
-
L: 117,
|
|
425
|
-
S: this,
|
|
426
|
-
A: [
|
|
427
|
-
"selection?.mode === 'single'",
|
|
428
|
-
"'Selection mode is not single'"
|
|
429
|
-
]
|
|
430
|
-
});
|
|
431
|
-
selection.id = id;
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
updateMulti(contextId, ids) {
|
|
435
|
-
untracked2(() => {
|
|
436
|
-
const selection = this.getSelection(contextId, "multi");
|
|
437
|
-
invariant(selection?.mode === "multi", "Selection mode is not multi", {
|
|
438
|
-
F: __dxlog_file,
|
|
439
|
-
L: 125,
|
|
440
|
-
S: this,
|
|
441
|
-
A: [
|
|
442
|
-
"selection?.mode === 'multi'",
|
|
443
|
-
"'Selection mode is not multi'"
|
|
444
|
-
]
|
|
445
|
-
});
|
|
446
|
-
selection.ids.splice(0, selection.ids.length, ...ids);
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
updateRange(contextId, from, to) {
|
|
450
|
-
untracked2(() => {
|
|
451
|
-
const selection = this.getSelection(contextId, "range");
|
|
452
|
-
invariant(selection?.mode === "range", "Selection mode is not range", {
|
|
453
|
-
F: __dxlog_file,
|
|
454
|
-
L: 133,
|
|
455
|
-
S: this,
|
|
456
|
-
A: [
|
|
457
|
-
"selection?.mode === 'range'",
|
|
458
|
-
"'Selection mode is not range'"
|
|
459
|
-
]
|
|
460
|
-
});
|
|
461
|
-
selection.from = from;
|
|
462
|
-
selection.to = to;
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
updateMultiRange(contextId, ranges) {
|
|
466
|
-
untracked2(() => {
|
|
467
|
-
const selection = this.getSelection(contextId, "multi-range");
|
|
468
|
-
invariant(selection?.mode === "multi-range", "Selection mode is not multi-range", {
|
|
469
|
-
F: __dxlog_file,
|
|
470
|
-
L: 142,
|
|
471
|
-
S: this,
|
|
472
|
-
A: [
|
|
473
|
-
"selection?.mode === 'multi-range'",
|
|
474
|
-
"'Selection mode is not multi-range'"
|
|
475
|
-
]
|
|
476
|
-
});
|
|
477
|
-
selection.ranges.splice(0, selection.ranges.length, ...ranges);
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
clearSelection(contextId) {
|
|
481
|
-
untracked2(() => {
|
|
482
|
-
const selection = this.getSelection(contextId);
|
|
483
|
-
Match.type().pipe(Match.when(void 0, () => {
|
|
484
|
-
}), Match.when({
|
|
485
|
-
mode: "single"
|
|
486
|
-
}, (s) => {
|
|
487
|
-
s.id = void 0;
|
|
488
|
-
}), Match.when({
|
|
489
|
-
mode: "multi"
|
|
490
|
-
}, (s) => {
|
|
491
|
-
s.ids.splice(0, s.ids.length);
|
|
492
|
-
}), Match.when({
|
|
493
|
-
mode: "range"
|
|
494
|
-
}, (s) => {
|
|
495
|
-
s.from = void 0;
|
|
496
|
-
s.to = void 0;
|
|
497
|
-
}), Match.when({
|
|
498
|
-
mode: "multi-range"
|
|
499
|
-
}, (s) => {
|
|
500
|
-
s.ranges.splice(0, s.ranges.length);
|
|
501
|
-
}), Match.exhaustive)(selection);
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
toggleSelection(contextId, id) {
|
|
505
|
-
untracked2(() => {
|
|
506
|
-
const selection = this.getSelection(contextId, "multi");
|
|
507
|
-
invariant(selection?.mode === "multi", "Selection mode is not multi", {
|
|
508
|
-
F: __dxlog_file,
|
|
509
|
-
L: 175,
|
|
510
|
-
S: this,
|
|
511
|
-
A: [
|
|
512
|
-
"selection?.mode === 'multi'",
|
|
513
|
-
"'Selection mode is not multi'"
|
|
514
|
-
]
|
|
515
|
-
});
|
|
516
|
-
if (selection.ids.includes(id)) {
|
|
517
|
-
const index = selection.ids.indexOf(id);
|
|
518
|
-
selection.ids.splice(index, 1);
|
|
519
|
-
} else {
|
|
520
|
-
selection.ids.push(id);
|
|
521
|
-
}
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
// src/components/SelectionProvider.tsx
|
|
527
|
-
import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
|
|
528
|
-
import { createContext as createContext2 } from "@radix-ui/react-context";
|
|
529
|
-
import { Match as Match2 } from "effect";
|
|
530
|
-
import React3, { useCallback, useMemo as useMemo3 } from "react";
|
|
531
|
-
import { useDefaultValue as useDefaultValue2 } from "@dxos/react-ui";
|
|
532
|
-
var SELECTION_NAME = "Selection";
|
|
533
|
-
var [SelectionContextProvider, useSelectionContext] = createContext2(SELECTION_NAME, {
|
|
534
|
-
selection: new SelectionManager()
|
|
535
|
-
});
|
|
536
|
-
var SelectionProvider = ({ children, selection: propsSelection }) => {
|
|
537
|
-
var _effect = _useSignals3();
|
|
538
|
-
try {
|
|
539
|
-
const selection = useDefaultValue2(propsSelection, () => new SelectionManager());
|
|
540
|
-
return /* @__PURE__ */ React3.createElement(SelectionContextProvider, {
|
|
541
|
-
selection
|
|
542
|
-
}, children);
|
|
543
|
-
} finally {
|
|
544
|
-
_effect.f();
|
|
545
|
-
}
|
|
546
|
-
};
|
|
547
|
-
var useSelectionManager = () => {
|
|
548
|
-
const { selection } = useSelectionContext(SELECTION_NAME);
|
|
549
|
-
return selection;
|
|
550
|
-
};
|
|
551
|
-
var useSelected = (contextId, mode = "multi") => {
|
|
552
|
-
const { selection } = useSelectionContext(SELECTION_NAME);
|
|
553
|
-
if (contextId) {
|
|
554
|
-
return selection.getSelected(contextId, mode);
|
|
555
|
-
}
|
|
556
|
-
return Match2.type().pipe(Match2.when({
|
|
557
|
-
mode: "single"
|
|
558
|
-
}, (s) => s.id), Match2.when({
|
|
559
|
-
mode: "multi"
|
|
560
|
-
}, (s) => s.ids), Match2.when({
|
|
561
|
-
mode: "range"
|
|
562
|
-
}, (s) => s.from && s.to ? {
|
|
563
|
-
from: s.from,
|
|
564
|
-
to: s.to
|
|
565
|
-
} : void 0), Match2.when({
|
|
566
|
-
mode: "multi-range"
|
|
567
|
-
}, (s) => s.ranges), Match2.exhaustive)(defaultSelection(mode));
|
|
568
|
-
};
|
|
569
|
-
var useSelectionActions = (contextIds, mode = "multi") => {
|
|
570
|
-
const stableContextIds = useMemo3(() => contextIds, [
|
|
571
|
-
JSON.stringify(contextIds)
|
|
572
|
-
]);
|
|
573
|
-
const { selection } = useSelectionContext(SELECTION_NAME);
|
|
574
|
-
const singleSelect = useCallback((id) => {
|
|
575
|
-
for (const contextId of stableContextIds) {
|
|
576
|
-
selection.updateSingle(contextId, id);
|
|
577
|
-
}
|
|
578
|
-
}, [
|
|
579
|
-
selection,
|
|
580
|
-
stableContextIds
|
|
581
|
-
]);
|
|
582
|
-
const multiSelect = useCallback((ids) => {
|
|
583
|
-
for (const contextId of stableContextIds) {
|
|
584
|
-
selection.updateMulti(contextId, ids);
|
|
585
|
-
}
|
|
586
|
-
}, [
|
|
587
|
-
selection,
|
|
588
|
-
stableContextIds
|
|
589
|
-
]);
|
|
590
|
-
const rangeSelect = useCallback((from, to) => {
|
|
591
|
-
for (const contextId of stableContextIds) {
|
|
592
|
-
selection.updateRange(contextId, from, to);
|
|
593
|
-
}
|
|
594
|
-
}, [
|
|
595
|
-
selection,
|
|
596
|
-
stableContextIds
|
|
597
|
-
]);
|
|
598
|
-
const toggle = useCallback((id) => {
|
|
599
|
-
for (const contextId of stableContextIds) {
|
|
600
|
-
selection.toggleSelection(contextId, id);
|
|
601
|
-
}
|
|
602
|
-
}, [
|
|
603
|
-
selection,
|
|
604
|
-
stableContextIds
|
|
605
|
-
]);
|
|
606
|
-
const clear = useCallback(() => {
|
|
607
|
-
for (const contextId of stableContextIds) {
|
|
608
|
-
selection.clearSelection(contextId);
|
|
609
|
-
}
|
|
610
|
-
}, [
|
|
611
|
-
selection,
|
|
612
|
-
stableContextIds
|
|
613
|
-
]);
|
|
614
|
-
return {
|
|
615
|
-
singleSelect,
|
|
616
|
-
multiSelect,
|
|
617
|
-
rangeSelect,
|
|
618
|
-
toggle,
|
|
619
|
-
clear
|
|
620
|
-
};
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
export {
|
|
624
|
-
ATTENDABLE_PATH_SEPARATOR,
|
|
625
|
-
AttentionSchema,
|
|
626
|
-
AttentionManager,
|
|
627
|
-
getAttendables,
|
|
628
|
-
Syncing,
|
|
629
|
-
AttentionGlyph,
|
|
630
|
-
ATTENTION_NAME,
|
|
631
|
-
ATTENABLE_ATTRIBUTE,
|
|
632
|
-
ATTENTION_SOURCE_ATTRIBUTE,
|
|
633
|
-
useAttentionContext,
|
|
634
|
-
useAttention,
|
|
635
|
-
useAttended,
|
|
636
|
-
useAttentionAttributes,
|
|
637
|
-
useAttentionPath,
|
|
638
|
-
RootAttentionProvider,
|
|
639
|
-
AttentionProvider,
|
|
640
|
-
AttendableContainer,
|
|
641
|
-
SelectionSchema,
|
|
642
|
-
defaultSelection,
|
|
643
|
-
getSelectionSet,
|
|
644
|
-
SelectionManager,
|
|
645
|
-
SelectionProvider,
|
|
646
|
-
useSelectionManager,
|
|
647
|
-
useSelected,
|
|
648
|
-
useSelectionActions
|
|
649
|
-
};
|
|
650
|
-
//# sourceMappingURL=chunk-3K54PTHR.mjs.map
|