@grafloria/element 0.3.23 → 0.4.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 +7 -7
- package/package.json +7 -9
- package/src/index.d.ts +16 -16
- package/src/index.js +58 -339
- package/src/lib/dashboard-kit/dashboard.d.ts +1 -1
- package/src/lib/dashboard-kit/dashboard.js +22 -26
- package/src/lib/dashboard-kit/grid-binder.d.ts +1 -1
- package/src/lib/dashboard-kit/grid-binder.js +62 -65
- package/src/lib/dashboard-kit/grid-mapping.js +14 -26
- package/src/lib/dashboard-kit/index.d.ts +5 -5
- package/src/lib/dashboard-kit/index.js +5 -30
- package/src/lib/dashboard-kit/styles.js +4 -8
- package/src/lib/dashboard-kit/widgets.d.ts +1 -1
- package/src/lib/dashboard-kit/widgets.js +16 -26
- package/src/lib/diagram-kit/card.d.ts +2 -2
- package/src/lib/diagram-kit/card.js +22 -32
- package/src/lib/diagram-kit/editing.js +15 -20
- package/src/lib/diagram-kit/er.d.ts +1 -1
- package/src/lib/diagram-kit/er.js +15 -18
- package/src/lib/diagram-kit/handles.d.ts +4 -4
- package/src/lib/diagram-kit/handles.js +16 -29
- package/src/lib/diagram-kit/index.d.ts +10 -10
- package/src/lib/diagram-kit/index.js +10 -50
- package/src/lib/diagram-kit/join-guidance.js +9 -18
- package/src/lib/diagram-kit/rows.js +1 -4
- package/src/lib/diagram-kit/styles.js +4 -8
- package/src/lib/diagram-kit/uml.js +10 -13
- package/src/lib/diagram-kit/update.d.ts +1 -1
- package/src/lib/diagram-kit/update.js +19 -26
- package/src/lib/grafloria-flow-element.js +19 -24
- package/src/lib/grafloria.d.ts +4 -4
- package/src/lib/grafloria.js +12 -17
- package/src/lib/load.d.ts +3 -3
- package/src/lib/load.js +19 -22
- package/src/lib/node-type-registry.js +6 -14
- package/src/lib/stencil-kit/builders.js +4 -10
- package/src/lib/stencil-kit/card-builders.js +14 -17
- package/src/lib/stencil-kit/index.d.ts +7 -7
- package/src/lib/stencil-kit/index.js +6 -16
- package/src/lib/stencil-kit/palette.js +14 -17
- package/src/lib/stencil-kit/shape-data.js +25 -28
- package/src/lib/stencil-kit/styles.js +1 -4
- package/esm/src/index.js +0 -295
- package/esm/src/lib/dashboard-kit/dashboard.js +0 -590
- package/esm/src/lib/dashboard-kit/grid-binder.js +0 -1633
- package/esm/src/lib/dashboard-kit/grid-mapping.js +0 -164
- package/esm/src/lib/dashboard-kit/index.js +0 -8
- package/esm/src/lib/dashboard-kit/styles.js +0 -205
- package/esm/src/lib/dashboard-kit/widgets.js +0 -379
- package/esm/src/lib/diagram-kit/card.js +0 -199
- package/esm/src/lib/diagram-kit/editing.js +0 -448
- package/esm/src/lib/diagram-kit/er.js +0 -160
- package/esm/src/lib/diagram-kit/handles.js +0 -312
- package/esm/src/lib/diagram-kit/index.js +0 -15
- package/esm/src/lib/diagram-kit/join-guidance.js +0 -322
- package/esm/src/lib/diagram-kit/rows.js +0 -144
- package/esm/src/lib/diagram-kit/styles.js +0 -118
- package/esm/src/lib/diagram-kit/uml.js +0 -135
- package/esm/src/lib/diagram-kit/update.js +0 -244
- package/esm/src/lib/grafloria-flow-element.js +0 -266
- package/esm/src/lib/grafloria.js +0 -69
- package/esm/src/lib/load.js +0 -252
- package/esm/src/lib/node-type-registry.js +0 -42
- package/esm/src/lib/stencil-kit/builders.js +0 -18
- package/esm/src/lib/stencil-kit/card-builders.js +0 -105
- package/esm/src/lib/stencil-kit/index.js +0 -9
- package/esm/src/lib/stencil-kit/palette.js +0 -427
- package/esm/src/lib/stencil-kit/shape-data.js +0 -537
- package/esm/src/lib/stencil-kit/styles.js +0 -126
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The shape-data panel — Visio's "Shape Data" window, grown into the full
|
|
3
|
-
* properties panel Visio users actually expect.
|
|
4
|
-
*
|
|
5
|
-
* T9 built the dataSchema form: read the selected node's schema, render its
|
|
6
|
-
* fields, write them back through `SetNodeDataCommand`. The audit then held it
|
|
7
|
-
* against Visio and found the gap: a plain node said "This shape has no data
|
|
8
|
-
* fields.", a selected EDGE said "Select a shape…", multi-select said "Select a
|
|
9
|
-
* single shape." — but a Visio user gets Name, Size & Position and Format for
|
|
10
|
-
* EVERYTHING selected. So now:
|
|
11
|
-
*
|
|
12
|
-
* one node → Shape (Name), Size & Position (X/Y/W/H), Format (fill, line,
|
|
13
|
-
* width, dash, corner radius on rect silhouettes) — then the
|
|
14
|
-
* master's dataSchema fields, unchanged, below.
|
|
15
|
-
* a kit card → Size & Position, then the kit Table/Column (or Class) sections
|
|
16
|
-
* EXACTLY as before. No Format: an ER/UML card paints
|
|
17
|
-
* `fill:none` shells on purpose — its look belongs to the kit.
|
|
18
|
-
* one edge → Label, Line, Arrows (the engine marker vocabulary), Route.
|
|
19
|
-
* N > 1 → "N shapes" plus a Format section that restyles the whole
|
|
20
|
-
* selection as ONE undo entry.
|
|
21
|
-
*
|
|
22
|
-
* Every write goes through a command (SetNodeLabel / Move / Resize /
|
|
23
|
-
* SetNodeStyle / SetNodeShapeConfig / UpdateLinkStyle / SetLinkDisplayLabel /
|
|
24
|
-
* SetLinkPathType, batched for multi) so every edit is undoable and
|
|
25
|
-
* collab-safe. The new controls use their own `gf-sd-ctl` class — the legacy
|
|
26
|
-
* `.gf-sd-input` selector contract (first match = first dataSchema field; the
|
|
27
|
-
* kit card's Name rows) is load-bearing for existing demos and gates.
|
|
28
|
-
*/
|
|
29
|
-
import { BatchCommand, MoveNodeCommand, ResizeNodeCommand, SetLinkDisplayLabelCommand, SetLinkPathTypeCommand, SetNodeDataCommand, SetNodeLabelCommand, SetNodeShapeConfigCommand, SetNodeStyleCommand, UpdateLinkStyleCommand, } from '@grafloria/engine';
|
|
30
|
-
import { erTable, umlClass } from '../diagram-kit';
|
|
31
|
-
import { ensureStencilKitStyles } from './styles';
|
|
32
|
-
/** The schema a node's shape data follows, via the master it was stamped from. */
|
|
33
|
-
function schemaFor(engine, node) {
|
|
34
|
-
var _a, _b, _c, _d;
|
|
35
|
-
const templateId = (_a = node === null || node === void 0 ? void 0 : node.getMetadata) === null || _a === void 0 ? void 0 : _a.call(node, 'templateId');
|
|
36
|
-
if (!templateId)
|
|
37
|
-
return null;
|
|
38
|
-
const master = (_c = (_b = engine === null || engine === void 0 ? void 0 : engine.templateRegistry) === null || _b === void 0 ? void 0 : _b.get) === null || _c === void 0 ? void 0 : _c.call(_b, templateId);
|
|
39
|
-
const props = (_d = master === null || master === void 0 ? void 0 : master.dataSchema) === null || _d === void 0 ? void 0 : _d.properties;
|
|
40
|
-
return props && typeof props === 'object' ? props : null;
|
|
41
|
-
}
|
|
42
|
-
/** Turn `unitCost` / `unit_cost` into "Unit cost" for the field label. */
|
|
43
|
-
function humanize(key) {
|
|
44
|
-
const spaced = key.replace(/[_-]+/g, ' ').replace(/([a-z0-9])([A-Z])/g, '$1 $2');
|
|
45
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
46
|
-
}
|
|
47
|
-
/** `<input type=color>` accepts ONLY #rrggbb — normalise or fall back. */
|
|
48
|
-
function toHexColor(value, fallback) {
|
|
49
|
-
if (typeof value === 'string') {
|
|
50
|
-
const v = value.trim();
|
|
51
|
-
if (/^#[0-9a-f]{6}$/i.test(v))
|
|
52
|
-
return v.toLowerCase();
|
|
53
|
-
const short = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i.exec(v);
|
|
54
|
-
if (short)
|
|
55
|
-
return `#${short[1]}${short[1]}${short[2]}${short[2]}${short[3]}${short[3]}`.toLowerCase();
|
|
56
|
-
}
|
|
57
|
-
return fallback;
|
|
58
|
-
}
|
|
59
|
-
/** The engine's built-in marker vocabulary (ArrowStyle.type literals). */
|
|
60
|
-
const ARROW_TYPES = [
|
|
61
|
-
'none', 'arrow', 'circle', 'square', 'diamond',
|
|
62
|
-
'hollow-diamond', 'filled-diamond', 'generalization', 'open-arrow', 'double-arrow',
|
|
63
|
-
'crow-foot', 'one', 'zero-or-one', 'zero-or-many', 'one-or-many',
|
|
64
|
-
'cross', 'bar', 'dot', 'oval', 'half-arrow-left', 'half-arrow-right',
|
|
65
|
-
];
|
|
66
|
-
const ROUTE_TYPES = ['smooth', 'orthogonal', 'direct', 'bezier'];
|
|
67
|
-
/** Dash pattern the "Dashed" checkbox writes. */
|
|
68
|
-
const DASH = '6 4';
|
|
69
|
-
const PANEL_STYLE_ID = 'grafloria-shapedata-panel-styles';
|
|
70
|
-
/**
|
|
71
|
-
* The NEW controls' css, kept panel-local: `styles.ts` belongs to the stencil
|
|
72
|
-
* rail/palette work, and the legacy `.gf-sd-*` classes it defines are a shared
|
|
73
|
-
* selector contract this file must not churn.
|
|
74
|
-
*/
|
|
75
|
-
function ensureShapeDataPanelStyles(doc = document) {
|
|
76
|
-
if (doc.getElementById(PANEL_STYLE_ID))
|
|
77
|
-
return;
|
|
78
|
-
const style = doc.createElement('style');
|
|
79
|
-
style.id = PANEL_STYLE_ID;
|
|
80
|
-
style.textContent = `
|
|
81
|
-
.gf-sd-ctl {
|
|
82
|
-
padding: 6px 8px; border: 1px solid var(--gf-st-line, #e5e7eb); border-radius: 7px;
|
|
83
|
-
background: var(--gf-st-bg, #fff); color: var(--gf-st-ink, #1e2436);
|
|
84
|
-
font: inherit; outline: none; width: 100%; box-sizing: border-box;
|
|
85
|
-
}
|
|
86
|
-
.gf-sd-ctl:focus { border-color: var(--gf-st-accent, #3B52D9); }
|
|
87
|
-
.gf-sd-color {
|
|
88
|
-
width: 100%; height: 28px; padding: 1px 2px; box-sizing: border-box; cursor: pointer;
|
|
89
|
-
border: 1px solid var(--gf-st-line, #e5e7eb); border-radius: 7px; background: var(--gf-st-bg, #fff);
|
|
90
|
-
}
|
|
91
|
-
.gf-sd-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
|
|
92
|
-
.gf-sd-inline { display: flex; flex-direction: row; align-items: center; gap: 8px; }
|
|
93
|
-
.gf-sd-inline .gf-sd-label { flex: 1; }
|
|
94
|
-
.gf-sd-count { padding: 12px 12px 0; font-weight: 600; font-size: 12px; }
|
|
95
|
-
`;
|
|
96
|
-
doc.head.appendChild(style);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Bind a shape-data panel into `host`. It follows the diagram's selection:
|
|
100
|
-
* nodes, edges and multi-selections each get their own sections; nothing
|
|
101
|
-
* selected shows the empty message.
|
|
102
|
-
*/
|
|
103
|
-
export function bindShapeDataPanel(api, host, options = {}) {
|
|
104
|
-
var _a, _b, _c;
|
|
105
|
-
const doc = (_a = host.ownerDocument) !== null && _a !== void 0 ? _a : document;
|
|
106
|
-
ensureStencilKitStyles(doc);
|
|
107
|
-
ensureShapeDataPanelStyles(doc);
|
|
108
|
-
host.classList.add('gf-shapedata');
|
|
109
|
-
const title = (_b = options.title) !== null && _b !== void 0 ? _b : 'Shape data';
|
|
110
|
-
const emptyText = (_c = options.emptyText) !== null && _c !== void 0 ? _c : 'Select a shape to edit its data.';
|
|
111
|
-
/** Execute a command, then rebuild so clamped/normalised values show. */
|
|
112
|
-
function exec(cmd) {
|
|
113
|
-
var _a, _b;
|
|
114
|
-
const engine = api.getEngine();
|
|
115
|
-
Promise.resolve((_b = (_a = engine === null || engine === void 0 ? void 0 : engine.commandManager) === null || _a === void 0 ? void 0 : _a.execute) === null || _b === void 0 ? void 0 : _b.call(_a, cmd)).then(() => render(), () => render());
|
|
116
|
-
}
|
|
117
|
-
function render() {
|
|
118
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
119
|
-
host.innerHTML = '';
|
|
120
|
-
const head = document.createElement('div');
|
|
121
|
-
head.className = 'gf-sd-title';
|
|
122
|
-
head.textContent = title;
|
|
123
|
-
host.appendChild(head);
|
|
124
|
-
const diagram = api.getModel();
|
|
125
|
-
const engine = api.getEngine();
|
|
126
|
-
const nodes = ((_b = (_a = diagram === null || diagram === void 0 ? void 0 : diagram.getSelectedNodes) === null || _a === void 0 ? void 0 : _a.call(diagram)) !== null && _b !== void 0 ? _b : []);
|
|
127
|
-
const links = ((_d = (_c = diagram === null || diagram === void 0 ? void 0 : diagram.getLinks) === null || _c === void 0 ? void 0 : _c.call(diagram)) !== null && _d !== void 0 ? _d : []).filter((l) => (l === null || l === void 0 ? void 0 : l.state) === 'selected');
|
|
128
|
-
const total = nodes.length + links.length;
|
|
129
|
-
if (total === 0) {
|
|
130
|
-
const empty = document.createElement('div');
|
|
131
|
-
empty.className = 'gf-sd-empty';
|
|
132
|
-
empty.textContent = emptyText;
|
|
133
|
-
host.appendChild(empty);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (total > 1) {
|
|
137
|
-
renderMulti(nodes, links);
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
if (links.length === 1) {
|
|
141
|
-
renderLink(links[0]);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const node = nodes[0];
|
|
145
|
-
const isKit = !!(((_e = node.getMetadata) === null || _e === void 0 ? void 0 : _e.call(node, 'kitEntity')) || ((_f = node.getMetadata) === null || _f === void 0 ? void 0 : _f.call(node, 'kitClass')));
|
|
146
|
-
renderNodeSections(node, isKit);
|
|
147
|
-
// KIT CARDS keep their own sections. An ER entity / UML class is not a
|
|
148
|
-
// schema-shaped bag of scalars — it is a card the diagram kit already owns,
|
|
149
|
-
// with a live handle API (`erTable(api,id)`) and a row-selection event.
|
|
150
|
-
// Driving that is the whole point: it is the same surface `erd-editor`
|
|
151
|
-
// edits, so the panel and the canvas can never disagree. The dataSchema
|
|
152
|
-
// path below stays for plain template masters.
|
|
153
|
-
if (isKit) {
|
|
154
|
-
renderKitCard(node);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
const props = schemaFor(engine, node);
|
|
158
|
-
if (!props || Object.keys(props).length === 0)
|
|
159
|
-
return;
|
|
160
|
-
const form = document.createElement('div');
|
|
161
|
-
form.className = 'gf-sd-fields';
|
|
162
|
-
for (const [key, prop] of Object.entries(props)) {
|
|
163
|
-
const row = document.createElement('label');
|
|
164
|
-
row.className = 'gf-sd-row';
|
|
165
|
-
const label = document.createElement('span');
|
|
166
|
-
label.className = 'gf-sd-label';
|
|
167
|
-
label.textContent = (_g = prop.title) !== null && _g !== void 0 ? _g : humanize(key);
|
|
168
|
-
if (prop.description)
|
|
169
|
-
row.title = prop.description;
|
|
170
|
-
row.appendChild(label);
|
|
171
|
-
const current = (_k = (_j = (_h = node.getData) === null || _h === void 0 ? void 0 : _h.call(node, key)) !== null && _j !== void 0 ? _j : prop.default) !== null && _k !== void 0 ? _k : '';
|
|
172
|
-
// Commit through the command so the edit is undoable and collab-safe.
|
|
173
|
-
const commit = (value) => {
|
|
174
|
-
var _a;
|
|
175
|
-
void engine.commandManager.execute(new SetNodeDataCommand(node.id, { [key]: value }));
|
|
176
|
-
(_a = options.onEdit) === null || _a === void 0 ? void 0 : _a.call(options, { nodeId: node.id, key, value });
|
|
177
|
-
};
|
|
178
|
-
let field;
|
|
179
|
-
if (Array.isArray(prop.enum) && prop.enum.length > 0) {
|
|
180
|
-
const sel = document.createElement('select');
|
|
181
|
-
sel.className = 'gf-sd-input';
|
|
182
|
-
for (const opt of prop.enum) {
|
|
183
|
-
const o = document.createElement('option');
|
|
184
|
-
o.value = String(opt);
|
|
185
|
-
o.textContent = String(opt);
|
|
186
|
-
if (String(opt) === String(current))
|
|
187
|
-
o.selected = true;
|
|
188
|
-
sel.appendChild(o);
|
|
189
|
-
}
|
|
190
|
-
sel.addEventListener('change', () => commit(sel.value));
|
|
191
|
-
field = sel;
|
|
192
|
-
}
|
|
193
|
-
else if (prop.type === 'boolean') {
|
|
194
|
-
const box = document.createElement('input');
|
|
195
|
-
box.type = 'checkbox';
|
|
196
|
-
box.className = 'gf-sd-check';
|
|
197
|
-
box.checked = current === true || current === 'true';
|
|
198
|
-
box.addEventListener('change', () => commit(box.checked));
|
|
199
|
-
field = box;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
const input = document.createElement('input');
|
|
203
|
-
const numeric = prop.type === 'number' || prop.type === 'integer';
|
|
204
|
-
input.type = numeric ? 'number' : 'text';
|
|
205
|
-
input.className = 'gf-sd-input';
|
|
206
|
-
input.value = current === undefined || current === null ? '' : String(current);
|
|
207
|
-
// Commit on change (blur / Enter), not per keystroke: one undo entry per edit.
|
|
208
|
-
input.addEventListener('change', () => {
|
|
209
|
-
var _a, _b;
|
|
210
|
-
if (numeric) {
|
|
211
|
-
const n = input.value === '' ? null : Number(input.value);
|
|
212
|
-
if (n !== null && Number.isNaN(n)) {
|
|
213
|
-
input.value = String((_b = (_a = node.getData) === null || _a === void 0 ? void 0 : _a.call(node, key)) !== null && _b !== void 0 ? _b : '');
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
commit(n);
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
commit(input.value);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
input.addEventListener('keydown', (e) => {
|
|
223
|
-
e.stopPropagation(); // keep canvas keys out of the field
|
|
224
|
-
if (e.key === 'Enter')
|
|
225
|
-
input.blur();
|
|
226
|
-
});
|
|
227
|
-
field = input;
|
|
228
|
-
}
|
|
229
|
-
row.appendChild(field);
|
|
230
|
-
form.appendChild(row);
|
|
231
|
-
}
|
|
232
|
-
host.appendChild(form);
|
|
233
|
-
}
|
|
234
|
-
// ── the Visio sections every node gets ────────────────────────────────────
|
|
235
|
-
/** Smallest size a resize may produce — the gesture path's floor + the node's own minimum. */
|
|
236
|
-
function minSize(node) {
|
|
237
|
-
var _a, _b, _c, _d;
|
|
238
|
-
const sizing = ((_b = (_a = node.getMetadata) === null || _a === void 0 ? void 0 : _a.call(node, 'sizing')) !== null && _b !== void 0 ? _b : {});
|
|
239
|
-
return { w: Math.max(16, (_c = sizing.minWidth) !== null && _c !== void 0 ? _c : 0), h: Math.max(16, (_d = sizing.minHeight) !== null && _d !== void 0 ? _d : 0) };
|
|
240
|
-
}
|
|
241
|
-
function renderNodeSections(node, isKit) {
|
|
242
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
243
|
-
const form = document.createElement('div');
|
|
244
|
-
form.className = 'gf-sd-fields';
|
|
245
|
-
if (!isKit) {
|
|
246
|
-
form.appendChild(sectionLabel('Shape'));
|
|
247
|
-
form.appendChild(ctlTextField('Name', String((_b = (_a = node.getLabel) === null || _a === void 0 ? void 0 : _a.call(node)) !== null && _b !== void 0 ? _b : ''), (v) => exec(new SetNodeLabelCommand(node.id, v))));
|
|
248
|
-
}
|
|
249
|
-
form.appendChild(sectionLabel('Size & Position'));
|
|
250
|
-
const move = (x, y) => { var _a; return exec(new MoveNodeCommand(node.id, { x, y, z: (_a = node.position) === null || _a === void 0 ? void 0 : _a.z }, undefined, { mergeable: false })); };
|
|
251
|
-
const resize = (w, h) => {
|
|
252
|
-
var _a;
|
|
253
|
-
const min = minSize(node);
|
|
254
|
-
exec(new ResizeNodeCommand(node.id, {
|
|
255
|
-
width: Math.max(min.w, w),
|
|
256
|
-
height: Math.max(min.h, h),
|
|
257
|
-
depth: (_a = node.size) === null || _a === void 0 ? void 0 : _a.depth,
|
|
258
|
-
}));
|
|
259
|
-
};
|
|
260
|
-
form.appendChild(pair(numberField('X', () => { var _a, _b; return (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0; }, (n) => { var _a, _b; return move(n, (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : 0); }), numberField('Y', () => { var _a, _b; return (_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.y) !== null && _b !== void 0 ? _b : 0; }, (n) => { var _a, _b; return move((_b = (_a = node.position) === null || _a === void 0 ? void 0 : _a.x) !== null && _b !== void 0 ? _b : 0, n); })));
|
|
261
|
-
form.appendChild(pair(numberField('W', () => { var _a, _b; return (_b = (_a = node.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0; }, (n) => { var _a, _b; return resize(n, (_b = (_a = node.size) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 0); }), numberField('H', () => { var _a, _b; return (_b = (_a = node.size) === null || _a === void 0 ? void 0 : _a.height) !== null && _b !== void 0 ? _b : 0; }, (n) => { var _a, _b; return resize((_b = (_a = node.size) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 0, n); })));
|
|
262
|
-
// NO Format for kit cards: their shells are `fill:none` ON PURPOSE — the
|
|
263
|
-
// card's look belongs to the kit, and a panel fill write would repaint the
|
|
264
|
-
// transparent shell into a solid slab over the rows.
|
|
265
|
-
if (!isKit) {
|
|
266
|
-
const shape = ((_d = (_c = node.getMetadata) === null || _c === void 0 ? void 0 : _c.call(node, 'shape')) !== null && _d !== void 0 ? _d : {});
|
|
267
|
-
const style = ((_e = node.style) !== null && _e !== void 0 ? _e : {});
|
|
268
|
-
form.appendChild(sectionLabel('Format'));
|
|
269
|
-
form.appendChild(colorField('Fill', toHexColor((_f = style['fill']) !== null && _f !== void 0 ? _f : shape['fill'], '#ffffff'), (v) => exec(new SetNodeStyleCommand(node.id, { fill: v }))));
|
|
270
|
-
form.appendChild(colorField('Line', toHexColor((_g = style['stroke']) !== null && _g !== void 0 ? _g : shape['stroke'], '#333333'), (v) => exec(new SetNodeStyleCommand(node.id, { stroke: v }))));
|
|
271
|
-
form.appendChild(numberField('Line width', () => { var _a, _b; return (_b = (_a = style['strokeWidth']) !== null && _a !== void 0 ? _a : shape['strokeWidth']) !== null && _b !== void 0 ? _b : 1; }, (n) => exec(new SetNodeStyleCommand(node.id, { strokeWidth: Math.max(0, n) }))));
|
|
272
|
-
form.appendChild(checkField('Dashed', !!style['strokeDasharray'], (c) => exec(new SetNodeStyleCommand(node.id, { strokeDasharray: c ? DASH : undefined }))));
|
|
273
|
-
// Corner radius only where the silhouette HAS corners: the rect family.
|
|
274
|
-
const silhouette = (_h = shape['type']) !== null && _h !== void 0 ? _h : 'rect';
|
|
275
|
-
if (silhouette === 'rect') {
|
|
276
|
-
form.appendChild(numberField('Corner radius', () => { var _a, _b; return (_b = (_a = style['borderRadius']) !== null && _a !== void 0 ? _a : shape['cornerRadius']) !== null && _b !== void 0 ? _b : 0; }, (n) => {
|
|
277
|
-
const r = Math.max(0, n);
|
|
278
|
-
// shape.cornerRadius is GEOMETRY and defers over the style-borne
|
|
279
|
-
// borderRadius — on masters that ship it (BPMN tasks), the style
|
|
280
|
-
// write would not paint. Edit the key that does.
|
|
281
|
-
exec(shape['cornerRadius'] !== undefined
|
|
282
|
-
? new SetNodeShapeConfigCommand(node.id, { cornerRadius: r })
|
|
283
|
-
: new SetNodeStyleCommand(node.id, { borderRadius: r }));
|
|
284
|
-
}));
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
host.appendChild(form);
|
|
288
|
-
}
|
|
289
|
-
// ── one selected edge ─────────────────────────────────────────────────────
|
|
290
|
-
function renderLink(link) {
|
|
291
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
292
|
-
const form = document.createElement('div');
|
|
293
|
-
form.className = 'gf-sd-fields';
|
|
294
|
-
const style = ((_a = link.style) !== null && _a !== void 0 ? _a : {});
|
|
295
|
-
form.appendChild(sectionLabel('Label'));
|
|
296
|
-
form.appendChild(ctlTextField('Text', String((_c = (_b = link.getLabel) === null || _b === void 0 ? void 0 : _b.call(link)) !== null && _c !== void 0 ? _c : ''), (v) => exec(new SetLinkDisplayLabelCommand(link.id, v))));
|
|
297
|
-
form.appendChild(sectionLabel('Line'));
|
|
298
|
-
form.appendChild(colorField('Colour', toHexColor(style['stroke'], '#999999'), (v) => exec(new UpdateLinkStyleCommand(link.id, { stroke: v }))));
|
|
299
|
-
form.appendChild(numberField('Width', () => { var _a; return (_a = style['strokeWidth']) !== null && _a !== void 0 ? _a : 2; }, (n) => exec(new UpdateLinkStyleCommand(link.id, { strokeWidth: Math.max(0, n) }))));
|
|
300
|
-
form.appendChild(checkField('Dashed', !!style['strokeDasharray'], (c) => exec(new UpdateLinkStyleCommand(link.id, { strokeDasharray: c ? DASH : undefined }))));
|
|
301
|
-
form.appendChild(sectionLabel('Arrows'));
|
|
302
|
-
const marker = (slot, type) => {
|
|
303
|
-
var _a;
|
|
304
|
-
const existing = ((_a = style[slot]) !== null && _a !== void 0 ? _a : {});
|
|
305
|
-
exec(new UpdateLinkStyleCommand(link.id, {
|
|
306
|
-
[slot]: Object.assign(Object.assign({ size: 10, filled: true }, existing), { type }),
|
|
307
|
-
}));
|
|
308
|
-
};
|
|
309
|
-
// The renderer paints an implicit 'arrow' head when none is set — seed the
|
|
310
|
-
// select with what the user SEES, not with the absent key.
|
|
311
|
-
form.appendChild(selectField('Start', ARROW_TYPES, String((_e = (_d = style['arrowTail']) === null || _d === void 0 ? void 0 : _d.type) !== null && _e !== void 0 ? _e : 'none'), (v) => marker('arrowTail', v)));
|
|
312
|
-
form.appendChild(selectField('End', ARROW_TYPES, String((_g = (_f = style['arrowHead']) === null || _f === void 0 ? void 0 : _f.type) !== null && _g !== void 0 ? _g : 'arrow'), (v) => marker('arrowHead', v)));
|
|
313
|
-
form.appendChild(sectionLabel('Route'));
|
|
314
|
-
form.appendChild(selectField('Path', ROUTE_TYPES, String((_h = link.pathType) !== null && _h !== void 0 ? _h : 'smooth'), (v) => exec(new SetLinkPathTypeCommand(link.id, v))));
|
|
315
|
-
host.appendChild(form);
|
|
316
|
-
}
|
|
317
|
-
// ── multi-selection ───────────────────────────────────────────────────────
|
|
318
|
-
function renderMulti(nodes, links) {
|
|
319
|
-
var _a, _b;
|
|
320
|
-
const count = document.createElement('div');
|
|
321
|
-
count.className = 'gf-sd-count';
|
|
322
|
-
count.textContent = `${nodes.length + links.length} shapes`;
|
|
323
|
-
host.appendChild(count);
|
|
324
|
-
// Kit cards keep their kit look — see the single-node Format note.
|
|
325
|
-
const styleNodes = nodes.filter((n) => { var _a, _b; return !(((_a = n.getMetadata) === null || _a === void 0 ? void 0 : _a.call(n, 'kitEntity')) || ((_b = n.getMetadata) === null || _b === void 0 ? void 0 : _b.call(n, 'kitClass'))); });
|
|
326
|
-
if (styleNodes.length + links.length === 0)
|
|
327
|
-
return;
|
|
328
|
-
const nodeIds = styleNodes.map((n) => n.id);
|
|
329
|
-
const seed = ((_a = styleNodes[0]) !== null && _a !== void 0 ? _a : links[0]);
|
|
330
|
-
const seedStyle = ((_b = seed === null || seed === void 0 ? void 0 : seed.style) !== null && _b !== void 0 ? _b : {});
|
|
331
|
-
/** The whole selection restyled as ONE undo entry. */
|
|
332
|
-
const applyAll = (nodePatch, linkPatch) => {
|
|
333
|
-
const cmds = [];
|
|
334
|
-
if (nodePatch && nodeIds.length > 0)
|
|
335
|
-
cmds.push(new SetNodeStyleCommand(nodeIds, nodePatch));
|
|
336
|
-
if (linkPatch)
|
|
337
|
-
for (const l of links)
|
|
338
|
-
cmds.push(new UpdateLinkStyleCommand(l.id, linkPatch));
|
|
339
|
-
if (cmds.length === 0)
|
|
340
|
-
return;
|
|
341
|
-
exec(cmds.length === 1 ? cmds[0] : new BatchCommand('Format selection', cmds));
|
|
342
|
-
};
|
|
343
|
-
const form = document.createElement('div');
|
|
344
|
-
form.className = 'gf-sd-fields';
|
|
345
|
-
form.appendChild(sectionLabel('Format'));
|
|
346
|
-
if (nodeIds.length > 0) {
|
|
347
|
-
form.appendChild(colorField('Fill', toHexColor(seedStyle['fill'], '#ffffff'), (v) => applyAll({ fill: v }, null)));
|
|
348
|
-
}
|
|
349
|
-
form.appendChild(colorField('Line', toHexColor(seedStyle['stroke'], '#333333'), (v) => applyAll({ stroke: v }, { stroke: v })));
|
|
350
|
-
form.appendChild(numberField('Line width', () => { var _a; return (_a = seedStyle['strokeWidth']) !== null && _a !== void 0 ? _a : 1; }, (n) => {
|
|
351
|
-
const w = Math.max(0, n);
|
|
352
|
-
applyAll({ strokeWidth: w }, { strokeWidth: w });
|
|
353
|
-
}));
|
|
354
|
-
form.appendChild(checkField('Dashed', !!seedStyle['strokeDasharray'], (c) => {
|
|
355
|
-
const dash = c ? DASH : undefined;
|
|
356
|
-
applyAll({ strokeDasharray: dash }, { strokeDasharray: dash });
|
|
357
|
-
}));
|
|
358
|
-
host.appendChild(form);
|
|
359
|
-
}
|
|
360
|
-
/** Table/class properties, plus the selected column's, Visio-style. */
|
|
361
|
-
function renderKitCard(node) {
|
|
362
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
363
|
-
const isEr = !!node.getMetadata('kitEntity');
|
|
364
|
-
const spec = node.getMetadata(isEr ? 'kitEntity' : 'kitClass');
|
|
365
|
-
const handle = isEr ? erTable(api, node.id) : umlClass(api, node.id);
|
|
366
|
-
const form = document.createElement('div');
|
|
367
|
-
form.className = 'gf-sd-fields';
|
|
368
|
-
// ── the card itself ──
|
|
369
|
-
form.appendChild(sectionLabel(isEr ? 'Table' : 'Class'));
|
|
370
|
-
form.appendChild(textField('Name', (_a = spec === null || spec === void 0 ? void 0 : spec.name) !== null && _a !== void 0 ? _a : node.id, (v) => { void handle.rename(v); }));
|
|
371
|
-
form.appendChild(readOnlyField(isEr ? 'Columns' : 'Members', String(isEr ? ((_c = (_b = spec === null || spec === void 0 ? void 0 : spec.columns) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0)
|
|
372
|
-
: (((_e = (_d = spec === null || spec === void 0 ? void 0 : spec.attributes) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0) + ((_g = (_f = spec === null || spec === void 0 ? void 0 : spec.methods) === null || _f === void 0 ? void 0 : _f.length) !== null && _g !== void 0 ? _g : 0)))));
|
|
373
|
-
// ── the selected row, if any ──
|
|
374
|
-
const rowHost = document.createElement('div');
|
|
375
|
-
form.appendChild(rowHost);
|
|
376
|
-
const showField = (field) => {
|
|
377
|
-
var _a, _b;
|
|
378
|
-
rowHost.innerHTML = '';
|
|
379
|
-
if (!field) {
|
|
380
|
-
rowHost.appendChild(hint('Click a column to edit it.'));
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
// ErField exposes name/type/pk/fk as getters over the live spec.
|
|
384
|
-
rowHost.appendChild(sectionLabel('Column'));
|
|
385
|
-
rowHost.appendChild(textField('Name', (_a = field.name) !== null && _a !== void 0 ? _a : '', (v) => { void field.rename(v); }));
|
|
386
|
-
rowHost.appendChild(textField('Type', (_b = field.type) !== null && _b !== void 0 ? _b : '', (v) => { void field.setType(v); }));
|
|
387
|
-
if (field.pk || field.fk) {
|
|
388
|
-
rowHost.appendChild(readOnlyField('Key', [field.pk && 'PK', field.fk && 'FK'].filter(Boolean).join(' + ')));
|
|
389
|
-
}
|
|
390
|
-
const del = document.createElement('button');
|
|
391
|
-
del.className = 'gf-sd-danger';
|
|
392
|
-
del.textContent = 'Delete column';
|
|
393
|
-
del.addEventListener('click', () => { void field.remove(); rowHost.innerHTML = ''; });
|
|
394
|
-
rowHost.appendChild(del);
|
|
395
|
-
};
|
|
396
|
-
showField(isEr ? (_h = handle.selectedColumn) !== null && _h !== void 0 ? _h : null : null);
|
|
397
|
-
// Live: the panel follows the kit's own row-selection event.
|
|
398
|
-
offRow === null || offRow === void 0 ? void 0 : offRow();
|
|
399
|
-
offRow = (_k = (_j = handle.onRowSelect) === null || _j === void 0 ? void 0 : _j.call(handle, ({ field }) => showField(field))) !== null && _k !== void 0 ? _k : null;
|
|
400
|
-
host.appendChild(form);
|
|
401
|
-
}
|
|
402
|
-
const sectionLabel = (text) => {
|
|
403
|
-
const el = document.createElement('div');
|
|
404
|
-
el.className = 'gf-sd-section';
|
|
405
|
-
el.textContent = text;
|
|
406
|
-
return el;
|
|
407
|
-
};
|
|
408
|
-
const hint = (text) => {
|
|
409
|
-
const el = document.createElement('div');
|
|
410
|
-
el.className = 'gf-sd-empty';
|
|
411
|
-
el.textContent = text;
|
|
412
|
-
return el;
|
|
413
|
-
};
|
|
414
|
-
const readOnlyField = (label, value) => {
|
|
415
|
-
const row = document.createElement('label');
|
|
416
|
-
row.className = 'gf-sd-row';
|
|
417
|
-
const l = document.createElement('span');
|
|
418
|
-
l.className = 'gf-sd-label';
|
|
419
|
-
l.textContent = label;
|
|
420
|
-
const v = document.createElement('input');
|
|
421
|
-
v.className = 'gf-sd-input';
|
|
422
|
-
v.value = value;
|
|
423
|
-
v.readOnly = true;
|
|
424
|
-
row.append(l, v);
|
|
425
|
-
return row;
|
|
426
|
-
};
|
|
427
|
-
const textField = (label, value, commit) => {
|
|
428
|
-
const row = document.createElement('label');
|
|
429
|
-
row.className = 'gf-sd-row';
|
|
430
|
-
const l = document.createElement('span');
|
|
431
|
-
l.className = 'gf-sd-label';
|
|
432
|
-
l.textContent = label;
|
|
433
|
-
const i = document.createElement('input');
|
|
434
|
-
i.className = 'gf-sd-input';
|
|
435
|
-
i.value = value;
|
|
436
|
-
i.addEventListener('change', () => commit(i.value));
|
|
437
|
-
i.addEventListener('keydown', (e) => { e.stopPropagation(); if (e.key === 'Enter')
|
|
438
|
-
i.blur(); });
|
|
439
|
-
row.append(l, i);
|
|
440
|
-
return row;
|
|
441
|
-
};
|
|
442
|
-
// ── the NEW controls (gf-sd-ctl, NOT gf-sd-input — see the header note) ──
|
|
443
|
-
const fieldRow = (label, field, inline = false) => {
|
|
444
|
-
const row = document.createElement('label');
|
|
445
|
-
row.className = inline ? 'gf-sd-row gf-sd-inline' : 'gf-sd-row';
|
|
446
|
-
const l = document.createElement('span');
|
|
447
|
-
l.className = 'gf-sd-label';
|
|
448
|
-
l.textContent = label;
|
|
449
|
-
row.append(l, field);
|
|
450
|
-
return row;
|
|
451
|
-
};
|
|
452
|
-
const ctlTextField = (label, value, commit) => {
|
|
453
|
-
const i = document.createElement('input');
|
|
454
|
-
i.type = 'text';
|
|
455
|
-
i.className = 'gf-sd-ctl';
|
|
456
|
-
i.value = value;
|
|
457
|
-
i.addEventListener('change', () => commit(i.value));
|
|
458
|
-
i.addEventListener('keydown', (e) => { e.stopPropagation(); if (e.key === 'Enter')
|
|
459
|
-
i.blur(); });
|
|
460
|
-
return fieldRow(label, i);
|
|
461
|
-
};
|
|
462
|
-
/**
|
|
463
|
-
* Numeric input that CANNOT write garbage: on blur/Enter the value is parsed,
|
|
464
|
-
* and anything unparseable reverts to the model's current value — no NaN ever
|
|
465
|
-
* reaches a command.
|
|
466
|
-
*/
|
|
467
|
-
const numberField = (label, current, commit) => {
|
|
468
|
-
const i = document.createElement('input');
|
|
469
|
-
i.type = 'number';
|
|
470
|
-
i.className = 'gf-sd-ctl';
|
|
471
|
-
i.step = 'any';
|
|
472
|
-
const seed = () => { var _a; i.value = String(Math.round(((_a = current()) !== null && _a !== void 0 ? _a : 0) * 100) / 100); };
|
|
473
|
-
seed();
|
|
474
|
-
i.addEventListener('change', () => {
|
|
475
|
-
const n = Number(i.value);
|
|
476
|
-
if (i.value.trim() === '' || !Number.isFinite(n)) {
|
|
477
|
-
seed();
|
|
478
|
-
return;
|
|
479
|
-
}
|
|
480
|
-
commit(n);
|
|
481
|
-
});
|
|
482
|
-
i.addEventListener('keydown', (e) => { e.stopPropagation(); if (e.key === 'Enter')
|
|
483
|
-
i.blur(); });
|
|
484
|
-
return fieldRow(label, i);
|
|
485
|
-
};
|
|
486
|
-
const colorField = (label, hex, commit) => {
|
|
487
|
-
const i = document.createElement('input');
|
|
488
|
-
i.type = 'color';
|
|
489
|
-
i.className = 'gf-sd-color';
|
|
490
|
-
i.value = hex;
|
|
491
|
-
// 'change' fires once when the picker commits — one undo entry per edit.
|
|
492
|
-
i.addEventListener('change', () => commit(i.value));
|
|
493
|
-
return fieldRow(label, i);
|
|
494
|
-
};
|
|
495
|
-
const checkField = (label, checked, commit) => {
|
|
496
|
-
const i = document.createElement('input');
|
|
497
|
-
i.type = 'checkbox';
|
|
498
|
-
i.className = 'gf-sd-check';
|
|
499
|
-
i.checked = checked;
|
|
500
|
-
i.addEventListener('change', () => commit(i.checked));
|
|
501
|
-
return fieldRow(label, i, true);
|
|
502
|
-
};
|
|
503
|
-
const selectField = (label, opts, current, commit) => {
|
|
504
|
-
const sel = document.createElement('select');
|
|
505
|
-
sel.className = 'gf-sd-ctl';
|
|
506
|
-
const values = opts.includes(current) ? opts : [current, ...opts];
|
|
507
|
-
for (const opt of values) {
|
|
508
|
-
const o = document.createElement('option');
|
|
509
|
-
o.value = opt;
|
|
510
|
-
o.textContent = opt;
|
|
511
|
-
if (opt === current)
|
|
512
|
-
o.selected = true;
|
|
513
|
-
sel.appendChild(o);
|
|
514
|
-
}
|
|
515
|
-
sel.addEventListener('change', () => commit(sel.value));
|
|
516
|
-
return fieldRow(label, sel);
|
|
517
|
-
};
|
|
518
|
-
const pair = (a, b) => {
|
|
519
|
-
const wrap = document.createElement('div');
|
|
520
|
-
wrap.className = 'gf-sd-pair';
|
|
521
|
-
wrap.append(a, b);
|
|
522
|
-
return wrap;
|
|
523
|
-
};
|
|
524
|
-
let offRow = null;
|
|
525
|
-
const off = api.on('selection:change', () => render());
|
|
526
|
-
render();
|
|
527
|
-
return {
|
|
528
|
-
refresh: render,
|
|
529
|
-
destroy() {
|
|
530
|
-
offRow === null || offRow === void 0 ? void 0 : offRow();
|
|
531
|
-
off === null || off === void 0 ? void 0 : off();
|
|
532
|
-
host.classList.remove('gf-shapedata');
|
|
533
|
-
host.innerHTML = '';
|
|
534
|
-
},
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
//# sourceMappingURL=shape-data.js.map
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stencil-palette styles, injected once per document (same discipline as the
|
|
3
|
-
* dashboard kit: the kit owns its chrome so a host gets a working palette
|
|
4
|
-
* without copying CSS, and every colour is a themable custom property).
|
|
5
|
-
*/
|
|
6
|
-
const STENCIL_KIT_STYLE_ID = 'grafloria-stencil-kit-styles';
|
|
7
|
-
const CSS = `
|
|
8
|
-
.gf-stencil {
|
|
9
|
-
--gf-st-bg: #fff; --gf-st-ink: #1e2436; --gf-st-mut: #6b7280;
|
|
10
|
-
--gf-st-line: #e5e7eb; --gf-st-accent: #3B52D9; --gf-st-hover: #f5f7ff;
|
|
11
|
-
display: flex; flex-direction: column; min-height: 0; height: 100%;
|
|
12
|
-
background: var(--gf-st-bg); color: var(--gf-st-ink);
|
|
13
|
-
font: 13px/1.4 ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
14
|
-
border-right: 1px solid var(--gf-st-line); box-sizing: border-box;
|
|
15
|
-
}
|
|
16
|
-
@media (prefers-color-scheme: dark) {
|
|
17
|
-
.gf-stencil {
|
|
18
|
-
--gf-st-bg: #161a24; --gf-st-ink: #e8ecfb; --gf-st-mut: #9aa4bf;
|
|
19
|
-
--gf-st-line: #2a3040; --gf-st-accent: #8fa2ff; --gf-st-hover: #1e2536;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
.gf-stencil-search { padding: 10px; border-bottom: 1px solid var(--gf-st-line); }
|
|
23
|
-
.gf-stencil-search input {
|
|
24
|
-
width: 100%; box-sizing: border-box; padding: 7px 10px; border-radius: 8px;
|
|
25
|
-
border: 1px solid var(--gf-st-line); background: var(--gf-st-bg);
|
|
26
|
-
color: var(--gf-st-ink); font: inherit; outline: none;
|
|
27
|
-
}
|
|
28
|
-
.gf-stencil-search input:focus { border-color: var(--gf-st-accent); }
|
|
29
|
-
.gf-stencil-body { overflow: auto; flex: 1; min-height: 0; padding-bottom: 8px; }
|
|
30
|
-
.gf-stencil-group > summary {
|
|
31
|
-
cursor: pointer; padding: 8px 10px; font-weight: 600; font-size: 12px;
|
|
32
|
-
letter-spacing: .02em; text-transform: uppercase; color: var(--gf-st-mut);
|
|
33
|
-
list-style: none; user-select: none; display: flex; align-items: center; gap: 6px;
|
|
34
|
-
}
|
|
35
|
-
.gf-stencil-group > summary::-webkit-details-marker { display: none; }
|
|
36
|
-
.gf-stencil-group > summary::before { content: '▸'; font-size: 10px; transition: transform .15s; }
|
|
37
|
-
.gf-stencil-group[open] > summary::before { transform: rotate(90deg); }
|
|
38
|
-
.gf-stencil-group > summary:hover { color: var(--gf-st-ink); }
|
|
39
|
-
.gf-stencil-count { margin-left: auto; font-weight: 500; opacity: .7; text-transform: none; }
|
|
40
|
-
.gf-stencil-items {
|
|
41
|
-
display: grid; grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
|
|
42
|
-
gap: 4px; padding: 2px 8px 10px;
|
|
43
|
-
}
|
|
44
|
-
.gf-stencil-item {
|
|
45
|
-
display: flex; flex-direction: column; align-items: center; gap: 4px;
|
|
46
|
-
padding: 8px 4px; border-radius: 8px; cursor: grab; border: 1px solid transparent;
|
|
47
|
-
text-align: center; background: none;
|
|
48
|
-
}
|
|
49
|
-
.gf-stencil-item:hover { background: var(--gf-st-hover); border-color: var(--gf-st-line); }
|
|
50
|
-
.gf-stencil-item:active { cursor: grabbing; }
|
|
51
|
-
.gf-stencil-item svg { display: block; overflow: visible; }
|
|
52
|
-
.gf-stencil-label {
|
|
53
|
-
font-size: 10.5px; color: var(--gf-st-mut); line-height: 1.25;
|
|
54
|
-
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
|
|
55
|
-
overflow: hidden; word-break: break-word;
|
|
56
|
-
}
|
|
57
|
-
.gf-stencil-item:hover .gf-stencil-label { color: var(--gf-st-ink); }
|
|
58
|
-
.gf-stencil-empty { padding: 18px 12px; color: var(--gf-st-mut); font-size: 12px; text-align: center; }
|
|
59
|
-
.gf-shapedata {
|
|
60
|
-
--gf-st-bg: #fff; --gf-st-ink: #1e2436; --gf-st-mut: #6b7280;
|
|
61
|
-
--gf-st-line: #e5e7eb; --gf-st-accent: #3B52D9;
|
|
62
|
-
display: flex; flex-direction: column; height: 100%; box-sizing: border-box;
|
|
63
|
-
background: var(--gf-st-bg); color: var(--gf-st-ink); overflow: auto;
|
|
64
|
-
font: 13px/1.4 ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
65
|
-
border-left: 1px solid var(--gf-st-line);
|
|
66
|
-
}
|
|
67
|
-
@media (prefers-color-scheme: dark) {
|
|
68
|
-
.gf-shapedata { --gf-st-bg: #161a24; --gf-st-ink: #e8ecfb; --gf-st-mut: #9aa4bf; --gf-st-line: #2a3040; --gf-st-accent: #8fa2ff; }
|
|
69
|
-
}
|
|
70
|
-
.gf-sd-title {
|
|
71
|
-
padding: 10px 12px; font-weight: 600; font-size: 12px; text-transform: uppercase;
|
|
72
|
-
letter-spacing: .02em; color: var(--gf-st-mut); border-bottom: 1px solid var(--gf-st-line);
|
|
73
|
-
}
|
|
74
|
-
.gf-sd-empty { padding: 16px 12px; color: var(--gf-st-mut); font-size: 12px; }
|
|
75
|
-
.gf-sd-fields { display: flex; flex-direction: column; gap: 8px; padding: 12px; }
|
|
76
|
-
.gf-sd-row { display: flex; flex-direction: column; gap: 3px; }
|
|
77
|
-
.gf-sd-label { font-size: 11px; color: var(--gf-st-mut); }
|
|
78
|
-
.gf-sd-input {
|
|
79
|
-
padding: 6px 8px; border: 1px solid var(--gf-st-line); border-radius: 7px;
|
|
80
|
-
background: var(--gf-st-bg); color: var(--gf-st-ink); font: inherit; outline: none; width: 100%; box-sizing: border-box;
|
|
81
|
-
}
|
|
82
|
-
.gf-sd-input:focus { border-color: var(--gf-st-accent); }
|
|
83
|
-
.gf-sd-check { width: 16px; height: 16px; accent-color: var(--gf-st-accent); }
|
|
84
|
-
.gf-sd-section {
|
|
85
|
-
font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
|
|
86
|
-
color: var(--gf-st-mut); margin: 10px 0 2px; padding-top: 8px; border-top: 1px solid var(--gf-st-line);
|
|
87
|
-
}
|
|
88
|
-
.gf-sd-section:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
|
|
89
|
-
.gf-sd-danger {
|
|
90
|
-
margin-top: 8px; padding: 6px 10px; border-radius: 7px; cursor: pointer;
|
|
91
|
-
border: 1px solid #fecaca; background: #fef2f2; color: #b91c1c;
|
|
92
|
-
font: 600 12px ui-sans-serif, system-ui, sans-serif;
|
|
93
|
-
}
|
|
94
|
-
.gf-sd-danger:hover { background: #fee2e2; }
|
|
95
|
-
@media (prefers-color-scheme: dark) {
|
|
96
|
-
.gf-sd-danger { background: #2a1414; border-color: #7f1d1d; color: #fca5a5; }
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/* The canvas while a stencil is held over it. */
|
|
100
|
-
.gf-stencil-target { outline: 2px dashed var(--gf-st-accent); outline-offset: -3px; }
|
|
101
|
-
|
|
102
|
-
/* The drag GHOST — the master's silhouette + name following the cursor during a
|
|
103
|
-
palette drag, so the drop point is legible. A DOM element on purpose: the
|
|
104
|
-
browser's native drag image lives outside the DOM, does not screenshot, and
|
|
105
|
-
several platforms simply never paint it. */
|
|
106
|
-
.gf-stencil-ghost {
|
|
107
|
-
position: fixed; z-index: 1000; pointer-events: none;
|
|
108
|
-
transform: translate(-50%, -50%); opacity: .6;
|
|
109
|
-
display: flex; align-items: center; gap: 8px; padding: 6px 10px;
|
|
110
|
-
background: var(--gf-st-bg, #fff); color: var(--gf-st-ink, #1e2436);
|
|
111
|
-
border: 1px solid var(--gf-st-accent, #3B52D9); border-radius: 8px;
|
|
112
|
-
box-shadow: 0 6px 20px rgba(0,0,0,.18);
|
|
113
|
-
font: 12px ui-sans-serif, system-ui, sans-serif;
|
|
114
|
-
}
|
|
115
|
-
.gf-stencil-ghost svg { display: block; }
|
|
116
|
-
`;
|
|
117
|
-
/** Inject the palette stylesheet once per document. */
|
|
118
|
-
export function ensureStencilKitStyles(doc = document) {
|
|
119
|
-
if (doc.getElementById(STENCIL_KIT_STYLE_ID))
|
|
120
|
-
return;
|
|
121
|
-
const style = doc.createElement('style');
|
|
122
|
-
style.id = STENCIL_KIT_STYLE_ID;
|
|
123
|
-
style.textContent = CSS;
|
|
124
|
-
doc.head.appendChild(style);
|
|
125
|
-
}
|
|
126
|
-
//# sourceMappingURL=styles.js.map
|