flexdesk 0.4.7 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1036 -301
- package/css/base.css +2958 -2
- package/css/flexdesk.css +2962 -4
- package/css/overrides.css +4 -2
- package/css/tokens.css +86 -0
- package/dist/canvas.js +390 -0
- package/dist/canvas.js.map +7 -0
- package/dist/charts.js +782 -782
- package/dist/{chunk-LH5TSOZW.js → chunk-6JSOVNID.js} +42 -2
- package/dist/{chunk-LH5TSOZW.js.map → chunk-6JSOVNID.js.map} +3 -3
- package/dist/{chunk-ELXVW542.js → chunk-6NG7YVBG.js} +3 -471
- package/dist/chunk-6NG7YVBG.js.map +7 -0
- package/dist/{chunk-F5FYDCOA.js → chunk-CNXP3SIN.js} +2373 -2373
- package/dist/{chunk-F5FYDCOA.js.map → chunk-CNXP3SIN.js.map} +7 -7
- package/dist/chunk-H4LT43RS.js +475 -0
- package/dist/chunk-H4LT43RS.js.map +7 -0
- package/dist/{chunk-WMGOJ3I6.js → chunk-KH2GJTKR.js} +3702 -2628
- package/dist/chunk-KH2GJTKR.js.map +7 -0
- package/dist/chunk-SN4LSOAR.js +532 -0
- package/dist/chunk-SN4LSOAR.js.map +7 -0
- package/dist/flexdesk.css +2962 -4
- package/dist/flow.js +10030 -0
- package/dist/flow.js.map +7 -0
- package/dist/tiles.js +2195 -2195
- package/dist/tokens.css +86 -0
- package/dist/widgets.js +5824 -5796
- package/dist/widgets.js.map +1 -1
- package/dist/wm.js +8077 -8074
- package/dist/wm.js.map +7 -7
- package/package.json +89 -81
- package/src/canvas/canvas_adapter.js +523 -0
- package/src/canvas/edge_router.js +392 -0
- package/src/canvas/graph/connector_router.js +189 -0
- package/src/canvas/graph/history_service.js +170 -0
- package/src/canvas/graph/node_platform.js +116 -0
- package/src/canvas/index.js +16 -0
- package/src/flow/canvas_editor/editor.js +1081 -0
- package/src/flow/canvas_editor/index.js +16 -0
- package/src/flow/canvas_editor/layout.js +118 -0
- package/src/flow/canvas_editor/strings.js +37 -0
- package/src/flow/kit/always_before.js +325 -0
- package/src/flow/kit/catalogue.js +170 -0
- package/src/flow/kit/chip_input.js +461 -0
- package/src/flow/kit/dom.js +205 -0
- package/src/flow/kit/findings.js +129 -0
- package/src/flow/kit/history.js +173 -0
- package/src/flow/kit/index.js +38 -0
- package/src/flow/kit/keys.js +127 -0
- package/src/flow/kit/logic_graph.js +210 -0
- package/src/flow/kit/references.js +199 -0
- package/src/flow/kit/settings_panel.js +357 -0
- package/src/flow/kit/settings_schema.js +175 -0
- package/src/flow/kit/step_picker.js +209 -0
- package/src/flow/kit/strings.js +102 -0
- package/src/flow/kit/value_picker.js +238 -0
- package/src/flow/kit/widgets.js +763 -0
- package/src/flow/lanes/editor.js +1469 -0
- package/src/flow/lanes/index.js +26 -0
- package/src/flow/lanes/layout.js +525 -0
- package/src/flow/lanes/pipeline.js +327 -0
- package/src/flow/lanes/preview.js +109 -0
- package/src/flow/lanes/strings.js +98 -0
- package/src/flow/outline/editor.js +1511 -0
- package/src/flow/outline/index.js +20 -0
- package/src/flow/outline/mapping.js +286 -0
- package/src/flow/outline/menu.js +154 -0
- package/src/flow/outline/operations.js +738 -0
- package/src/flow/outline/recognise.js +755 -0
- package/src/flow/outline/strings.js +135 -0
- package/src/tiling/landing_table.js +13 -5
- package/src/tiling/wm.js +3916 -3916
- package/src/ui/components/data_table.js +4205 -3294
- package/src/ui/components/row_activation.js +70 -0
- package/src/ui/components/table_values.js +423 -0
- package/dist/chunk-ELXVW542.js.map +0 -7
- package/dist/chunk-WMGOJ3I6.js.map +0 -7
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* INSERT A VALUE — the picker a field's "{ }" opens (36 §3.7).
|
|
3
|
+
*
|
|
4
|
+
* openValuePicker({anchor, groups, note, onPick, strings});
|
|
5
|
+
* // groups: [{id, label, sub, items, layout?: 'grid'|'chips', empty?}]
|
|
6
|
+
* // item: {label, type, ref, children?} `ref` is the TEXT inserted
|
|
7
|
+
*
|
|
8
|
+
* The kit DRAWS; the groups are the consumer's — which values each step
|
|
9
|
+
* offers, in its own words — computed over the steps that always run before
|
|
10
|
+
* the field's step (`alwaysBefore`), so a value the run may not have is not
|
|
11
|
+
* offered. `note` says so under the list in the consumer's words.
|
|
12
|
+
*
|
|
13
|
+
* An item with `children` opens one level down ("body ›"): its first option
|
|
14
|
+
* there inserts the whole value, and Back (or Escape) climbs again. A group
|
|
15
|
+
* drawn as a `grid` lists its items two to a row with their type beside them
|
|
16
|
+
* (a loop's row, column by column); any other group draws chips. A group with
|
|
17
|
+
* no items shows its `empty` sentence instead, so "none yet, and why" is on
|
|
18
|
+
* the screen. The search box filters every level at once.
|
|
19
|
+
*
|
|
20
|
+
* Returns a promise of the picked item (or null), carrying `close()`; `onPick`
|
|
21
|
+
* is called with the item as well. The line under the list says what the
|
|
22
|
+
* active option inserts — "shown as a chip, kept as text".
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { el, icon, nextOption, openPopover, uid } from './dom.js';
|
|
26
|
+
import { createStrings, say } from './strings.js';
|
|
27
|
+
|
|
28
|
+
const words = (s) => String(s ?? '').toLowerCase();
|
|
29
|
+
|
|
30
|
+
export function openValuePicker({ anchor, groups = [], note = null, onPick = null, strings = null,
|
|
31
|
+
title = null } = {}) {
|
|
32
|
+
const S = createStrings(strings);
|
|
33
|
+
let settle;
|
|
34
|
+
const done = new Promise((resolve) => { settle = resolve; });
|
|
35
|
+
let result = null;
|
|
36
|
+
const trail = []; // the items drilled into
|
|
37
|
+
|
|
38
|
+
const root = el('div', 'twm-flow-values');
|
|
39
|
+
const headingId = uid('values-title');
|
|
40
|
+
const heading = el('span', 'twm-flow-values__title', title ?? say(S, 'insertValue'));
|
|
41
|
+
heading.id = headingId;
|
|
42
|
+
const head = el('div', 'twm-flow-values__head');
|
|
43
|
+
head.appendChild(heading);
|
|
44
|
+
const listId = uid('values-list');
|
|
45
|
+
const searchRow = el('div', 'twm-flow-values__search');
|
|
46
|
+
searchRow.appendChild(icon('search', 'twm-flow-values__search-icon'));
|
|
47
|
+
const search = el('input', 'twm-flow-values__input');
|
|
48
|
+
search.type = 'search';
|
|
49
|
+
search.placeholder = say(S, 'searchValues');
|
|
50
|
+
search.setAttribute('aria-label', say(S, 'searchValues'));
|
|
51
|
+
search.setAttribute('role', 'combobox');
|
|
52
|
+
search.setAttribute('aria-controls', listId);
|
|
53
|
+
search.setAttribute('aria-expanded', 'true');
|
|
54
|
+
search.setAttribute('aria-autocomplete', 'list');
|
|
55
|
+
search.autocomplete = 'off';
|
|
56
|
+
searchRow.appendChild(search);
|
|
57
|
+
head.appendChild(searchRow);
|
|
58
|
+
root.appendChild(head);
|
|
59
|
+
|
|
60
|
+
const crumb = el('div', 'twm-flow-values__crumb');
|
|
61
|
+
crumb.hidden = true;
|
|
62
|
+
root.appendChild(crumb);
|
|
63
|
+
const list = el('div', 'twm-flow-values__list');
|
|
64
|
+
list.id = listId;
|
|
65
|
+
list.setAttribute('role', 'listbox');
|
|
66
|
+
list.setAttribute('aria-labelledby', headingId);
|
|
67
|
+
root.appendChild(list);
|
|
68
|
+
const foot = el('div', 'twm-flow-values__foot');
|
|
69
|
+
const inserts = el('span', 'twm-flow-values__inserts');
|
|
70
|
+
inserts.setAttribute('aria-live', 'polite');
|
|
71
|
+
if (note) foot.appendChild(el('span', 'twm-flow-values__note', note));
|
|
72
|
+
foot.appendChild(inserts);
|
|
73
|
+
root.appendChild(foot);
|
|
74
|
+
|
|
75
|
+
let active = null;
|
|
76
|
+
let shown = []; // [{opt, item, whole?}]
|
|
77
|
+
|
|
78
|
+
function activate(opt) {
|
|
79
|
+
active?.setAttribute('aria-selected', 'false');
|
|
80
|
+
active?.classList.remove('twm-flow-values__option--active');
|
|
81
|
+
active = opt;
|
|
82
|
+
const hit = shown.find((s) => s.opt === opt);
|
|
83
|
+
if (opt) {
|
|
84
|
+
opt.setAttribute('aria-selected', 'true');
|
|
85
|
+
opt.classList.add('twm-flow-values__option--active');
|
|
86
|
+
search.setAttribute('aria-activedescendant', opt.id);
|
|
87
|
+
opt.scrollIntoView?.({ block: 'nearest' });
|
|
88
|
+
} else {
|
|
89
|
+
search.removeAttribute('aria-activedescendant');
|
|
90
|
+
}
|
|
91
|
+
inserts.textContent = hit && hit.item?.ref !== undefined && !(hit.item.children?.length && !hit.whole && !search.value)
|
|
92
|
+
? say(S, 'insertsLine', hit.item.ref) : '';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function option(item, { grid = false, whole = false, label = null } = {}) {
|
|
96
|
+
const opens = !whole && Array.isArray(item.children) && item.children.length > 0;
|
|
97
|
+
const opt = el('div', `twm-flow-values__option${grid ? ' twm-flow-values__option--row' : ' twm-flow-values__option--chip'}`);
|
|
98
|
+
opt.id = uid('values-option');
|
|
99
|
+
opt.setAttribute('role', 'option');
|
|
100
|
+
opt.setAttribute('aria-selected', 'false');
|
|
101
|
+
const text = label ?? (whole ? say(S, 'wholeValue', item.label) : item.label);
|
|
102
|
+
opt.appendChild(el('span', 'twm-flow-values__label', opens && !search.value ? `${text} ›` : text));
|
|
103
|
+
if (grid && item.type) opt.appendChild(el('span', 'twm-flow-values__type', item.type));
|
|
104
|
+
if (opens) opt.setAttribute('aria-haspopup', 'listbox');
|
|
105
|
+
opt.addEventListener('mousedown', (ev) => ev.preventDefault());
|
|
106
|
+
opt.addEventListener('click', () => take(item, { whole }));
|
|
107
|
+
opt.addEventListener('mousemove', () => { if (active !== opt) activate(opt); });
|
|
108
|
+
shown.push({ opt, item, whole });
|
|
109
|
+
return opt;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function matches(item, terms) {
|
|
113
|
+
const hay = [item.label, item.type, item.ref].map(words).join(' ');
|
|
114
|
+
return terms.every((t) => hay.includes(t));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Every descendant of `items`, with its labels joined by ›, for a search. */
|
|
118
|
+
function flatten(items, prefix = '') {
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const it of items || []) {
|
|
121
|
+
const label = prefix ? `${prefix} › ${it.label}` : it.label;
|
|
122
|
+
out.push({ item: it, label });
|
|
123
|
+
if (Array.isArray(it.children)) out.push(...flatten(it.children, label));
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function paint() {
|
|
129
|
+
list.replaceChildren();
|
|
130
|
+
shown = [];
|
|
131
|
+
active = null;
|
|
132
|
+
const terms = words(search.value).split(/\s+/).filter(Boolean);
|
|
133
|
+
const level = trail[trail.length - 1] ?? null;
|
|
134
|
+
crumb.hidden = !level;
|
|
135
|
+
crumb.replaceChildren();
|
|
136
|
+
if (level) {
|
|
137
|
+
const back = el('button', 'twm-flow-values__back');
|
|
138
|
+
back.type = 'button';
|
|
139
|
+
back.appendChild(icon('chevron_left'));
|
|
140
|
+
back.appendChild(el('span', '', say(S, 'back')));
|
|
141
|
+
back.addEventListener('mousedown', (ev) => ev.preventDefault());
|
|
142
|
+
back.addEventListener('click', climb);
|
|
143
|
+
crumb.append(back, el('span', 'twm-flow-values__path', trail.map((t) => t.label).join(' › ')));
|
|
144
|
+
const box = el('div', 'twm-flow-values__chips');
|
|
145
|
+
if (!terms.length || matches(level, terms)) box.appendChild(option(level, { whole: true }));
|
|
146
|
+
for (const it of level.children || []) {
|
|
147
|
+
if (terms.length && !matches(it, terms)) continue;
|
|
148
|
+
box.appendChild(option(it));
|
|
149
|
+
}
|
|
150
|
+
list.appendChild(box);
|
|
151
|
+
} else {
|
|
152
|
+
for (const g of groups) {
|
|
153
|
+
const group = el('div', 'twm-flow-values__group');
|
|
154
|
+
group.setAttribute('role', 'group');
|
|
155
|
+
const gid = uid('values-group');
|
|
156
|
+
const label = el('div', 'twm-flow-values__group-label');
|
|
157
|
+
label.id = gid;
|
|
158
|
+
label.appendChild(el('span', 'twm-flow-values__group-name', g.label ?? ''));
|
|
159
|
+
if (g.sub) label.appendChild(el('span', 'twm-flow-values__group-sub', g.sub));
|
|
160
|
+
group.setAttribute('aria-labelledby', gid);
|
|
161
|
+
const items = Array.isArray(g.items) ? g.items : [];
|
|
162
|
+
const grid = g.layout === 'grid';
|
|
163
|
+
const box = el('div', grid ? 'twm-flow-values__grid' : 'twm-flow-values__chips');
|
|
164
|
+
if (terms.length) {
|
|
165
|
+
for (const { item, label: path } of flatten(items)) {
|
|
166
|
+
if (matches(item, terms) || words(path).includes(terms.join(' '))) {
|
|
167
|
+
box.appendChild(option(item, { grid, label: path }));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!box.childNodes.length) continue;
|
|
171
|
+
} else if (!items.length) {
|
|
172
|
+
if (g.empty) label.appendChild(el('span', 'twm-flow-values__group-empty', `— ${g.empty}`));
|
|
173
|
+
group.appendChild(label);
|
|
174
|
+
list.appendChild(group);
|
|
175
|
+
continue;
|
|
176
|
+
} else {
|
|
177
|
+
for (const it of items) box.appendChild(option(it, { grid }));
|
|
178
|
+
}
|
|
179
|
+
group.append(label, box);
|
|
180
|
+
list.appendChild(group);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!shown.length) list.appendChild(el('p', 'twm-flow-values__empty', terms.length
|
|
184
|
+
? say(S, 'nothingMatches', search.value.trim()) : say(S, 'noValues')));
|
|
185
|
+
activate(nextOption(list, null, 1));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function take(item, { whole = false } = {}) {
|
|
189
|
+
const opens = !whole && Array.isArray(item.children) && item.children.length > 0;
|
|
190
|
+
if (opens && !search.value) {
|
|
191
|
+
trail.push(item);
|
|
192
|
+
paint();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
result = item;
|
|
196
|
+
onPick?.(item);
|
|
197
|
+
handle.close('chosen');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function climb() {
|
|
201
|
+
trail.pop();
|
|
202
|
+
paint();
|
|
203
|
+
search.focus({ preventScroll: true });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
search.addEventListener('input', paint);
|
|
207
|
+
search.addEventListener('keydown', (ev) => {
|
|
208
|
+
if (ev.isComposing) return;
|
|
209
|
+
if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') {
|
|
210
|
+
ev.preventDefault();
|
|
211
|
+
activate(nextOption(list, active, ev.key === 'ArrowDown' ? 1 : -1) || active);
|
|
212
|
+
} else if (ev.key === 'Enter') {
|
|
213
|
+
ev.preventDefault();
|
|
214
|
+
const hit = shown.find((s) => s.opt === active);
|
|
215
|
+
if (hit) take(hit.item, { whole: hit.whole });
|
|
216
|
+
} else if (ev.key === 'ArrowRight' && !search.value) {
|
|
217
|
+
const hit = shown.find((s) => s.opt === active);
|
|
218
|
+
if (hit && !hit.whole && hit.item.children?.length) { ev.preventDefault(); take(hit.item); }
|
|
219
|
+
} else if (ev.key === 'Escape' && trail.length) {
|
|
220
|
+
ev.preventDefault();
|
|
221
|
+
ev.stopPropagation();
|
|
222
|
+
climb();
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
root.addEventListener('focusout', (ev) => {
|
|
226
|
+
const to = ev.relatedTarget;
|
|
227
|
+
if (to && !root.contains(to) && to !== anchor) handle.close('blur');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const handle = openPopover({
|
|
231
|
+
anchor, content: root, label: title ?? say(S, 'insertValue'), className: 'twm-flow-popover--values',
|
|
232
|
+
focus: search, onClose: () => settle(result),
|
|
233
|
+
});
|
|
234
|
+
if (handle.toggled) settle(null);
|
|
235
|
+
else paint();
|
|
236
|
+
done.close = () => handle.close('closed');
|
|
237
|
+
return done;
|
|
238
|
+
}
|