@dxos/react-ui-attention 0.8.4-main.4a85c3132b → 0.8.4-main.51f1e5ca51
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-TAZUDNJI.mjs → chunk-DAIZNFKL.mjs} +118 -155
- package/dist/lib/browser/chunk-DAIZNFKL.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +14 -10
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -2
- package/dist/lib/browser/testing/index.mjs.map +1 -1
- package/dist/lib/browser/types.mjs +0 -6
- package/dist/lib/node-esm/{chunk-FT553KKP.mjs → chunk-HZTQDMTS.mjs} +118 -154
- package/dist/lib/node-esm/chunk-HZTQDMTS.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +14 -10
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +1 -2
- package/dist/lib/node-esm/testing/index.mjs.map +1 -1
- package/dist/lib/node-esm/types.mjs +0 -6
- package/dist/types/src/attention.d.ts +41 -10
- package/dist/types/src/attention.d.ts.map +1 -1
- package/dist/types/src/components/AttentionGlyph.stories.d.ts.map +1 -1
- package/dist/types/src/components/AttentionProvider.d.ts +8 -10
- package/dist/types/src/components/AttentionProvider.d.ts.map +1 -1
- package/dist/types/src/components/AttentionProvider.stories.d.ts.map +1 -1
- package/dist/types/src/components/SelectionProvider.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- 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 +0 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -25
- package/src/attention.test.ts +179 -23
- package/src/attention.ts +132 -54
- package/src/components/AttentionGlyph.tsx +1 -1
- package/src/components/AttentionProvider.stories.tsx +0 -1
- package/src/components/AttentionProvider.tsx +16 -43
- package/src/index.ts +0 -1
- package/src/types.ts +0 -3
- package/dist/lib/browser/chunk-PAZBZOO2.mjs +0 -7
- package/dist/lib/browser/chunk-PAZBZOO2.mjs.map +0 -7
- package/dist/lib/browser/chunk-TAZUDNJI.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FT553KKP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-PMGNPL4M.mjs +0 -9
- package/dist/lib/node-esm/chunk-PMGNPL4M.mjs.map +0 -7
|
@@ -1,38 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ATTENDABLE_PATH_SEPARATOR
|
|
3
|
-
} from "./chunk-PAZBZOO2.mjs";
|
|
4
|
-
|
|
5
1
|
// src/attention.ts
|
|
6
2
|
import { Atom } from "@effect-atom/atom-react";
|
|
7
|
-
import { ComplexMap } from "@dxos/util";
|
|
8
|
-
var stringKey = (key) => key.join(",");
|
|
9
3
|
var AttentionManager = class {
|
|
10
4
|
_registry;
|
|
11
|
-
|
|
12
|
-
// The lookup is not a reactive object to ensure that attention for each path is subscribable independently.
|
|
13
|
-
_map = new ComplexMap(stringKey);
|
|
5
|
+
_map = /* @__PURE__ */ new Map();
|
|
14
6
|
_currentAtom;
|
|
15
7
|
constructor(_registry, initial = []) {
|
|
16
8
|
this._registry = _registry;
|
|
17
|
-
this._currentAtom = Atom.make([]);
|
|
9
|
+
this._currentAtom = Atom.make([]).pipe(Atom.keepAlive);
|
|
18
10
|
if (initial.length > 0) {
|
|
19
11
|
this.update(initial);
|
|
20
12
|
}
|
|
21
13
|
}
|
|
22
14
|
/**
|
|
23
|
-
* Atom for the currently attended element
|
|
15
|
+
* Atom for the currently attended element IDs.
|
|
24
16
|
*/
|
|
25
17
|
get current() {
|
|
26
18
|
return this._currentAtom;
|
|
27
19
|
}
|
|
28
20
|
/**
|
|
29
|
-
* Gets the currently attended element
|
|
21
|
+
* Gets the currently attended element IDs.
|
|
30
22
|
*/
|
|
31
23
|
getCurrent() {
|
|
32
24
|
return this._registry.get(this._currentAtom);
|
|
33
25
|
}
|
|
34
26
|
/**
|
|
35
|
-
* Subscribe to changes in the current attention
|
|
27
|
+
* Subscribe to changes in the current attention IDs.
|
|
36
28
|
*/
|
|
37
29
|
subscribeCurrent(cb) {
|
|
38
30
|
this._registry.get(this._currentAtom);
|
|
@@ -41,30 +33,30 @@ var AttentionManager = class {
|
|
|
41
33
|
});
|
|
42
34
|
}
|
|
43
35
|
/**
|
|
44
|
-
* All
|
|
36
|
+
* All tracked qualified IDs.
|
|
45
37
|
*/
|
|
46
38
|
keys() {
|
|
47
39
|
return Array.from(this._map.keys());
|
|
48
40
|
}
|
|
49
41
|
/**
|
|
50
|
-
* Get the attention state for a given
|
|
42
|
+
* Get the attention state for a given qualified ID.
|
|
51
43
|
*/
|
|
52
|
-
get(
|
|
53
|
-
const atom = this._getAtom(
|
|
44
|
+
get(id) {
|
|
45
|
+
const atom = this._getAtom(id);
|
|
54
46
|
return this._registry.get(atom);
|
|
55
47
|
}
|
|
56
48
|
/**
|
|
57
|
-
* Subscribe to changes in the attention state for a given
|
|
49
|
+
* Subscribe to changes in the attention state for a given qualified ID.
|
|
58
50
|
*/
|
|
59
|
-
subscribe(
|
|
60
|
-
const atom = this._getAtom(
|
|
51
|
+
subscribe(id, cb) {
|
|
52
|
+
const atom = this._getAtom(id);
|
|
61
53
|
this._registry.get(atom);
|
|
62
54
|
return this._registry.subscribe(atom, () => {
|
|
63
55
|
cb(this._registry.get(atom));
|
|
64
56
|
});
|
|
65
57
|
}
|
|
66
|
-
_getAtom(
|
|
67
|
-
const existing = this._map.get(
|
|
58
|
+
_getAtom(id) {
|
|
59
|
+
const existing = this._map.get(id);
|
|
68
60
|
if (existing) {
|
|
69
61
|
return existing;
|
|
70
62
|
}
|
|
@@ -72,43 +64,70 @@ var AttentionManager = class {
|
|
|
72
64
|
hasAttention: false,
|
|
73
65
|
isAncestor: false,
|
|
74
66
|
isRelated: false
|
|
75
|
-
});
|
|
76
|
-
this._map.set(
|
|
67
|
+
}).pipe(Atom.keepAlive);
|
|
68
|
+
this._map.set(id, newAtom);
|
|
77
69
|
return newAtom;
|
|
78
70
|
}
|
|
79
71
|
/**
|
|
80
72
|
* Update the currently attended element.
|
|
73
|
+
* Takes the array of qualified IDs collected from the DOM; the first element is the primary attended item.
|
|
74
|
+
* Ancestry is derived from the progressive prefixes of the primary ID.
|
|
75
|
+
* Relatedness is derived from the segment ID: any tracked key whose last `/` segment matches the
|
|
76
|
+
* attended ID's segment ID is marked `isRelated`. Additionally, if the primary ID is a linked segment
|
|
77
|
+
* (starts with `~`), its immediate parent gets `isRelated` alongside `isAncestor`.
|
|
81
78
|
*
|
|
82
79
|
* @internal
|
|
83
80
|
*/
|
|
84
|
-
update(
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
]
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
81
|
+
update(nextIds) {
|
|
82
|
+
const primaryId = nextIds[0];
|
|
83
|
+
if (!primaryId) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const currentIds = this.getCurrent();
|
|
87
|
+
const prevPrimaryId = currentIds[0];
|
|
88
|
+
if (prevPrimaryId) {
|
|
89
|
+
const prevPrefixes = expandAttendableId(prevPrimaryId);
|
|
90
|
+
for (const prefix of prevPrefixes) {
|
|
91
|
+
this._set(prefix, {});
|
|
92
|
+
}
|
|
93
|
+
const prevSegmentId = getSegmentId(prevPrimaryId);
|
|
94
|
+
for (const key of this.keys()) {
|
|
95
|
+
if (getSegmentId(key) === prevSegmentId) {
|
|
96
|
+
this._set(key, {});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
this._registry.set(this._currentAtom, nextIds);
|
|
101
|
+
const prefixes = expandAttendableId(primaryId);
|
|
102
|
+
const prefixSet = new Set(prefixes);
|
|
103
|
+
const linkedParent = isLinkedSegment(primaryId) ? getParentId(primaryId) : void 0;
|
|
104
|
+
for (const prefix of prefixes) {
|
|
105
|
+
if (prefix === primaryId) {
|
|
106
|
+
this._set(prefix, {
|
|
107
|
+
hasAttention: true
|
|
108
|
+
});
|
|
109
|
+
} else if (prefix === linkedParent) {
|
|
110
|
+
this._set(prefix, {
|
|
111
|
+
isAncestor: true,
|
|
112
|
+
isRelated: true
|
|
113
|
+
});
|
|
114
|
+
} else {
|
|
115
|
+
this._set(prefix, {
|
|
116
|
+
isAncestor: true
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const segmentId = getSegmentId(primaryId);
|
|
121
|
+
for (const key of this.keys()) {
|
|
122
|
+
if (!prefixSet.has(key) && getSegmentId(key) === segmentId) {
|
|
123
|
+
this._set(key, {
|
|
124
|
+
isRelated: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
109
128
|
}
|
|
110
|
-
_set(
|
|
111
|
-
const atom = this._getAtom(
|
|
129
|
+
_set(id, attention) {
|
|
130
|
+
const atom = this._getAtom(id);
|
|
112
131
|
this._registry.set(atom, {
|
|
113
132
|
hasAttention: attention.hasAttention ?? false,
|
|
114
133
|
isAncestor: attention.isAncestor ?? false,
|
|
@@ -128,7 +147,7 @@ var getAttendables = (selector, cursor, acc = []) => {
|
|
|
128
147
|
return getAttendables(selector, trigger, acc);
|
|
129
148
|
}
|
|
130
149
|
} else {
|
|
131
|
-
acc.push(
|
|
150
|
+
acc.push(attendableId);
|
|
132
151
|
return !closestAttendable.parentElement ? acc : getAttendables(selector, closestAttendable.parentElement, acc);
|
|
133
152
|
}
|
|
134
153
|
}
|
|
@@ -136,6 +155,30 @@ var getAttendables = (selector, cursor, acc = []) => {
|
|
|
136
155
|
...new Set(acc)
|
|
137
156
|
];
|
|
138
157
|
};
|
|
158
|
+
var LINKED_PREFIX = "~";
|
|
159
|
+
var expandAttendableId = (qualifiedId) => {
|
|
160
|
+
const segments = qualifiedId.split("/");
|
|
161
|
+
return segments.map((_, index) => segments.slice(0, index + 1).join("/"));
|
|
162
|
+
};
|
|
163
|
+
var isLinkedSegment = (qualifiedId) => {
|
|
164
|
+
const lastSegment = qualifiedId.split("/").pop() ?? "";
|
|
165
|
+
return lastSegment.startsWith(LINKED_PREFIX);
|
|
166
|
+
};
|
|
167
|
+
var linkedSegment = (variant) => `${LINKED_PREFIX}${variant}`;
|
|
168
|
+
var getLinkedVariant = (qualifiedId) => {
|
|
169
|
+
const lastSegment = qualifiedId.split("/").pop() ?? "";
|
|
170
|
+
return lastSegment.startsWith(LINKED_PREFIX) ? lastSegment.slice(LINKED_PREFIX.length) : lastSegment;
|
|
171
|
+
};
|
|
172
|
+
var getParentId = (qualifiedId) => {
|
|
173
|
+
if (!qualifiedId) {
|
|
174
|
+
return void 0;
|
|
175
|
+
}
|
|
176
|
+
const lastSlash = qualifiedId.lastIndexOf("/");
|
|
177
|
+
return lastSlash > 0 ? qualifiedId.slice(0, lastSlash) : void 0;
|
|
178
|
+
};
|
|
179
|
+
var getSegmentId = (qualifiedId) => {
|
|
180
|
+
return qualifiedId.split("/").pop() ?? qualifiedId;
|
|
181
|
+
};
|
|
139
182
|
|
|
140
183
|
// src/components/AttentionProvider.tsx
|
|
141
184
|
import { RegistryContext } from "@effect-atom/atom-react";
|
|
@@ -151,8 +194,7 @@ var ATTENTION_NAME = "Attention";
|
|
|
151
194
|
var ATTENDABLE_ATTRIBUTE = "data-attendable-id";
|
|
152
195
|
var ATTENTION_SOURCE_ATTRIBUTE = "data-w-attention-source";
|
|
153
196
|
var [AttentionContextProvider, useAttentionContext] = createContext(ATTENTION_NAME, {
|
|
154
|
-
attention: void 0
|
|
155
|
-
path: []
|
|
197
|
+
attention: void 0
|
|
156
198
|
});
|
|
157
199
|
var UNKNOWN_ATTENDABLE = {
|
|
158
200
|
hasAttention: false,
|
|
@@ -160,32 +202,21 @@ var UNKNOWN_ATTENDABLE = {
|
|
|
160
202
|
isRelated: false
|
|
161
203
|
};
|
|
162
204
|
var useAttention = (attendableId) => {
|
|
163
|
-
const { attention
|
|
205
|
+
const { attention } = useAttentionContext(ATTENTION_NAME);
|
|
164
206
|
const [state, setState] = useState(UNKNOWN_ATTENDABLE);
|
|
165
|
-
const key = useMemo(() => {
|
|
166
|
-
if (!attendableId) {
|
|
167
|
-
return void 0;
|
|
168
|
-
}
|
|
169
|
-
return [
|
|
170
|
-
...attendableId.split(ATTENDABLE_PATH_SEPARATOR),
|
|
171
|
-
...path
|
|
172
|
-
];
|
|
173
|
-
}, [
|
|
174
|
-
attendableId,
|
|
175
|
-
path
|
|
176
|
-
]);
|
|
177
207
|
useEffect(() => {
|
|
178
|
-
if (!
|
|
208
|
+
if (!attendableId || !attention) {
|
|
179
209
|
setState(UNKNOWN_ATTENDABLE);
|
|
180
210
|
return;
|
|
181
211
|
}
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
const currentState = attention.get(attendableId);
|
|
213
|
+
setState(currentState);
|
|
214
|
+
return attention.subscribe(attendableId, (newState) => {
|
|
184
215
|
setState(newState);
|
|
185
216
|
});
|
|
186
217
|
}, [
|
|
187
218
|
attention,
|
|
188
|
-
|
|
219
|
+
attendableId
|
|
189
220
|
]);
|
|
190
221
|
return state;
|
|
191
222
|
};
|
|
@@ -220,10 +251,6 @@ var useAttentionAttributes = (attendableId) => {
|
|
|
220
251
|
hasAttention
|
|
221
252
|
]);
|
|
222
253
|
};
|
|
223
|
-
var useAttentionPath = () => {
|
|
224
|
-
const { path } = useAttentionContext(ATTENTION_NAME);
|
|
225
|
-
return path;
|
|
226
|
-
};
|
|
227
254
|
var RootAttentionProvider = ({ children, attention: propsAttention, onChange }) => {
|
|
228
255
|
const registry = useContext(RegistryContext);
|
|
229
256
|
const attention = useDefaultValue(propsAttention, () => new AttentionManager(registry));
|
|
@@ -231,12 +258,7 @@ var RootAttentionProvider = ({ children, attention: propsAttention, onChange })
|
|
|
231
258
|
log("focus", {
|
|
232
259
|
related: event.relatedTarget,
|
|
233
260
|
target: event.target
|
|
234
|
-
}, {
|
|
235
|
-
F: __dxlog_file,
|
|
236
|
-
L: 130,
|
|
237
|
-
S: void 0,
|
|
238
|
-
C: (f, a) => f(...a)
|
|
239
|
-
});
|
|
261
|
+
}, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 83, S: void 0 });
|
|
240
262
|
const selector = [
|
|
241
263
|
"[data-attendable-id]",
|
|
242
264
|
...Array.from(document.querySelectorAll("[aria-controls]")).map((el) => `[id="${el.getAttribute("aria-controls")}"]`)
|
|
@@ -249,35 +271,20 @@ var RootAttentionProvider = ({ children, attention: propsAttention, onChange })
|
|
|
249
271
|
}
|
|
250
272
|
};
|
|
251
273
|
return /* @__PURE__ */ React.createElement(AttentionContextProvider, {
|
|
252
|
-
attention
|
|
253
|
-
path: []
|
|
274
|
+
attention
|
|
254
275
|
}, /* @__PURE__ */ React.createElement("div", {
|
|
255
276
|
role: "none",
|
|
256
277
|
className: "contents",
|
|
257
278
|
onFocusCapture: handleFocus
|
|
258
279
|
}, children));
|
|
259
280
|
};
|
|
260
|
-
var AttentionProvider = ({ id, children }) => {
|
|
261
|
-
const { attention, path } = useAttentionContext(ATTENTION_NAME);
|
|
262
|
-
const nextPath = useMemo(() => [
|
|
263
|
-
id,
|
|
264
|
-
...path
|
|
265
|
-
], [
|
|
266
|
-
id,
|
|
267
|
-
path
|
|
268
|
-
]);
|
|
269
|
-
return /* @__PURE__ */ React.createElement(AttentionContextProvider, {
|
|
270
|
-
attention,
|
|
271
|
-
path: nextPath
|
|
272
|
-
}, children);
|
|
273
|
-
};
|
|
274
281
|
var AttendableContainer = /* @__PURE__ */ forwardRef(({ id, classNames, children, asChild, ...props }, forwardedRef) => {
|
|
275
282
|
const attentionAttrs = useAttentionAttributes(id);
|
|
276
283
|
const Comp = asChild ? Slot : Primitive.div;
|
|
277
284
|
return /* @__PURE__ */ React.createElement(Comp, {
|
|
278
285
|
role: "none",
|
|
279
|
-
...attentionAttrs,
|
|
280
286
|
...props,
|
|
287
|
+
...attentionAttrs,
|
|
281
288
|
className: mx("dx-attention-surface", props.tabIndex === 0 && "dx-focus-ring-inset-over-all", classNames),
|
|
282
289
|
ref: forwardedRef
|
|
283
290
|
}, children);
|
|
@@ -384,15 +391,7 @@ var SelectionManager = class {
|
|
|
384
391
|
// TODO(burdon): Disambiguate with getSelection?
|
|
385
392
|
getSelected(contextId, mode = "multi") {
|
|
386
393
|
const selection = this.getSelection(contextId, mode);
|
|
387
|
-
invariant(selection?.mode === mode, "Selection mode mismatch", {
|
|
388
|
-
F: __dxlog_file2,
|
|
389
|
-
L: 134,
|
|
390
|
-
S: this,
|
|
391
|
-
A: [
|
|
392
|
-
"selection?.mode === mode",
|
|
393
|
-
"'Selection mode mismatch'"
|
|
394
|
-
]
|
|
395
|
-
});
|
|
394
|
+
invariant(selection?.mode === mode, "Selection mode mismatch", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 105, S: this, A: ["selection?.mode === mode", "'Selection mode mismatch'"] });
|
|
396
395
|
return Match.type().pipe(Match.when({
|
|
397
396
|
mode: "single"
|
|
398
397
|
}, (s) => s.id), Match.when({
|
|
@@ -408,15 +407,7 @@ var SelectionManager = class {
|
|
|
408
407
|
}
|
|
409
408
|
updateSingle(contextId, id) {
|
|
410
409
|
const selection = this.getSelection(contextId, "single");
|
|
411
|
-
invariant(selection?.mode === "single", "Selection mode is not single", {
|
|
412
|
-
F: __dxlog_file2,
|
|
413
|
-
L: 147,
|
|
414
|
-
S: this,
|
|
415
|
-
A: [
|
|
416
|
-
"selection?.mode === 'single'",
|
|
417
|
-
"'Selection mode is not single'"
|
|
418
|
-
]
|
|
419
|
-
});
|
|
410
|
+
invariant(selection?.mode === "single", "Selection mode is not single", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 122, S: this, A: ["selection?.mode === 'single'", "'Selection mode is not single'"] });
|
|
420
411
|
this._registry.update(this._state, (state) => ({
|
|
421
412
|
selections: {
|
|
422
413
|
...state.selections,
|
|
@@ -429,15 +420,7 @@ var SelectionManager = class {
|
|
|
429
420
|
}
|
|
430
421
|
updateMulti(contextId, ids) {
|
|
431
422
|
const selection = this.getSelection(contextId, "multi");
|
|
432
|
-
invariant(selection?.mode === "multi", "Selection mode is not multi", {
|
|
433
|
-
F: __dxlog_file2,
|
|
434
|
-
L: 158,
|
|
435
|
-
S: this,
|
|
436
|
-
A: [
|
|
437
|
-
"selection?.mode === 'multi'",
|
|
438
|
-
"'Selection mode is not multi'"
|
|
439
|
-
]
|
|
440
|
-
});
|
|
423
|
+
invariant(selection?.mode === "multi", "Selection mode is not multi", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 135, S: this, A: ["selection?.mode === 'multi'", "'Selection mode is not multi'"] });
|
|
441
424
|
this._registry.update(this._state, (state) => ({
|
|
442
425
|
selections: {
|
|
443
426
|
...state.selections,
|
|
@@ -452,15 +435,7 @@ var SelectionManager = class {
|
|
|
452
435
|
}
|
|
453
436
|
updateRange(contextId, from, to) {
|
|
454
437
|
const selection = this.getSelection(contextId, "range");
|
|
455
|
-
invariant(selection?.mode === "range", "Selection mode is not range", {
|
|
456
|
-
F: __dxlog_file2,
|
|
457
|
-
L: 169,
|
|
458
|
-
S: this,
|
|
459
|
-
A: [
|
|
460
|
-
"selection?.mode === 'range'",
|
|
461
|
-
"'Selection mode is not range'"
|
|
462
|
-
]
|
|
463
|
-
});
|
|
438
|
+
invariant(selection?.mode === "range", "Selection mode is not range", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 150, S: this, A: ["selection?.mode === 'range'", "'Selection mode is not range'"] });
|
|
464
439
|
this._registry.update(this._state, (state) => ({
|
|
465
440
|
selections: {
|
|
466
441
|
...state.selections,
|
|
@@ -474,15 +449,7 @@ var SelectionManager = class {
|
|
|
474
449
|
}
|
|
475
450
|
updateMultiRange(contextId, ranges) {
|
|
476
451
|
const selection = this.getSelection(contextId, "multi-range");
|
|
477
|
-
invariant(selection?.mode === "multi-range", "Selection mode is not multi-range", {
|
|
478
|
-
F: __dxlog_file2,
|
|
479
|
-
L: 180,
|
|
480
|
-
S: this,
|
|
481
|
-
A: [
|
|
482
|
-
"selection?.mode === 'multi-range'",
|
|
483
|
-
"'Selection mode is not multi-range'"
|
|
484
|
-
]
|
|
485
|
-
});
|
|
452
|
+
invariant(selection?.mode === "multi-range", "Selection mode is not multi-range", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 164, S: this, A: ["selection?.mode === 'multi-range'", "'Selection mode is not multi-range'"] });
|
|
486
453
|
this._registry.update(this._state, (state) => ({
|
|
487
454
|
selections: {
|
|
488
455
|
...state.selections,
|
|
@@ -531,15 +498,7 @@ var SelectionManager = class {
|
|
|
531
498
|
}
|
|
532
499
|
toggleSelection(contextId, id) {
|
|
533
500
|
const selection = this.getSelection(contextId, "multi");
|
|
534
|
-
invariant(selection?.mode === "multi", "Selection mode is not multi", {
|
|
535
|
-
F: __dxlog_file2,
|
|
536
|
-
L: 213,
|
|
537
|
-
S: this,
|
|
538
|
-
A: [
|
|
539
|
-
"selection?.mode === 'multi'",
|
|
540
|
-
"'Selection mode is not multi'"
|
|
541
|
-
]
|
|
542
|
-
});
|
|
501
|
+
invariant(selection?.mode === "multi", "Selection mode is not multi", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 213, S: this, A: ["selection?.mode === 'multi'", "'Selection mode is not multi'"] });
|
|
543
502
|
const newIds = selection.ids.includes(id) ? selection.ids.filter((existingId) => existingId !== id) : [
|
|
544
503
|
...selection.ids,
|
|
545
504
|
id
|
|
@@ -682,6 +641,12 @@ var useSelectionActions = (contextIds, mode = "multi") => {
|
|
|
682
641
|
export {
|
|
683
642
|
AttentionManager,
|
|
684
643
|
getAttendables,
|
|
644
|
+
expandAttendableId,
|
|
645
|
+
isLinkedSegment,
|
|
646
|
+
linkedSegment,
|
|
647
|
+
getLinkedVariant,
|
|
648
|
+
getParentId,
|
|
649
|
+
getSegmentId,
|
|
685
650
|
ATTENTION_NAME,
|
|
686
651
|
ATTENDABLE_ATTRIBUTE,
|
|
687
652
|
ATTENTION_SOURCE_ATTRIBUTE,
|
|
@@ -689,9 +654,7 @@ export {
|
|
|
689
654
|
useAttention,
|
|
690
655
|
useAttended,
|
|
691
656
|
useAttentionAttributes,
|
|
692
|
-
useAttentionPath,
|
|
693
657
|
RootAttentionProvider,
|
|
694
|
-
AttentionProvider,
|
|
695
658
|
AttendableContainer,
|
|
696
659
|
SelectionSchema,
|
|
697
660
|
defaultSelection,
|
|
@@ -702,4 +665,4 @@ export {
|
|
|
702
665
|
useSelected,
|
|
703
666
|
useSelectionActions
|
|
704
667
|
};
|
|
705
|
-
//# sourceMappingURL=chunk-
|
|
668
|
+
//# sourceMappingURL=chunk-DAIZNFKL.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/attention.ts", "../../../src/components/AttentionProvider.tsx", "../../../src/selection.ts", "../../../src/components/SelectionProvider.tsx"],
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { Atom, type Registry } from '@effect-atom/atom-react';\n\nimport { type Attention } from './types';\n\n/**\n * Manages attention state for an application.\n * Attention keys are slash-qualified graph IDs; ancestry is derived from progressive prefixes.\n */\nexport class AttentionManager {\n private readonly _map = new Map<string, Atom.Writable<Attention>>();\n private readonly _currentAtom: Atom.Writable<string[]>;\n\n constructor(\n private readonly _registry: Registry.Registry,\n initial: string[] = [],\n ) {\n this._currentAtom = Atom.make<string[]>([]).pipe(Atom.keepAlive);\n if (initial.length > 0) {\n this.update(initial);\n }\n }\n\n /**\n * Atom for the currently attended element IDs.\n */\n get current(): Atom.Atom<string[]> {\n return this._currentAtom;\n }\n\n /**\n * Gets the currently attended element IDs.\n */\n getCurrent(): readonly string[] {\n return this._registry.get(this._currentAtom);\n }\n\n /**\n * Subscribe to changes in the current attention IDs.\n */\n subscribeCurrent(cb: (current: readonly string[]) => void): () => void {\n this._registry.get(this._currentAtom);\n return this._registry.subscribe(this._currentAtom, () => {\n cb(this._registry.get(this._currentAtom));\n });\n }\n\n /**\n * All tracked qualified IDs.\n */\n keys(): string[] {\n return Array.from(this._map.keys());\n }\n\n /**\n * Get the attention state for a given qualified ID.\n */\n get(id: string): Attention {\n const atom = this._getAtom(id);\n return this._registry.get(atom);\n }\n\n /**\n * Subscribe to changes in the attention state for a given qualified ID.\n */\n subscribe(id: string, cb: (attention: Attention) => void): () => void {\n const atom = this._getAtom(id);\n this._registry.get(atom);\n return this._registry.subscribe(atom, () => {\n cb(this._registry.get(atom));\n });\n }\n\n private _getAtom(id: string): Atom.Writable<Attention> {\n const existing = this._map.get(id);\n if (existing) {\n return existing;\n }\n\n const newAtom = Atom.make<Attention>({ hasAttention: false, isAncestor: false, isRelated: false }).pipe(\n Atom.keepAlive,\n );\n this._map.set(id, newAtom);\n return newAtom;\n }\n\n /**\n * Update the currently attended element.\n * Takes the array of qualified IDs collected from the DOM; the first element is the primary attended item.\n * Ancestry is derived from the progressive prefixes of the primary ID.\n * Relatedness is derived from the segment ID: any tracked key whose last `/` segment matches the\n * attended ID's segment ID is marked `isRelated`. Additionally, if the primary ID is a linked segment\n * (starts with `~`), its immediate parent gets `isRelated` alongside `isAncestor`.\n *\n * @internal\n */\n update(nextIds: string[]): void {\n const primaryId = nextIds[0];\n if (!primaryId) {\n return;\n }\n\n const currentIds = this.getCurrent();\n const prevPrimaryId = currentIds[0];\n // Clear previous attention state: ancestors, primary, and related keys.\n if (prevPrimaryId) {\n const prevPrefixes = expandAttendableId(prevPrimaryId);\n for (const prefix of prevPrefixes) {\n this._set(prefix, {});\n }\n const prevSegmentId = getSegmentId(prevPrimaryId);\n for (const key of this.keys()) {\n if (getSegmentId(key) === prevSegmentId) {\n this._set(key, {});\n }\n }\n }\n\n this._registry.set(this._currentAtom, nextIds);\n\n // Set ancestors and primary.\n const prefixes = expandAttendableId(primaryId);\n const prefixSet = new Set(prefixes);\n const linkedParent = isLinkedSegment(primaryId) ? getParentId(primaryId) : undefined;\n\n for (const prefix of prefixes) {\n if (prefix === primaryId) {\n this._set(prefix, { hasAttention: true });\n } else if (prefix === linkedParent) {\n this._set(prefix, { isAncestor: true, isRelated: true });\n } else {\n this._set(prefix, { isAncestor: true });\n }\n }\n\n // Set related keys: any tracked key sharing the same segment ID.\n const segmentId = getSegmentId(primaryId);\n for (const key of this.keys()) {\n if (!prefixSet.has(key) && getSegmentId(key) === segmentId) {\n this._set(key, { isRelated: true });\n }\n }\n }\n\n private _set(id: string, attention: Partial<Attention>): void {\n const atom = this._getAtom(id);\n this._registry.set(atom, {\n hasAttention: attention.hasAttention ?? false,\n isAncestor: attention.isAncestor ?? false,\n isRelated: attention.isRelated ?? false,\n });\n }\n}\n\n/**\n * Accumulates all attendable IDs between the element provided and the root, inclusive.\n * Each `data-attendable-id` value is treated as a single qualified ID (no splitting).\n */\nexport const getAttendables = (selector: string, cursor: Element, acc: string[] = []): string[] => {\n // Find the closest element with `data-attendable-id`, if any; start from cursor and move up the DOM tree.\n const closestAttendable = cursor.closest(selector);\n if (closestAttendable) {\n const attendableId = closestAttendable.getAttribute('data-attendable-id');\n if (!attendableId) {\n // This has an id of an aria-controls elsewhere on the page, move cursor to that trigger.\n const trigger = document.querySelector(`[aria-controls=\"${closestAttendable.getAttribute('id')}\"]`);\n if (!trigger) {\n return acc;\n } else {\n return getAttendables(selector, trigger, acc);\n }\n } else {\n acc.push(attendableId);\n return !closestAttendable.parentElement ? acc : getAttendables(selector, closestAttendable.parentElement, acc);\n }\n }\n\n return [...new Set(acc)];\n};\n\nexport type AttendableId = { attendableId?: string };\n\nexport type Related = { related?: boolean };\n\n/**\n * Prefix for linked segments. A linked segment's attention state is shared with its parent:\n * when a linked node has attention, the parent is marked as both ancestor and related.\n */\nconst LINKED_PREFIX = '~';\n\n/**\n * Decompose a qualified graph ID into progressive prefixes for ancestry tracking.\n * e.g. `root/a/b/c` yields `['root', 'root/a', 'root/a/b', 'root/a/b/c']`.\n */\nexport const expandAttendableId = (qualifiedId: string): string[] => {\n const segments = qualifiedId.split('/');\n return segments.map((_, index) => segments.slice(0, index + 1).join('/'));\n};\n\n/**\n * Whether the last segment of a qualified ID is a linked segment (starts with `~`).\n * Linked segments share their attention state with the parent node.\n */\nexport const isLinkedSegment = (qualifiedId: string): boolean => {\n const lastSegment = qualifiedId.split('/').pop() ?? '';\n return lastSegment.startsWith(LINKED_PREFIX);\n};\n\n/**\n * Build a linked segment ID from a variant name (e.g., `'settings'` -> `'~settings'`).\n * Nodes identified by linked segments share their attention state with the parent node.\n */\nexport const linkedSegment = (variant: string): string => `${LINKED_PREFIX}${variant}`;\n\n/**\n * Extract the variant name from the last segment of a qualified ID, stripping the linked prefix.\n * e.g. `'root/space/obj/~settings'` -> `'settings'`.\n * If the segment is not linked (no `~` prefix), returns the segment as-is.\n */\nexport const getLinkedVariant = (qualifiedId: string): string => {\n const lastSegment = qualifiedId.split('/').pop() ?? '';\n return lastSegment.startsWith(LINKED_PREFIX) ? lastSegment.slice(LINKED_PREFIX.length) : lastSegment;\n};\n\n/**\n * Get the parent qualified ID (everything before the last `/` segment).\n */\nexport const getParentId = (qualifiedId: string | undefined): string | undefined => {\n if (!qualifiedId) {\n return undefined;\n }\n\n const lastSlash = qualifiedId.lastIndexOf('/');\n return lastSlash > 0 ? qualifiedId.slice(0, lastSlash) : undefined;\n};\n\n/**\n * Extract the segment ID (last segment) of a qualified ID.\n */\nexport const getSegmentId = (qualifiedId: string): string => {\n return qualifiedId.split('/').pop() ?? qualifiedId;\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { RegistryContext } from '@effect-atom/atom-react';\nimport { createContext } from '@radix-ui/react-context';\nimport { Primitive } from '@radix-ui/react-primitive';\nimport { Slot } from '@radix-ui/react-slot';\nimport React, {\n type ComponentPropsWithRef,\n type FocusEvent,\n type PropsWithChildren,\n forwardRef,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nimport { log } from '@dxos/log';\nimport { useDefaultValue } from '@dxos/react-hooks';\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { mx } from '@dxos/ui-theme';\n\nimport { AttentionManager, getAttendables } from '../attention';\nimport { type Attention } from '../types';\n\nconst ATTENTION_NAME = 'Attention';\nconst ATTENDABLE_ATTRIBUTE = 'data-attendable-id';\nconst ATTENTION_SOURCE_ATTRIBUTE = 'data-w-attention-source';\n\ntype AttentionContextValue = {\n attention: AttentionManager;\n};\n\nconst [AttentionContextProvider, useAttentionContext] = createContext<AttentionContextValue>(ATTENTION_NAME, {\n attention: undefined as unknown as AttentionManager,\n});\n\nconst UNKNOWN_ATTENDABLE = { hasAttention: false, isAncestor: false, isRelated: false } as Attention;\n\n/**\n * Subscribe to the attention state for a qualified graph ID.\n */\nconst useAttention = (attendableId?: string): Attention => {\n const { attention } = useAttentionContext(ATTENTION_NAME);\n const [state, setState] = useState<Attention>(UNKNOWN_ATTENDABLE);\n\n useEffect(() => {\n if (!attendableId || !attention) {\n setState(UNKNOWN_ATTENDABLE);\n return;\n }\n\n const currentState = attention.get(attendableId);\n setState(currentState);\n\n return attention.subscribe(attendableId, (newState) => {\n setState(newState);\n });\n }, [attention, attendableId]);\n\n return state;\n};\n\nconst useAttended = () => {\n const { attention } = useAttentionContext(ATTENTION_NAME);\n const [current, setCurrent] = useState<readonly string[]>([]);\n\n useEffect(() => {\n if (!attention) {\n return;\n }\n\n setCurrent(attention.getCurrent());\n\n return attention.subscribeCurrent((newCurrent) => {\n setCurrent(newCurrent);\n });\n }, [attention]);\n\n return current;\n};\n\n/**\n * Computes HTML element attributes to apply so the attention system can detect changes.\n */\nconst useAttentionAttributes = (attendableId?: string) => {\n const { hasAttention } = useAttention(attendableId);\n return useMemo(() => {\n const attributes: Record<string, string | undefined> = { [ATTENDABLE_ATTRIBUTE]: attendableId };\n if (hasAttention) {\n attributes[ATTENTION_SOURCE_ATTRIBUTE] = 'true';\n }\n\n return attributes;\n }, [attendableId, hasAttention]);\n};\n\nconst RootAttentionProvider = ({\n children,\n attention: propsAttention,\n onChange,\n}: PropsWithChildren<\n Partial<{\n attention: AttentionManager;\n onChange: (nextAttended: string[]) => void;\n }>\n>) => {\n const registry = useContext(RegistryContext);\n const attention = useDefaultValue(propsAttention, () => new AttentionManager(registry));\n\n const handleFocus = (event: FocusEvent) => {\n // NOTE(thure): Use the following to debug focus movement across the app:\n log('focus', { related: event.relatedTarget, target: event.target });\n\n const selector = [\n '[data-attendable-id]',\n ...Array.from(document.querySelectorAll('[aria-controls]')).map(\n (el) => `[id=\"${el.getAttribute('aria-controls')}\"]`,\n ),\n ].join(',');\n const prev = attention.getCurrent();\n const next = getAttendables(selector, event.target);\n // TODO(wittjosiah): Not allowing empty state means that the attended item is not strictly guaranteed to be in the DOM.\n // Currently this depends on the deck in order to ensure that when the attended item is removed something else is attended.\n // Only update state if the result is different and not empty.\n if (next.length > 0 && (prev.length !== next.length || !!prev.find((id, index) => next[index] !== id))) {\n attention.update(next);\n onChange?.(next);\n }\n };\n\n return (\n <AttentionContextProvider attention={attention}>\n <div role='none' className='contents' onFocusCapture={handleFocus}>\n {children}\n </div>\n </AttentionContextProvider>\n );\n};\n\nexport type AttendableContainerProps = ThemedClassName<\n ComponentPropsWithRef<'div'> & { id: string; asChild?: boolean }\n>;\n\n/**\n * Note that DeckPlugin and StackPlugin both handle attention on their own,\n * and when rendering content in those cases it is not necessary to also render an `AttendableContainer`.\n * This component is primarily for Storybook stories and other testing scenarios,\n * or the rare cases where an attendable entity is rendered outside of either of those plugins.\n */\nconst AttendableContainer = forwardRef<HTMLDivElement, AttendableContainerProps>(\n ({ id, classNames, children, asChild, ...props }, forwardedRef) => {\n const attentionAttrs = useAttentionAttributes(id);\n const Comp = asChild ? Slot : Primitive.div;\n return (\n <Comp\n role='none'\n {...props}\n {...attentionAttrs}\n className={mx('dx-attention-surface', props.tabIndex === 0 && 'dx-focus-ring-inset-over-all', classNames)}\n ref={forwardedRef}\n >\n {children}\n </Comp>\n );\n },\n);\n\nexport {\n RootAttentionProvider,\n AttendableContainer,\n useAttentionContext,\n useAttention,\n useAttended,\n useAttentionAttributes,\n ATTENTION_NAME,\n ATTENDABLE_ATTRIBUTE,\n ATTENTION_SOURCE_ATTRIBUTE,\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { Atom, type Registry } from '@effect-atom/atom-react';\nimport * as Match from 'effect/Match';\nimport * as Schema from 'effect/Schema';\n\nimport { invariant } from '@dxos/invariant';\n\n// TODO(burdon): Reconcile with @dxos/graph.\n\nexport type SelectionMode = 'single' | 'multi' | 'range' | 'multi-range';\n\nexport const SelectionSchema = Schema.Union(\n Schema.Struct({ mode: Schema.Literal('single'), id: Schema.optional(Schema.String) }).pipe(Schema.mutable),\n Schema.Struct({ mode: Schema.Literal('multi'), ids: Schema.Array(Schema.String).pipe(Schema.mutable) }).pipe(\n Schema.mutable,\n ),\n Schema.Struct({\n mode: Schema.Literal('range'),\n from: Schema.optional(Schema.String),\n to: Schema.optional(Schema.String),\n }).pipe(Schema.mutable),\n Schema.Struct({\n mode: Schema.Literal('multi-range'),\n ranges: Schema.Array(Schema.Struct({ from: Schema.String, to: Schema.String })).pipe(Schema.mutable),\n }).pipe(Schema.mutable),\n).pipe(Schema.mutable);\n\nexport type Selection = Schema.Schema.Type<typeof SelectionSchema>;\n\nexport const defaultSelection = Match.type<SelectionMode>().pipe(\n Match.withReturnType<Selection>(),\n Match.when('single', () => ({ mode: 'single' })),\n Match.when('multi', () => ({ mode: 'multi', ids: [] })),\n Match.when('range', () => ({ mode: 'range' })),\n Match.when('multi-range', () => ({ mode: 'multi-range', ranges: [] })),\n Match.exhaustive,\n);\n\nexport type SelectionResult<T extends SelectionMode> = T extends 'single'\n ? string | undefined\n : T extends 'multi'\n ? string[]\n : T extends 'range'\n ? { from: string; to: string } | undefined\n : T extends 'multi-range'\n ? { from: string; to: string }[]\n : never;\n\n// TODO(burdon): Refactor.\nexport const getSelectionSet = (selectionManager: SelectionManager, contextId?: string) => {\n const ids = new Set<string>(contextId ? [contextId] : []);\n for (const context of selectionManager.getSelectionContexts()) {\n const selection = selectionManager.getSelection(context);\n if (selection?.mode === 'multi') {\n for (const id of selection.ids) {\n ids.add(id);\n }\n }\n }\n\n return ids;\n};\n\n/**\n * Manages selection state for different contexts.\n * Each context maintains its own selection mode and state.\n */\nexport class SelectionManager {\n private readonly _state: Atom.Writable<{ selections: Record<string, Selection> }>;\n\n constructor(\n private readonly _registry: Registry.Registry,\n initial: Record<string, Selection> = {},\n ) {\n this._state = Atom.make<{ selections: Record<string, Selection> }>({\n selections: { ...initial },\n });\n }\n\n /**\n * Get the state atom for reactive access in the graph system.\n */\n get state(): Atom.Atom<{ selections: Record<string, Selection> }> {\n return this._state;\n }\n\n /**\n * Gets the current state.\n */\n getState(): { selections: Record<string, Selection> } {\n return this._registry.get(this._state);\n }\n\n /**\n * Subscribe to changes in the selection state.\n */\n subscribe(cb: (state: { selections: Record<string, Selection> }) => void): () => void {\n this._registry.get(this._state);\n return this._registry.subscribe(this._state, () => {\n cb(this._registry.get(this._state));\n });\n }\n\n getSelectionContexts(): string[] {\n return Object.keys(this._registry.get(this._state).selections);\n }\n\n getSelection(contextId: string): Selection | undefined;\n getSelection<T extends SelectionMode>(contextId: string, mode: T): Selection;\n getSelection<T extends SelectionMode | undefined>(\n contextId: string,\n mode: T = undefined as T,\n ): Selection | undefined {\n const state = this._registry.get(this._state);\n const selection = state.selections[contextId];\n if (!mode || selection) {\n return selection;\n }\n\n // Create new selection for the context.\n const newSelection = defaultSelection(mode);\n this._registry.update(this._state, (state) => ({\n selections: { ...state.selections, [contextId]: newSelection },\n }));\n return newSelection;\n }\n\n // TODO(burdon): Disambiguate with getSelection?\n getSelected<T extends SelectionMode>(contextId: string, mode: T = 'multi' as T): SelectionResult<T> {\n const selection = this.getSelection(contextId, mode);\n invariant(selection?.mode === mode, 'Selection mode mismatch');\n // Cast required because TypeScript can't infer the relationship between T and the matched result.\n return Match.type<Selection>().pipe(\n Match.when({ mode: 'single' }, (s) => s.id),\n Match.when({ mode: 'multi' }, (s) => s.ids),\n Match.when({ mode: 'range' }, (s) => (s.from && s.to ? { from: s.from, to: s.to } : undefined)),\n Match.when({ mode: 'multi-range' }, (s) => s.ranges),\n Match.exhaustive,\n )(selection) as SelectionResult<T>;\n }\n\n updateSingle(contextId: string, id: string): void {\n const selection = this.getSelection(contextId, 'single');\n invariant(selection?.mode === 'single', 'Selection mode is not single');\n this._registry.update(this._state, (state) => ({\n selections: {\n ...state.selections,\n [contextId]: { ...selection, id },\n },\n }));\n }\n\n updateMulti(contextId: string, ids: string[]) {\n const selection = this.getSelection(contextId, 'multi');\n invariant(selection?.mode === 'multi', 'Selection mode is not multi');\n this._registry.update(this._state, (state) => ({\n selections: {\n ...state.selections,\n [contextId]: { ...selection, ids: [...ids] },\n },\n }));\n }\n\n updateRange(contextId: string, from: string, to: string) {\n const selection = this.getSelection(contextId, 'range');\n invariant(selection?.mode === 'range', 'Selection mode is not range');\n this._registry.update(this._state, (state) => ({\n selections: {\n ...state.selections,\n [contextId]: { ...selection, from, to },\n },\n }));\n }\n\n updateMultiRange(contextId: string, ranges: { from: string; to: string }[]) {\n const selection = this.getSelection(contextId, 'multi-range');\n invariant(selection?.mode === 'multi-range', 'Selection mode is not multi-range');\n this._registry.update(this._state, (state) => ({\n selections: {\n ...state.selections,\n [contextId]: { ...selection, ranges: [...ranges] },\n },\n }));\n }\n\n clearSelection(contextId: string): void {\n const selection = this.getSelection(contextId);\n if (!selection) {\n return;\n }\n\n const clearedSelection = Match.type<Selection>().pipe(\n Match.when({ mode: 'single' }, (s) => ({ ...s, id: undefined })),\n Match.when({ mode: 'multi' }, (s) => ({ ...s, ids: [] })),\n Match.when({ mode: 'range' }, (s) => ({ ...s, from: undefined, to: undefined })),\n Match.when({ mode: 'multi-range' }, (s) => ({ ...s, ranges: [] })),\n Match.exhaustive,\n )(selection);\n\n this._registry.update(this._state, (state) => ({\n selections: {\n ...state.selections,\n [contextId]: clearedSelection,\n },\n }));\n }\n\n toggleSelection(contextId: string, id: string): void {\n const selection = this.getSelection(contextId, 'multi');\n invariant(selection?.mode === 'multi', 'Selection mode is not multi');\n\n const newIds = selection.ids.includes(id)\n ? selection.ids.filter((existingId) => existingId !== id)\n : [...selection.ids, id];\n\n this._registry.update(this._state, (state) => ({\n selections: {\n ...state.selections,\n [contextId]: { ...selection, ids: newIds },\n },\n }));\n }\n}\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { RegistryContext } from '@effect-atom/atom-react';\nimport { createContext } from '@radix-ui/react-context';\nimport * as Match from 'effect/Match';\nimport React, { type PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react';\n\nimport { useDefaultValue } from '@dxos/react-hooks';\n\nimport {\n type Selection,\n SelectionManager,\n type SelectionMode,\n type SelectionResult,\n defaultSelection,\n} from '../selection';\n\nconst SELECTION_NAME = 'Selection';\n\ntype SelectionContextValue = {\n selection: SelectionManager;\n};\n\nconst [SelectionContextProvider, useSelectionContext] = createContext<SelectionContextValue>(SELECTION_NAME, {\n selection: undefined as unknown as SelectionManager,\n});\n\n/**\n * Manages selection state across the app for multiple contexts.\n */\n// TODO(burdon): When is the selection removed?\nexport const SelectionProvider = ({\n children,\n selection: propsSelection,\n}: PropsWithChildren<{ selection?: SelectionManager }>) => {\n const registry = useContext(RegistryContext);\n const selection = useDefaultValue(propsSelection, () => new SelectionManager(registry));\n return <SelectionContextProvider selection={selection}>{children}</SelectionContextProvider>;\n};\n\n/**\n * Get the selection contexts.\n */\nexport const useSelectionManager = () => {\n const { selection } = useSelectionContext(SELECTION_NAME);\n return selection;\n};\n\nconst getDefaultResult = <T extends SelectionMode>(mode: T): SelectionResult<T> => {\n return Match.type<Selection>().pipe(\n Match.when({ mode: 'single' }, (selection) => selection.id),\n Match.when({ mode: 'multi' }, (selection) => selection.ids),\n Match.when({ mode: 'range' }, (selection) =>\n selection.from && selection.to ? { from: selection.from, to: selection.to } : undefined,\n ),\n Match.when({ mode: 'multi-range' }, (selection) => selection.ranges),\n Match.exhaustive,\n )(defaultSelection(mode)) as SelectionResult<T>;\n};\n\n/**\n * Get the selected objects for a given context.\n */\nexport const useSelected = <T extends SelectionMode>(\n contextId?: string,\n mode: T = 'multi' as T,\n): SelectionResult<T> => {\n const { selection } = useSelectionContext(SELECTION_NAME);\n const [state, setState] = useState<SelectionResult<T>>(() =>\n contextId && selection ? selection.getSelected(contextId, mode) : getDefaultResult(mode),\n );\n\n useEffect(() => {\n if (!contextId || !selection) {\n setState(getDefaultResult(mode));\n return;\n }\n\n // Set initial state.\n setState(selection.getSelected(contextId, mode));\n\n // Subscribe to changes.\n return selection.subscribe(() => {\n setState(selection.getSelected(contextId, mode));\n });\n }, [selection, contextId, mode]);\n\n return state;\n};\n\nexport type UseSelectionActions = {\n singleSelect: (id: string) => void;\n multiSelect: (ids: string[]) => void;\n rangeSelect: (from: string, to: string) => void;\n toggle: (id: string) => void;\n clear: () => void;\n};\n\n/**\n * Provides functions to manage the selection state for multiple contexts.\n */\n// TODO(burdon): Mode not used.\nexport const useSelectionActions = (contextIds: string[], mode: SelectionMode = 'multi'): UseSelectionActions => {\n const stableContextIds = useMemo(() => contextIds, [JSON.stringify(contextIds)]); // TODO(burdon): Avoid stringify.\n const { selection } = useSelectionContext(SELECTION_NAME);\n\n const singleSelect = useCallback(\n (id: string) => {\n if (!selection) {\n return;\n }\n for (const contextId of stableContextIds) {\n selection.updateSingle(contextId, id);\n }\n },\n [selection, stableContextIds],\n );\n\n const multiSelect = useCallback(\n (ids: string[]) => {\n if (!selection) {\n return;\n }\n for (const contextId of stableContextIds) {\n selection.updateMulti(contextId, ids);\n }\n },\n [selection, stableContextIds],\n );\n\n const rangeSelect = useCallback(\n (from: string, to: string) => {\n if (!selection) {\n return;\n }\n for (const contextId of stableContextIds) {\n selection.updateRange(contextId, from, to);\n }\n },\n [selection, stableContextIds],\n );\n\n const toggle = useCallback(\n (id: string) => {\n if (!selection) {\n return;\n }\n for (const contextId of stableContextIds) {\n selection.toggleSelection(contextId, id);\n }\n },\n [selection, stableContextIds],\n );\n\n const clear = useCallback(() => {\n if (!selection) {\n return;\n }\n for (const contextId of stableContextIds) {\n selection.clearSelection(contextId);\n }\n }, [selection, stableContextIds]);\n\n return { singleSelect, multiSelect, rangeSelect, toggle, clear };\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Atom", "AttentionManager", "_map", "Map", "_currentAtom", "_registry", "initial", "make", "pipe", "keepAlive", "length", "update", "current", "getCurrent", "get", "subscribeCurrent", "cb", "subscribe", "keys", "Array", "from", "id", "atom", "_getAtom", "existing", "newAtom", "hasAttention", "isAncestor", "isRelated", "set", "nextIds", "primaryId", "currentIds", "prevPrimaryId", "prevPrefixes", "expandAttendableId", "prefix", "_set", "prevSegmentId", "getSegmentId", "key", "prefixes", "prefixSet", "Set", "linkedParent", "isLinkedSegment", "getParentId", "undefined", "segmentId", "has", "attention", "getAttendables", "selector", "cursor", "acc", "closestAttendable", "closest", "attendableId", "getAttribute", "trigger", "document", "querySelector", "push", "parentElement", "LINKED_PREFIX", "qualifiedId", "segments", "split", "map", "_", "index", "slice", "join", "lastSegment", "pop", "startsWith", "linkedSegment", "variant", "getLinkedVariant", "lastSlash", "lastIndexOf", "RegistryContext", "createContext", "Primitive", "Slot", "React", "forwardRef", "useContext", "useEffect", "useMemo", "useState", "log", "useDefaultValue", "mx", "ATTENTION_NAME", "ATTENTION_SOURCE_ATTRIBUTE", "AttentionContextProvider", "attention", "undefined", "UNKNOWN_ATTENDABLE", "isAncestor", "isRelated", "setState", "useEffect", "attendableId", "subscribe", "newState", "useAttended", "setCurrent", "newCurrent", "attributes", "ATTENDABLE_ATTRIBUTE", "hasAttention", "useDefaultValue", "propsAttention", "AttentionManager", "registry", "handleFocus", "event", "log", "target", "selector", "join", "prev", "getCurrent", "next", "onChange", "div", "role", "onFocusCapture", "children", "Comp", "attentionAttrs", "className", "ref", "RootAttentionProvider", "Atom", "Match", "Schema", "invariant", "__dxlog_file", "SelectionSchema", "id", "pipe", "mutable", "Struct", "mode", "String", "optional", "to", "Array", "defaultSelection", "when", "ranges", "getSelectionSet", "contextId", "selection", "_state", "make", "selections", "initial", "_registry", "cb", "get", "getSelectionContexts", "getSelection", "undefined", "state", "newSelection", "getSelected", "s", "ids", "from", "exhaustive", "updateSingle", "update", "updateMulti", "updateRange", "updateMultiRange", "clearSelection", "toggleSelection", "newIds", "includes", "RegistryContext", "createContext", "Match", "React", "useCallback", "useContext", "useEffect", "useMemo", "useState", "useDefaultValue", "SELECTION_NAME", "SelectionContextProvider", "useSelectionContext", "createContext", "selection", "undefined", "SelectionProvider", "children", "propsSelection", "registry", "useContext", "RegistryContext", "useDefaultValue", "SelectionManager", "React", "useSelectionManager", "getDefaultResult", "mode", "type", "pipe", "when", "id", "ids", "from", "to", "ranges", "exhaustive", "defaultSelection", "useSelected", "contextId", "state", "setState", "useState", "getSelected", "useEffect", "subscribe", "useSelectionActions", "contextIds", "stableContextIds", "useMemo", "JSON", "stringify", "singleSelect", "useCallback", "updateSingle", "multiSelect", "updateMulti", "rangeSelect", "updateRange", "toggle", "toggleSelection", "clear", "clearSelection"]
|
|
7
|
+
}
|
|
@@ -4,32 +4,33 @@ import {
|
|
|
4
4
|
ATTENTION_SOURCE_ATTRIBUTE,
|
|
5
5
|
AttendableContainer,
|
|
6
6
|
AttentionManager,
|
|
7
|
-
AttentionProvider,
|
|
8
7
|
RootAttentionProvider,
|
|
9
8
|
SelectionManager,
|
|
10
9
|
SelectionProvider,
|
|
11
10
|
SelectionSchema,
|
|
12
11
|
defaultSelection,
|
|
12
|
+
expandAttendableId,
|
|
13
13
|
getAttendables,
|
|
14
|
+
getLinkedVariant,
|
|
15
|
+
getParentId,
|
|
16
|
+
getSegmentId,
|
|
14
17
|
getSelectionSet,
|
|
18
|
+
isLinkedSegment,
|
|
19
|
+
linkedSegment,
|
|
15
20
|
useAttended,
|
|
16
21
|
useAttention,
|
|
17
22
|
useAttentionAttributes,
|
|
18
23
|
useAttentionContext,
|
|
19
|
-
useAttentionPath,
|
|
20
24
|
useSelected,
|
|
21
25
|
useSelectionActions,
|
|
22
26
|
useSelectionManager
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import {
|
|
25
|
-
ATTENDABLE_PATH_SEPARATOR
|
|
26
|
-
} from "./chunk-PAZBZOO2.mjs";
|
|
27
|
+
} from "./chunk-DAIZNFKL.mjs";
|
|
27
28
|
|
|
28
29
|
// src/components/AttentionGlyph.tsx
|
|
29
30
|
import React, { forwardRef, useMemo } from "react";
|
|
30
31
|
import { Icon } from "@dxos/react-ui";
|
|
31
32
|
import { mx } from "@dxos/ui-theme";
|
|
32
|
-
var attentionGlyphStyles = mx("inline-block rounded-xs w-3 h-3 bg-transparent text-accent-text transition-colors", "[[data-contains-attended=true]_&]:bg-attention-
|
|
33
|
+
var attentionGlyphStyles = mx("inline-block rounded-xs w-3 h-3 bg-transparent text-accent-text transition-colors", "[[data-contains-attended=true]_&]:bg-attention-contains", "[[data-attention=true]_&]:bg-accent-surface", "[[data-attention=true]_&]:text-accent-surface-text", "[[aria-current][data-attention=true]_&]:bg-accent-surface", "[[aria-current][data-attention=true]_&]:text-accent-surface-text", '[[aria-selected="true"][data-attention=true]_&]:bg-accent-surface', '[[aria-selected="true"][data-attention=true]_&]:text-accent-surface-text');
|
|
33
34
|
var presenceIconStyles = mx("w-3 h-3");
|
|
34
35
|
var PresenceOne = () => {
|
|
35
36
|
return /* @__PURE__ */ React.createElement("svg", {
|
|
@@ -95,26 +96,29 @@ var AttentionGlyph = /* @__PURE__ */ forwardRef(({ presence, attended, syncing,
|
|
|
95
96
|
});
|
|
96
97
|
export {
|
|
97
98
|
ATTENDABLE_ATTRIBUTE,
|
|
98
|
-
ATTENDABLE_PATH_SEPARATOR,
|
|
99
99
|
ATTENTION_NAME,
|
|
100
100
|
ATTENTION_SOURCE_ATTRIBUTE,
|
|
101
101
|
AttendableContainer,
|
|
102
102
|
AttentionGlyph,
|
|
103
103
|
AttentionManager,
|
|
104
|
-
AttentionProvider,
|
|
105
104
|
RootAttentionProvider,
|
|
106
105
|
SelectionManager,
|
|
107
106
|
SelectionProvider,
|
|
108
107
|
SelectionSchema,
|
|
109
108
|
Syncing,
|
|
110
109
|
defaultSelection,
|
|
110
|
+
expandAttendableId,
|
|
111
111
|
getAttendables,
|
|
112
|
+
getLinkedVariant,
|
|
113
|
+
getParentId,
|
|
114
|
+
getSegmentId,
|
|
112
115
|
getSelectionSet,
|
|
116
|
+
isLinkedSegment,
|
|
117
|
+
linkedSegment,
|
|
113
118
|
useAttended,
|
|
114
119
|
useAttention,
|
|
115
120
|
useAttentionAttributes,
|
|
116
121
|
useAttentionContext,
|
|
117
|
-
useAttentionPath,
|
|
118
122
|
useSelected,
|
|
119
123
|
useSelectionActions,
|
|
120
124
|
useSelectionManager
|