flexdesk 0.17.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +221 -3
- package/canvas.js +18 -0
- package/charts.js +21 -0
- package/core.js +14 -0
- package/css/flexdesk.css +24 -0
- package/dist/charts.js +3 -5
- package/dist/charts.js.map +1 -1
- package/dist/{chunk-Q36EPT2R.js → chunk-LWV23YVE.js} +1614 -138
- package/dist/chunk-LWV23YVE.js.map +7 -0
- package/dist/{chunk-MCIXFNVF.js → chunk-RMTRWWEO.js} +2 -2
- package/dist/flexdesk.css +24 -0
- package/dist/flow.js +2 -2
- package/dist/flow.js.map +2 -2
- package/dist/tiles.js +2 -2
- package/dist/widgets.js +43 -5
- package/dist/widgets.js.map +1 -1
- package/editor.js +20 -0
- package/flow.js +21 -0
- package/host.js +14 -0
- package/package.json +10 -1
- package/src/flow/canvas_editor/editor.js +1 -1
- package/src/ui/components/cell_editor_host.js +100 -18
- package/src/ui/components/cell_editors.js +480 -0
- package/src/ui/components/cell_grid.js +121 -8
- package/src/ui/components/cell_paste.js +243 -0
- package/src/ui/components/data_table.js +96 -10
- package/src/ui/components/staged_edits.js +19 -1
- package/src/ui/components/tsv.js +74 -0
- package/tiles.js +27 -0
- package/widgets.js +69 -0
- package/wm.js +28 -0
- package/dist/chunk-Q36EPT2R.js.map +0 -7
- package/dist/chunk-QWQJUDPD.js +0 -610
- package/dist/chunk-QWQJUDPD.js.map +0 -7
- /package/dist/{chunk-MCIXFNVF.js.map → chunk-RMTRWWEO.js.map} +0 -0
package/editor.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flexdesk/editor — A Monaco-backed multi-file editor: the loader, the editor factory, the
|
|
3
|
+
* tab bar, search, a split-pane container and an undo manager.
|
|
4
|
+
*
|
|
5
|
+
* Monaco is a PEER dependency — 14 MB this bundle deliberately does NOT
|
|
6
|
+
* contain, and whose location it refuses to guess. Call setMonacoBasePath()
|
|
7
|
+
* and setDefaultLanguage().
|
|
8
|
+
*
|
|
9
|
+
* GENERATED by scripts/gen_barrels.py. Do not hand-edit.
|
|
10
|
+
*
|
|
11
|
+
* 7 modules.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export * from './src/editor/editor_pane.js';
|
|
15
|
+
export * from './src/editor/monaco_editor_factory.js';
|
|
16
|
+
export * from './src/editor/monaco_loader.js';
|
|
17
|
+
export * from './src/editor/notebook_search.js';
|
|
18
|
+
export * from './src/editor/notebook_tab_bar.js';
|
|
19
|
+
export * from './src/editor/notebook_undo_manager.js';
|
|
20
|
+
export * from './src/editor/split_pane_container.js';
|
package/flow.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flexdesk/flow — flow editors: the shared kit (a step-type catalogue, a
|
|
3
|
+
* settings panel generated from a JSON-Schema subset, a widget registry,
|
|
4
|
+
* reference chips, the step and value pickers, findings, undo over whole
|
|
5
|
+
* snapshots, keys) and the three editors built on it — the canvas, the outline
|
|
6
|
+
* and the lanes.
|
|
7
|
+
*
|
|
8
|
+
* Every editor is domain-neutral: it never fetches, never saves and never
|
|
9
|
+
* names a consumer's vocabulary. The step types, the reference grammar, every
|
|
10
|
+
* finding and every byte of I/O are the consumer's, passed in.
|
|
11
|
+
*
|
|
12
|
+
* HAND-WRITTEN, not generated. The other barrels say "GENERATED by
|
|
13
|
+
* scripts/gen_barrels.py", and that script is not in this repository, so this
|
|
14
|
+
* one is kept by hand. Each line below is one owner's module (36 §2): the kit
|
|
15
|
+
* is laid down first, and each editor fills its own index.js.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export * from './src/flow/kit/index.js';
|
|
19
|
+
export * from './src/flow/canvas_editor/index.js';
|
|
20
|
+
export * from './src/flow/outline/index.js';
|
|
21
|
+
export * from './src/flow/lanes/index.js';
|
package/host.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flexdesk/host — The host port. The library never touches pywebview, fetch or localStorage
|
|
3
|
+
* directly — it asks a Host, and the application supplies one.
|
|
4
|
+
*
|
|
5
|
+
* GENERATED by scripts/gen_barrels.py. Do not hand-edit.
|
|
6
|
+
*
|
|
7
|
+
* 5 modules.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export * from './src/host/host.js';
|
|
11
|
+
export * from './src/host/pywebview_host.js';
|
|
12
|
+
export * from './src/host/photino_host.js';
|
|
13
|
+
export * from './src/host/external_links.js';
|
|
14
|
+
export * from './src/host/file_picker.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flexdesk",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.1",
|
|
4
4
|
"description": "A tiling window manager and widget set for desktop-class web applications. Zero runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist",
|
|
20
20
|
"src",
|
|
21
|
+
"core.js",
|
|
22
|
+
"host.js",
|
|
23
|
+
"wm.js",
|
|
24
|
+
"widgets.js",
|
|
25
|
+
"charts.js",
|
|
26
|
+
"tiles.js",
|
|
27
|
+
"editor.js",
|
|
28
|
+
"canvas.js",
|
|
29
|
+
"flow.js",
|
|
21
30
|
"css",
|
|
22
31
|
"README.md",
|
|
23
32
|
"CHANGELOG.md",
|
|
@@ -2888,7 +2888,7 @@ export function createCanvasEditor(host, options = {}) {
|
|
|
2888
2888
|
for (const c of g.connections) {
|
|
2889
2889
|
const source = idOf.get(c.source);
|
|
2890
2890
|
const target = idOf.get(c.target);
|
|
2891
|
-
if (source && target) connect(graph, catalogue, { ...c, source, target }, { strings: S });
|
|
2891
|
+
if (source && target) connect(graph, catalogue, { ...c, source, target }, { strings: S, dataPorts: dataConnect });
|
|
2892
2892
|
}
|
|
2893
2893
|
const noteIds = [];
|
|
2894
2894
|
for (const from of noting ? g.notes || [] : []) {
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
* it likes (DataTable's `editors` option); the plain text box below is the
|
|
7
7
|
* default. A factory is called with
|
|
8
8
|
*
|
|
9
|
-
* { value, row, rowIdx, colIdx, column: {index, header, type},
|
|
10
|
-
* initialText, commit(value), cancel() }
|
|
9
|
+
* { ...args, value, row, rowIdx, colIdx, column: {index, header, type},
|
|
10
|
+
* initialText, commit(value, move?), cancel() }
|
|
11
11
|
*
|
|
12
|
-
* and returns either an element, or
|
|
12
|
+
* and returns either an element, or
|
|
13
|
+
* `{ el, getValue?, parse?, multiline?, focus?, destroy? }`.
|
|
13
14
|
* `initialText` is the character typed to open the editor (null when it was
|
|
14
15
|
* opened by F2, Enter or a double-click): the editor starts with it in place
|
|
15
16
|
* of the value, as a spreadsheet does.
|
|
@@ -29,6 +30,20 @@
|
|
|
29
30
|
* pressed in the editor stops there: the table's own keys (the arrows, Ctrl+A,
|
|
30
31
|
* Ctrl+C) are not the editor's.
|
|
31
32
|
*
|
|
33
|
+
* ══ A VALUE THAT DOES NOT PARSE (STORY-0087) ═══════════════════════════
|
|
34
|
+
*
|
|
35
|
+
* An editor with a `parse()` — `() => {ok: true, value} | {ok: false, error}`
|
|
36
|
+
* — commits what `parse()` reads, not `getValue()`. A refusal on Enter or Tab
|
|
37
|
+
* keeps the editor open, and its cell carries the message
|
|
38
|
+
* (`twm-dt-cell--error`, `title`, `data-error`) until the next keystroke in
|
|
39
|
+
* it; nothing is committed. A refusal when the focus leaves, or on `commit()`
|
|
40
|
+
* from outside, closes the editor and hands `onRefuse` the message and the
|
|
41
|
+
* text (`getValue()`); still nothing is committed. A `multiline` editor
|
|
42
|
+
* keeps Enter for a new line: Ctrl/Cmd+Enter commits it.
|
|
43
|
+
*
|
|
44
|
+
* `commit(value, move?)` takes an optional move ('down', 'up', 'right',
|
|
45
|
+
* 'left'), for an editor that commits on a key of its own.
|
|
46
|
+
*
|
|
32
47
|
* ══ EXACTLY ONCE ═════════════════════════════════════════════════════════
|
|
33
48
|
*
|
|
34
49
|
* A session closes once. Escape's own repaint takes the focused input out of
|
|
@@ -73,14 +88,29 @@ function normalise(made) {
|
|
|
73
88
|
let getValue = typeof own.getValue === 'function' ? own.getValue : null;
|
|
74
89
|
// A form field's value is the editor's; any other element says for itself.
|
|
75
90
|
if (!getValue && el.matches?.('input, select, textarea')) getValue = () => el.value;
|
|
91
|
+
const parse = typeof own.parse === 'function' ? own.parse : null;
|
|
92
|
+
// What a commit reads: the parse, else the value as it is.
|
|
93
|
+
let read = null;
|
|
94
|
+
if (parse) {
|
|
95
|
+
read = () => {
|
|
96
|
+
let r;
|
|
97
|
+
try { r = parse(); } catch (err) { r = { ok: false, error: err?.message || String(err) }; }
|
|
98
|
+
return r && r.ok === false ? { ok: false, error: String(r.error ?? 'Not valid') }
|
|
99
|
+
: { ok: true, value: r ? r.value : undefined };
|
|
100
|
+
};
|
|
101
|
+
} else if (getValue) read = () => ({ ok: true, value: getValue() });
|
|
76
102
|
return {
|
|
77
103
|
el,
|
|
78
104
|
getValue,
|
|
105
|
+
read,
|
|
106
|
+
multiline: !!own.multiline,
|
|
79
107
|
focus: typeof own.focus === 'function' ? own.focus : null,
|
|
80
108
|
destroy: typeof own.destroy === 'function' ? own.destroy : null,
|
|
81
109
|
};
|
|
82
110
|
}
|
|
83
111
|
|
|
112
|
+
const MOVES = new Set(['down', 'up', 'right', 'left']);
|
|
113
|
+
|
|
84
114
|
export class CellEditorHost {
|
|
85
115
|
/**
|
|
86
116
|
* @param {object} o
|
|
@@ -88,19 +118,24 @@ export class CellEditorHost {
|
|
|
88
118
|
* cell is drawn as it was again; `move` is 'down', 'up', 'right', 'left'
|
|
89
119
|
* or null; `reason` is 'key', 'blur', 'editor' or 'scroll'.
|
|
90
120
|
* @param {(session, reason) => void} o.onCancel
|
|
121
|
+
* @param {(session, error, text, reason) => void} [o.onRefuse] a value
|
|
122
|
+
* that did not parse, when the editor closed with it (the focus left,
|
|
123
|
+
* `commit()` from outside); without it such a close is a cancel.
|
|
91
124
|
*/
|
|
92
|
-
constructor({ onCommit, onCancel }) {
|
|
125
|
+
constructor({ onCommit, onCancel, onRefuse = null }) {
|
|
93
126
|
this._onCommit = onCommit;
|
|
94
127
|
this._onCancel = onCancel;
|
|
128
|
+
this._onRefuse = onRefuse;
|
|
95
129
|
/** The open session, or null. */
|
|
96
130
|
this.session = null;
|
|
97
131
|
}
|
|
98
132
|
|
|
99
133
|
/**
|
|
100
|
-
* Open an editor in `td
|
|
134
|
+
* Open an editor in `td`; `args` go to the factory beside the cell's own
|
|
135
|
+
* (DataTable's `locale`, `utc`, `messages`). Returns whether one opened (a factory that
|
|
101
136
|
* throws or returns nothing opens none, and the cell is untouched).
|
|
102
137
|
*/
|
|
103
|
-
open({ td, factory, value, row, rowIdx, colIdx, column, initialText = null, ...extra }) {
|
|
138
|
+
open({ td, factory, value, row, rowIdx, colIdx, column, initialText = null, args = {}, ...extra }) {
|
|
104
139
|
if (this.session) return false;
|
|
105
140
|
const s = {
|
|
106
141
|
...extra, td, value, row, rowIdx, colIdx, column,
|
|
@@ -109,8 +144,8 @@ export class CellEditorHost {
|
|
|
109
144
|
let made = null;
|
|
110
145
|
try {
|
|
111
146
|
made = factory({
|
|
112
|
-
value, row, rowIdx, colIdx, column, initialText,
|
|
113
|
-
commit: (v) => this._finish(s, v, null, 'editor'),
|
|
147
|
+
...args, value, row, rowIdx, colIdx, column, initialText,
|
|
148
|
+
commit: (v, move = null) => this._finish(s, v, MOVES.has(move) ? move : null, 'editor'),
|
|
114
149
|
cancel: () => this._cancel(s, 'editor'),
|
|
115
150
|
});
|
|
116
151
|
} catch (err) {
|
|
@@ -122,6 +157,10 @@ export class CellEditorHost {
|
|
|
122
157
|
s.editor = editor;
|
|
123
158
|
s.saved = [...td.childNodes];
|
|
124
159
|
s.savedTitle = td.getAttribute('title');
|
|
160
|
+
s.savedError = {
|
|
161
|
+
on: td.classList.contains('twm-dt-cell--error'),
|
|
162
|
+
message: td.getAttribute('data-error'),
|
|
163
|
+
};
|
|
125
164
|
this.session = s;
|
|
126
165
|
td.classList.add('twm-dt-cell--editing');
|
|
127
166
|
td.replaceChildren(editor.el);
|
|
@@ -136,26 +175,29 @@ export class CellEditorHost {
|
|
|
136
175
|
if (ev.key === 'Escape') {
|
|
137
176
|
ev.preventDefault();
|
|
138
177
|
this._cancel(s, 'key');
|
|
139
|
-
} else if (ev.key === 'Enter' && !ev.altKey &&
|
|
178
|
+
} else if (ev.key === 'Enter' && !ev.altKey && editor.read
|
|
179
|
+
&& (editor.multiline ? (ev.ctrlKey || ev.metaKey) : !(ev.ctrlKey || ev.metaKey))) {
|
|
140
180
|
ev.preventDefault();
|
|
141
|
-
this.
|
|
142
|
-
} else if (ev.key === 'Tab' && !ev.altKey && !ev.ctrlKey && !ev.metaKey && editor.
|
|
181
|
+
this._tryKey(s, ev.shiftKey ? 'up' : 'down');
|
|
182
|
+
} else if (ev.key === 'Tab' && !ev.altKey && !ev.ctrlKey && !ev.metaKey && editor.read) {
|
|
143
183
|
ev.preventDefault();
|
|
144
|
-
this.
|
|
184
|
+
this._tryKey(s, ev.shiftKey ? 'left' : 'right');
|
|
145
185
|
}
|
|
146
186
|
});
|
|
187
|
+
// A refusal's message stands until the text changes.
|
|
188
|
+
on('input', () => this._unmark(s));
|
|
147
189
|
// A press inside the editor is the editor's, not a click on the cell.
|
|
148
190
|
for (const type of ['click', 'dblclick', 'mousedown', 'contextmenu']) {
|
|
149
191
|
on(type, (ev) => ev.stopPropagation());
|
|
150
192
|
}
|
|
151
193
|
on('focusout', (ev) => {
|
|
152
|
-
if (s.closed || !editor.
|
|
194
|
+
if (s.closed || !editor.read) return;
|
|
153
195
|
const to = ev.relatedTarget;
|
|
154
196
|
if (to && editor.el.contains(to)) return;
|
|
155
197
|
// The window losing focus (another app) is not a click elsewhere.
|
|
156
198
|
const doc = editor.el.ownerDocument;
|
|
157
199
|
if (!to && doc && typeof doc.hasFocus === 'function' && !doc.hasFocus()) return;
|
|
158
|
-
this.
|
|
200
|
+
this._settle(s, null, 'blur');
|
|
159
201
|
});
|
|
160
202
|
|
|
161
203
|
try {
|
|
@@ -169,8 +211,49 @@ export class CellEditorHost {
|
|
|
169
211
|
commit(move = null, reason = 'editor') {
|
|
170
212
|
const s = this.session;
|
|
171
213
|
if (!s) return false;
|
|
172
|
-
if (!s.editor.
|
|
173
|
-
return this.
|
|
214
|
+
if (!s.editor.read) { this._cancel(s, reason); return false; }
|
|
215
|
+
return this._settle(s, move, reason);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Enter or Tab: commit what parses; a refusal keeps the editor open,
|
|
219
|
+
* the message on its cell. */
|
|
220
|
+
_tryKey(s, move) {
|
|
221
|
+
const r = s.editor.read();
|
|
222
|
+
if (r.ok) return this._finish(s, r.value, move, 'key');
|
|
223
|
+
s.td.classList.add('twm-dt-cell--error');
|
|
224
|
+
s.td.setAttribute('data-error', r.error);
|
|
225
|
+
s.td.setAttribute('title', r.error);
|
|
226
|
+
s.marked = true;
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_unmark(s) {
|
|
231
|
+
if (!s.marked) return;
|
|
232
|
+
s.marked = false;
|
|
233
|
+
this._restoreMark(s);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
_restoreMark(s) {
|
|
237
|
+
const { on, message } = s.savedError;
|
|
238
|
+
s.td.classList.toggle('twm-dt-cell--error', on);
|
|
239
|
+
if (message == null) s.td.removeAttribute('data-error');
|
|
240
|
+
else s.td.setAttribute('data-error', message);
|
|
241
|
+
if (s.savedTitle == null) s.td.removeAttribute('title');
|
|
242
|
+
else s.td.setAttribute('title', s.savedTitle);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** The editor closing with what it holds (the focus left, `commit()`):
|
|
246
|
+
* committed when it parses, else handed to `onRefuse`. */
|
|
247
|
+
_settle(s, move, reason) {
|
|
248
|
+
const r = s.editor.read();
|
|
249
|
+
if (r.ok) return this._finish(s, r.value, move, reason);
|
|
250
|
+
if (s.closed) return false;
|
|
251
|
+
let text = '';
|
|
252
|
+
try { text = s.editor.getValue ? s.editor.getValue() : ''; } catch (_) { /* gone */ }
|
|
253
|
+
this._close(s);
|
|
254
|
+
if (this._onRefuse) this._onRefuse(s, r.error, text, reason);
|
|
255
|
+
else this._onCancel?.(s, reason);
|
|
256
|
+
return true;
|
|
174
257
|
}
|
|
175
258
|
|
|
176
259
|
/** Cancel the open session: the cell is drawn as it was. */
|
|
@@ -191,8 +274,7 @@ export class CellEditorHost {
|
|
|
191
274
|
}
|
|
192
275
|
s.td.classList.remove('twm-dt-cell--editing');
|
|
193
276
|
s.td.replaceChildren(...s.saved);
|
|
194
|
-
|
|
195
|
-
else s.td.setAttribute('title', s.savedTitle);
|
|
277
|
+
this._restoreMark(s);
|
|
196
278
|
}
|
|
197
279
|
|
|
198
280
|
_finish(s, value, move, reason) {
|