@xeplr/ui-factory 1.0.14 → 1.2.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/AUTHORING.md +29 -5
- package/README.md +167 -20
- package/package.json +7 -2
- package/src/controls.js +19 -13
- package/src/designs/BuilderSample.jsx +77 -19
- package/src/designs/ControlView.jsx +10 -1
- package/src/designs/FlowRunnerSample.jsx +6 -4
- package/src/designs/ListView.jsx +40 -21
- package/src/designs/Palette.jsx +2 -1
- package/src/designs/PropertyPanel.jsx +57 -1
- package/src/designs/ScreenEditorSample.jsx +97 -0
- package/src/designs/ScreenModal.jsx +2 -2
- package/src/designs/ScreenSample.jsx +92 -41
- package/src/designs/factory.css +170 -76
- package/src/designs/index.js +4 -0
- package/src/designs/styles.js +11 -1
- package/src/generate.js +35 -10
- package/src/hooks.js +1 -1
- package/src/index.js +6 -2
- package/src/model.js +2 -1
- package/src/pages.jsx +18 -3
- package/src/presets.js +324 -0
- package/src/remote.js +7 -1
- package/src/scaffold.js +2 -2
- package/src/screenSource.js +91 -0
- package/src/tableSchema.js +109 -6
- package/src/urlState.js +71 -0
- package/src/useFactoryBuilder.js +97 -25
- package/src/useFactoryScreen.js +85 -47
- package/src/useFlowBuilder.js +7 -4
- package/src/useScreenSource.js +81 -0
- package/src/useUrlPlaces.js +48 -0
- package/src/validateDocument.js +5 -1
- package/src/values.js +18 -5
|
@@ -11,23 +11,39 @@ import { CHOICE_TYPES } from '../controls.js'
|
|
|
11
11
|
// The screen is never wider than the width it was designed at, so what was
|
|
12
12
|
// designed at 800px is shown at 800px, and only a narrower screen scales it.
|
|
13
13
|
//
|
|
14
|
-
//
|
|
14
|
+
// SAVE IS A BUTTON, AND AN AJAX CALL — never a form submit. The <form> below
|
|
15
|
+
// exists for semantics only: its submit is swallowed, so Enter in a field
|
|
16
|
+
// posts nothing and saves nothing. Only the Save button saves.
|
|
17
|
+
//
|
|
18
|
+
// ONE FOOTER. Back, "Step 2 of 3" with the save status under it, then Next,
|
|
19
|
+
// Cancel and Save — a single bar under the fields, inset by the screen's
|
|
20
|
+
// margin so its edges line up with theirs.
|
|
15
21
|
|
|
16
22
|
const STATUS = {
|
|
17
23
|
idle: '',
|
|
18
|
-
pending: 'Unsaved changes
|
|
24
|
+
pending: 'Unsaved changes',
|
|
19
25
|
saving: 'Saving…',
|
|
20
|
-
saved: '
|
|
21
|
-
incomplete: 'Fill in the required fields
|
|
26
|
+
saved: 'Saved',
|
|
27
|
+
incomplete: 'Fill in the required fields',
|
|
22
28
|
invalid: 'Fix the highlighted fields to save'
|
|
23
29
|
}
|
|
24
30
|
|
|
31
|
+
/** What the background save is doing, in words. */
|
|
32
|
+
function SaveStatus({ ctrl }) {
|
|
33
|
+
const text = ctrl.status === 'error' ? `Not saved — ${ctrl.saveError}` : STATUS[ctrl.status]
|
|
34
|
+
return (
|
|
35
|
+
<span className={`xeplr-factory-footer-status xeplr-factory-footer-status--${ctrl.status}`} role="status" aria-live="polite">
|
|
36
|
+
{text}
|
|
37
|
+
</span>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
25
41
|
/**
|
|
26
42
|
* Back and Next, under the page. Next holds at a step whose own fields are not
|
|
27
43
|
* filled in — and shows why, rather than moving on and leaving a problem
|
|
28
44
|
* behind on a step nobody is looking at.
|
|
29
45
|
*/
|
|
30
|
-
function StepNav({ ctrl, node, children }) {
|
|
46
|
+
function StepNav({ ctrl, node, status, children }) {
|
|
31
47
|
const at = ctrl.steps.active(node.id)
|
|
32
48
|
const total = ctrl.steps.count(node.id)
|
|
33
49
|
const off = ctrl.steps.disabled(node.id)
|
|
@@ -51,29 +67,60 @@ function StepNav({ ctrl, node, children }) {
|
|
|
51
67
|
const handsOn = Boolean(children) && Boolean(ctrl.doneLabel)
|
|
52
68
|
const lastStep = forward === null
|
|
53
69
|
return (
|
|
54
|
-
|
|
70
|
+
// Moving between steps (Back, Next) sits on the left as quiet buttons;
|
|
71
|
+
// Save — the one thing that writes — is the only filled button, on the
|
|
72
|
+
// right. In a flow, Next IS the way forward, so there it stays filled.
|
|
73
|
+
<div className="xeplr-factory-footer xeplr-factory-stepnav">
|
|
55
74
|
<button type="button" className="xeplr-factory-secondary" onClick={() => go(back, 'back')} disabled={back === null}>Back</button>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
75
|
+
{!handsOn && (
|
|
76
|
+
<button type="button" className="xeplr-factory-secondary" onClick={() => go(forward, 'next')} disabled={lastStep}>Next</button>
|
|
77
|
+
)}
|
|
78
|
+
<div className="xeplr-factory-footer-mid">
|
|
79
|
+
<span className="xeplr-factory-stepnav-where">Step {place} of {live}</span>
|
|
80
|
+
{status}
|
|
81
|
+
</div>
|
|
82
|
+
{handsOn && !lastStep && (
|
|
83
|
+
<button type="button" className="xeplr-factory-primary" onClick={() => go(forward, 'next')}>Next</button>
|
|
59
84
|
)}
|
|
60
85
|
{(!handsOn || lastStep) && children}
|
|
61
86
|
</div>
|
|
62
87
|
)
|
|
63
88
|
}
|
|
64
89
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
/**
|
|
91
|
+
* The ways out, at the end of the footer.
|
|
92
|
+
*
|
|
93
|
+
* Save one AJAX call. On a page or in a popup (the host gave onDone) it
|
|
94
|
+
* then goes back; a form embedded in a page stays, showing "Saved".
|
|
95
|
+
* Cancel goes back without saving — asking first if anything is unsaved.
|
|
96
|
+
* Only where there is somewhere to go back to.
|
|
97
|
+
*
|
|
98
|
+
* Inside a flow the flow names the button (doneLabel, "Next"): it saves what
|
|
99
|
+
* is unsaved, then moves the journey on — one button, not two.
|
|
100
|
+
*/
|
|
101
|
+
function SaveButtons({ ctrl }) {
|
|
102
|
+
const saving = ctrl.status === 'saving'
|
|
103
|
+
if (ctrl.doneLabel) {
|
|
104
|
+
return (
|
|
105
|
+
<button type="button" className="xeplr-factory-primary" disabled={saving}
|
|
106
|
+
onClick={() => ctrl.flush().then((r) => { if (r && r.ok) ctrl.done(r) })}>
|
|
107
|
+
{ctrl.doneLabel}
|
|
108
|
+
</button>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
const save = () => ctrl.save().then((r) => { if (r && r.ok && ctrl.done) ctrl.done() })
|
|
68
112
|
return (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
113
|
+
<>
|
|
114
|
+
{ctrl.done && (
|
|
115
|
+
<button type="button" className="xeplr-factory-secondary" disabled={saving}
|
|
116
|
+
onClick={() => { if (ctrl.confirmDiscard()) ctrl.done() }}>
|
|
117
|
+
Cancel
|
|
118
|
+
</button>
|
|
119
|
+
)}
|
|
120
|
+
<button type="button" className="xeplr-factory-primary" disabled={saving} onClick={save}>
|
|
121
|
+
{saving ? 'Saving…' : 'Save'}
|
|
122
|
+
</button>
|
|
123
|
+
</>
|
|
77
124
|
)
|
|
78
125
|
}
|
|
79
126
|
|
|
@@ -97,26 +144,22 @@ export default function ScreenSample({ ctrl, className, style, renderPopup }) {
|
|
|
97
144
|
const shown = ctrl.visibleNodes && ctrl.visibleNodes.length ? ctrl.visibleNodes : doc.nodes
|
|
98
145
|
const pageHeight = (contentBottom(doc, shown) + MARGIN) * aspect * 100
|
|
99
146
|
const fieldNodes = inputNodes(doc)
|
|
100
|
-
|
|
101
|
-
//
|
|
147
|
+
// A screen with no fields of its own (a list screen) has nothing to save —
|
|
148
|
+
// and no sheet: its list is the card (factory.css, --bare).
|
|
102
149
|
const hasFields = fieldNodes.length > 0
|
|
150
|
+
const status = hasFields ? <SaveStatus ctrl={ctrl} /> : null
|
|
151
|
+
const steppers = ctrl.steps.nodes
|
|
103
152
|
|
|
104
153
|
return (
|
|
105
154
|
<div
|
|
106
|
-
className={'xeplr-factory-screen' + (className ? ' ' + className : '')}
|
|
107
|
-
style={{ maxWidth: doc.width, ...screenStyle(doc), ...style }}
|
|
155
|
+
className={'xeplr-factory-screen' + (hasFields ? '' : ' xeplr-factory-screen--bare') + (className ? ' ' + className : '')}
|
|
156
|
+
style={{ maxWidth: doc.width, '--xf-margin': `${MARGIN * 100}%`, ...screenStyle(doc), ...style }}
|
|
108
157
|
>
|
|
109
|
-
{hasFields && (
|
|
110
|
-
<div className={`xeplr-factory-status xeplr-factory-status--${ctrl.status}`} role="status" aria-live="polite">
|
|
111
|
-
<span>{ctrl.recordId !== null && ctrl.recordId !== undefined ? 'Editing record' : 'New record'}</span>
|
|
112
|
-
<span>{statusText}</span>
|
|
113
|
-
</div>
|
|
114
|
-
)}
|
|
115
158
|
<form
|
|
116
159
|
className="xeplr-factory-page"
|
|
117
160
|
style={{ height: `${pageHeight}cqw` }}
|
|
118
|
-
// Enter
|
|
119
|
-
onSubmit={(e) =>
|
|
161
|
+
// Never a submit: Enter posts nothing and saves nothing. Save is a button.
|
|
162
|
+
onSubmit={(e) => e.preventDefault()}
|
|
120
163
|
noValidate
|
|
121
164
|
aria-label={doc.name}
|
|
122
165
|
>
|
|
@@ -156,22 +199,30 @@ export default function ScreenSample({ ctrl, className, style, renderPopup }) {
|
|
|
156
199
|
extraActions: ctrl.actionsFor ? ctrl.actionsFor(node) : [],
|
|
157
200
|
// A list with an edit screen opens it in a popup; one on a form
|
|
158
201
|
// opens the row in the form's own fields.
|
|
159
|
-
|
|
202
|
+
// A list on the form itself swaps the record in the fields — so
|
|
203
|
+
// unsaved changes to the one showing are asked about first.
|
|
204
|
+
onEdit: (rec) => (node.props.editScreen ? ctrl.openEditor(node, rec) : (ctrl.confirmDiscard() && ctrl.openRecord(rec))),
|
|
160
205
|
// eslint-disable-next-line no-alert
|
|
161
206
|
onDelete: (rec) => ctrl.deleteRecord(node, rec).catch((err) => window.alert(err.message || 'Could not delete')),
|
|
162
|
-
onNew: () => (node.props.editScreen ? ctrl.openEditor(node, null) : ctrl.newRecord())
|
|
207
|
+
onNew: () => (node.props.editScreen ? ctrl.openEditor(node, null) : (ctrl.confirmDiscard() && ctrl.newRecord()))
|
|
163
208
|
} : undefined}
|
|
164
209
|
/>
|
|
165
210
|
</div>
|
|
166
211
|
))}
|
|
167
212
|
</form>
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
213
|
+
{steppers.map((node, i) => {
|
|
214
|
+
const last = i === steppers.length - 1
|
|
215
|
+
return (
|
|
216
|
+
<StepNav key={node.id} ctrl={ctrl} node={node} status={last ? status : null}>
|
|
217
|
+
{last && (hasFields || ctrl.doneLabel) && <SaveButtons ctrl={ctrl} />}
|
|
218
|
+
</StepNav>
|
|
219
|
+
)
|
|
220
|
+
})}
|
|
221
|
+
{!steppers.length && (hasFields || ctrl.doneLabel) && (
|
|
222
|
+
<div className="xeplr-factory-footer">
|
|
223
|
+
<div className="xeplr-factory-footer-mid">{status}</div>
|
|
224
|
+
<SaveButtons ctrl={ctrl} />
|
|
225
|
+
</div>
|
|
175
226
|
)}
|
|
176
227
|
{ctrl.popup && renderPopup && renderPopup(ctrl.popup, ctrl)}
|
|
177
228
|
</div>
|
package/src/designs/factory.css
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
/* @xeplr/ui-factory — builder and screen.
|
|
2
|
-
Colours come from the
|
|
3
|
-
a host that defines none still
|
|
2
|
+
Colours come from the app's theme — @xeplr/ui-account's --xeplr-* variables
|
|
3
|
+
(theme.css) — each with a light fallback, so a host that defines none still
|
|
4
|
+
gets a finished look.
|
|
5
|
+
|
|
6
|
+
THE REAL NAMES, NOT LOOK-ALIKES. This file used to read --xeplr-text,
|
|
7
|
+
--xeplr-surface, --xeplr-border and --xeplr-muted, which no theme defines —
|
|
8
|
+
so every fallback won, and in a dark app a screen came out as a white sheet
|
|
9
|
+
while the @xeplr/ui-table inside it (which reads the real names) came out
|
|
10
|
+
dark. Everything below resolves through the --xf-* tokens, and those read
|
|
11
|
+
the variables theme.css actually sets. "Elevated" is --xeplr-bg-tertiary in
|
|
12
|
+
every built-in theme (white in light, the lighter grey in dark), which is
|
|
13
|
+
what a sheet, a panel and a popup are.
|
|
4
14
|
|
|
5
15
|
ONE SIZE UNIT, --xf-u: one pixel at the screen's design width (--xf-width,
|
|
6
16
|
set from the document). At that width or wider it IS a pixel — text is
|
|
@@ -12,11 +22,24 @@
|
|
|
12
22
|
.xeplr-factory-flow-builder {
|
|
13
23
|
container-type: inline-size;
|
|
14
24
|
--xf-u: min(1px, calc(100cqw / var(--xf-width, 800)));
|
|
25
|
+
}
|
|
26
|
+
.xeplr-factory-screen,
|
|
27
|
+
.xeplr-factory-canvas,
|
|
28
|
+
.xeplr-factory-flow-builder,
|
|
29
|
+
.xeplr-factory-builder,
|
|
30
|
+
.xeplr-factory-modal-backdrop {
|
|
15
31
|
--xf-accent: var(--xeplr-accent, #2563eb);
|
|
16
|
-
--xf-ink: var(--xeplr-text, #
|
|
17
|
-
--xf-
|
|
18
|
-
--xf-
|
|
19
|
-
--xf-
|
|
32
|
+
--xf-accent-ink: var(--xeplr-accent-text, #ffffff);
|
|
33
|
+
--xf-ink: var(--xeplr-text-primary, #1a1a19);
|
|
34
|
+
--xf-muted: var(--xeplr-text-secondary, #6b6a63);
|
|
35
|
+
--xf-border: var(--xeplr-border-primary, #d9d8d0);
|
|
36
|
+
--xf-surface: var(--xeplr-bg-tertiary, #ffffff);
|
|
37
|
+
--xf-plane: var(--xeplr-bg-primary, #f7f7f5);
|
|
38
|
+
--xf-hover: var(--xeplr-bg-hover, #f0f0ee);
|
|
39
|
+
--xf-field-bg: var(--xeplr-input-bg, var(--xf-surface));
|
|
40
|
+
--xf-field-border: var(--xeplr-input-border, var(--xf-border));
|
|
41
|
+
--xf-good: var(--xeplr-success, #1d7a4c);
|
|
42
|
+
--xf-warn: var(--xeplr-warning, #a86a00);
|
|
20
43
|
--xf-danger: var(--xeplr-danger, #c2372e);
|
|
21
44
|
}
|
|
22
45
|
|
|
@@ -28,21 +51,19 @@
|
|
|
28
51
|
host with --xeplr-factory-paper. */
|
|
29
52
|
.xeplr-factory-screen {
|
|
30
53
|
position: relative; width: 100%;
|
|
54
|
+
/* Centred in whatever it is given: shown at its design width, a screen that
|
|
55
|
+
hugs the left edge of a wide page looks abandoned there. */
|
|
56
|
+
margin-inline: auto;
|
|
57
|
+
box-sizing: border-box;
|
|
31
58
|
color: var(--xf-ink);
|
|
32
59
|
background: var(--xeplr-factory-paper, var(--xf-surface));
|
|
33
|
-
border
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
.xeplr-factory-screen
|
|
40
|
-
.xeplr-factory-status {
|
|
41
|
-
display: flex; justify-content: space-between; gap: 12px; min-height: 18px;
|
|
42
|
-
padding: 4px 2px 8px; font-size: 12px; color: var(--xf-muted);
|
|
43
|
-
}
|
|
44
|
-
.xeplr-factory-status--saved { color: var(--xeplr-status-good, #1d7a4c); }
|
|
45
|
-
.xeplr-factory-status--invalid, .xeplr-factory-status--error { color: var(--xf-danger); }
|
|
60
|
+
border: 1px solid var(--xf-border);
|
|
61
|
+
border-radius: 10px;
|
|
62
|
+
}
|
|
63
|
+
/* A screen of lists only (nothing to fill in) paints no sheet: the list is
|
|
64
|
+
its own card, and a sheet around it is a second frame with a block of empty
|
|
65
|
+
paper under the rows. */
|
|
66
|
+
.xeplr-factory-screen--bare { background: none; border: none; }
|
|
46
67
|
.xeplr-factory-page { position: relative; width: 100%; margin: 0; }
|
|
47
68
|
.xeplr-factory-node { position: absolute; box-sizing: border-box; }
|
|
48
69
|
|
|
@@ -66,15 +87,17 @@
|
|
|
66
87
|
.xeplr-factory-required { color: var(--xf-danger); }
|
|
67
88
|
.xeplr-factory-input {
|
|
68
89
|
flex: 1; min-height: 0; width: 100%; box-sizing: border-box;
|
|
69
|
-
/* The
|
|
70
|
-
|
|
71
|
-
dark ink
|
|
72
|
-
font: inherit; color: var(--xf-ink); background: var(--xf-
|
|
73
|
-
border: 1px solid var(--xf-border); border-radius: calc(6 * var(--xf-u));
|
|
90
|
+
/* The app's input colours — unless the SCREEN sets its own colours, when
|
|
91
|
+
screenStyle points --xf-ink / --xf-field-bg at those, so a screen designed
|
|
92
|
+
in dark ink on paper never gets a dark theme's box under that ink. */
|
|
93
|
+
font: inherit; color: var(--xf-ink); background: var(--xf-field-bg);
|
|
94
|
+
border: 1px solid var(--xf-field-border); border-radius: calc(6 * var(--xf-u));
|
|
74
95
|
padding: 0 calc(10 * var(--xf-u));
|
|
96
|
+
transition: border-color .12s, box-shadow .12s;
|
|
75
97
|
}
|
|
98
|
+
.xeplr-factory-input:hover { border-color: color-mix(in srgb, var(--xf-accent) 45%, var(--xf-field-border)); }
|
|
76
99
|
.xeplr-factory-textarea { padding-block: calc(8 * var(--xf-u)); resize: none; }
|
|
77
|
-
.xeplr-factory-input:focus { outline:
|
|
100
|
+
.xeplr-factory-input:focus { outline: none; border-color: var(--xf-accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--xf-accent) 25%, transparent); }
|
|
78
101
|
.xeplr-factory-field.has-error .xeplr-factory-input { border-color: var(--xf-danger); }
|
|
79
102
|
.xeplr-factory-error {
|
|
80
103
|
/* In the gap BELOW the field, out of flow: a message appearing must not
|
|
@@ -92,7 +115,10 @@
|
|
|
92
115
|
list of options cannot cover the next field. */
|
|
93
116
|
.xeplr-factory-choices { display: flex; gap: calc(6 * var(--xf-u)) calc(16 * var(--xf-u)); overflow: auto; min-height: 0; }
|
|
94
117
|
.xeplr-factory-choices--vertical { flex-direction: column; }
|
|
95
|
-
|
|
118
|
+
/* Side by side, the options sit in the band an input box would fill — so a
|
|
119
|
+
row of radios beside a dropdown lines up with the dropdown's box, not with
|
|
120
|
+
its label. */
|
|
121
|
+
.xeplr-factory-choices--horizontal { flex: 1; flex-direction: row; flex-wrap: wrap; align-items: center; align-content: center; }
|
|
96
122
|
.xeplr-factory-choice { display: flex; align-items: center; gap: calc(8 * var(--xf-u)); cursor: pointer; color: var(--xf-ink); }
|
|
97
123
|
.xeplr-factory-choice input { width: calc(16 * var(--xf-u)); height: calc(16 * var(--xf-u)); min-width: 11px; min-height: 11px; margin: 0; accent-color: var(--xf-accent); }
|
|
98
124
|
|
|
@@ -100,48 +126,81 @@
|
|
|
100
126
|
.xeplr-factory-file { display: flex; align-items: center; gap: calc(8 * var(--xf-u)); min-width: 0; }
|
|
101
127
|
.xeplr-factory-file-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--xf-ink); }
|
|
102
128
|
.xeplr-factory-file-clear {
|
|
103
|
-
flex: none; border: 1px solid var(--xf-border); background: var(--xf-
|
|
129
|
+
flex: none; border: 1px solid var(--xf-field-border); background: var(--xf-field-bg); color: var(--xf-muted);
|
|
104
130
|
border-radius: calc(6 * var(--xf-u)); padding: calc(3 * var(--xf-u)) calc(8 * var(--xf-u)); cursor: pointer; font: inherit;
|
|
105
131
|
}
|
|
106
132
|
.xeplr-factory-file-clear:hover { color: var(--xf-ink); border-color: var(--xf-accent); }
|
|
107
133
|
.xeplr-factory-file-input { padding: calc(5 * var(--xf-u)); }
|
|
108
134
|
|
|
109
135
|
/* ── stepper ──────────────────────────────────────────────────────────────
|
|
110
|
-
The journey across the top: a circle per step, joined by a line. The line
|
|
111
|
-
|
|
112
|
-
|
|
136
|
+
The journey across the top: a circle per step, joined by a line. The line
|
|
137
|
+
runs from the EDGE of one circle to the edge of the next, with a small gap —
|
|
138
|
+
never through them. (It used to run centre to centre behind the circles,
|
|
139
|
+
and each step's line was painted after the previous step's circle, so it
|
|
140
|
+
struck straight through the numbers.) --xf-mark is the circle's size. */
|
|
141
|
+
.xeplr-factory-stepper {
|
|
142
|
+
--xf-mark: max(22px, calc(28 * var(--xf-u)));
|
|
143
|
+
display: flex; align-items: center; width: 100%; height: 100%; overflow: hidden;
|
|
144
|
+
}
|
|
113
145
|
.xeplr-factory-steps { display: flex; align-items: flex-start; width: 100%; margin: 0; padding: 0; list-style: none; }
|
|
114
146
|
.xeplr-factory-step { flex: 1; position: relative; min-width: 0; text-align: center; }
|
|
115
147
|
.xeplr-factory-step + .xeplr-factory-step::before {
|
|
116
|
-
content: ''; position: absolute;
|
|
148
|
+
content: ''; position: absolute;
|
|
149
|
+
top: calc(var(--xf-mark) / 2 - 1px); height: 2px; border-radius: 1px;
|
|
150
|
+
left: calc(-50% + var(--xf-mark) / 2 + 8px);
|
|
151
|
+
right: calc(50% + var(--xf-mark) / 2 + 8px);
|
|
117
152
|
background: var(--xf-border);
|
|
153
|
+
transition: background .2s;
|
|
118
154
|
}
|
|
119
155
|
.xeplr-factory-step.is-active::before, .xeplr-factory-step.is-done::before { background: var(--xf-accent); }
|
|
120
156
|
.xeplr-factory-step-button {
|
|
121
|
-
position: relative; display: flex; flex-direction: column; align-items: center; gap: calc(
|
|
157
|
+
position: relative; display: flex; flex-direction: column; align-items: center; gap: calc(6 * var(--xf-u));
|
|
122
158
|
width: 100%; padding: 0; border: none; background: none; color: var(--xf-muted); font: inherit; cursor: pointer;
|
|
123
159
|
}
|
|
124
160
|
.xeplr-factory-step-mark {
|
|
125
|
-
display: flex; align-items: center; justify-content: center;
|
|
126
|
-
width:
|
|
127
|
-
border-radius: 50%;
|
|
161
|
+
display: flex; align-items: center; justify-content: center; box-sizing: border-box;
|
|
162
|
+
width: var(--xf-mark); height: var(--xf-mark);
|
|
163
|
+
border-radius: 50%; border: 2px solid var(--xf-border); background: var(--xf-surface);
|
|
164
|
+
color: var(--xf-muted); font-weight: 600; font-size: .9em; line-height: 1;
|
|
165
|
+
transition: background .2s, border-color .2s, box-shadow .2s, transform .08s;
|
|
128
166
|
}
|
|
167
|
+
.xeplr-factory-step-button:hover .xeplr-factory-step-mark { border-color: var(--xf-accent); }
|
|
168
|
+
.xeplr-factory-step-button:active .xeplr-factory-step-mark { transform: scale(.92); }
|
|
129
169
|
.xeplr-factory-step-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
|
|
130
|
-
.xeplr-factory-step.is-
|
|
131
|
-
|
|
170
|
+
.xeplr-factory-step.is-done .xeplr-factory-step-mark { background: var(--xf-accent); border-color: var(--xf-accent); color: var(--xf-accent-ink); }
|
|
171
|
+
/* Where you are: filled, with a soft halo. */
|
|
172
|
+
.xeplr-factory-step.is-active .xeplr-factory-step-mark {
|
|
173
|
+
background: var(--xf-accent); border-color: var(--xf-accent); color: var(--xf-accent-ink);
|
|
174
|
+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--xf-accent) 22%, transparent);
|
|
175
|
+
}
|
|
176
|
+
.xeplr-factory-step.is-done .xeplr-factory-step-button { color: var(--xf-ink); }
|
|
132
177
|
.xeplr-factory-step.is-active .xeplr-factory-step-button { color: var(--xf-accent); font-weight: 600; }
|
|
178
|
+
.xeplr-factory-step-button:focus { outline: none; }
|
|
179
|
+
.xeplr-factory-step-button:focus-visible .xeplr-factory-step-mark { outline: 2px solid var(--xf-accent); outline-offset: 3px; }
|
|
133
180
|
/* A step the app has ruled out: still shown, so the journey reads the same,
|
|
134
181
|
but plainly not one of the stops. */
|
|
135
182
|
.xeplr-factory-step.is-off .xeplr-factory-step-button { opacity: 0.4; cursor: default; }
|
|
136
183
|
.xeplr-factory-step.is-off .xeplr-factory-step-label { text-decoration: line-through; }
|
|
137
184
|
|
|
138
|
-
/*
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
185
|
+
/* ── the footer ─────────────────────────────────────────────────────────────
|
|
186
|
+
ONE bar under the fields: Back on the left, where you are and what the save
|
|
187
|
+
is doing in the middle, the ways forward on the right. Inset by the screen's
|
|
188
|
+
own margin (--xf-margin, set by ScreenSample) so the buttons line up with
|
|
189
|
+
the fields' edges instead of poking out past them. */
|
|
190
|
+
.xeplr-factory-footer {
|
|
191
|
+
display: flex; align-items: center; gap: 10px;
|
|
192
|
+
margin-top: calc(8 * var(--xf-u));
|
|
193
|
+
padding: 14px var(--xf-margin, 16px);
|
|
194
|
+
border-top: 1px solid var(--xf-border);
|
|
195
|
+
}
|
|
196
|
+
.xeplr-factory-footer-mid {
|
|
197
|
+
flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 2px;
|
|
198
|
+
color: var(--xf-muted); font-size: 12px; text-align: center;
|
|
199
|
+
}
|
|
200
|
+
.xeplr-factory-footer-mid:first-child { align-items: flex-start; text-align: left; }
|
|
201
|
+
.xeplr-factory-footer-status--saved { color: var(--xf-good); }
|
|
202
|
+
.xeplr-factory-footer-status--invalid, .xeplr-factory-footer-status--error { color: var(--xf-danger); }
|
|
203
|
+
.xeplr-factory-footer button[disabled] { opacity: 0.4; cursor: default; }
|
|
145
204
|
|
|
146
205
|
/* Which step the builder is on. */
|
|
147
206
|
.xeplr-factory-building { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; margin: 0 auto 8px; padding: 6px 0; }
|
|
@@ -159,37 +218,42 @@
|
|
|
159
218
|
|
|
160
219
|
/* ── list ───────────────────────────────────────────────────────────────── */
|
|
161
220
|
.xeplr-factory-list {
|
|
162
|
-
display: flex; flex-direction: column; width: 100%;
|
|
221
|
+
display: flex; flex-direction: column; width: 100%; box-sizing: border-box; overflow: auto;
|
|
222
|
+
/* As tall as its rows, up to the box it was given — never a frame around a
|
|
223
|
+
block of empty space under two records. */
|
|
224
|
+
height: auto; max-height: 100%;
|
|
163
225
|
font-size: max(10px, calc(var(--xf-font, 14) * var(--xf-u)));
|
|
164
|
-
border: 1px solid var(--xf-border); border-radius:
|
|
226
|
+
border: 1px solid var(--xf-border); border-radius: 10px; background: var(--xf-surface);
|
|
165
227
|
}
|
|
166
|
-
.xeplr-factory-list-head { display: flex; align-items: center; gap: 10px; padding:
|
|
167
|
-
.xeplr-factory-list-title { font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
228
|
+
.xeplr-factory-list-head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--xf-border); }
|
|
229
|
+
.xeplr-factory-list-title { font-weight: 600; font-size: 1.05em; color: var(--xf-ink); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
168
230
|
.xeplr-factory-list-new {
|
|
169
|
-
font: inherit; font-weight: 600; font-size: .9em; padding:
|
|
170
|
-
color: var(--xf-accent); background:
|
|
231
|
+
font: inherit; font-weight: 600; font-size: .9em; padding: 5px 12px; border-radius: 6px; cursor: pointer;
|
|
232
|
+
color: var(--xf-accent-ink); background: var(--xf-accent); border: 1px solid var(--xf-accent);
|
|
171
233
|
}
|
|
234
|
+
/* The table IS the card here: the list's frame is enough, so the table's own
|
|
235
|
+
border and corners go, and its toolbar becomes the list's header. */
|
|
236
|
+
.xeplr-factory-list--table { overflow: hidden; }
|
|
237
|
+
.xeplr-factory-list--table .xeplr-table-container { border: none; border-radius: 0; background: transparent; }
|
|
238
|
+
.xeplr-factory-list--table .xeplr-table-toolbar { padding: 12px 14px; background: transparent; gap: 10px; }
|
|
239
|
+
.xeplr-factory-list--design { height: 100%; }
|
|
172
240
|
.xeplr-factory-list-empty { padding: 16px; color: var(--xf-muted); text-align: center; }
|
|
173
241
|
.xeplr-factory-error-inline { color: var(--xf-danger); font-size: .9em; }
|
|
174
242
|
.xeplr-factory-list-mock { width: 100%; border-collapse: collapse; }
|
|
175
243
|
.xeplr-factory-list-mock th { text-align: left; font-weight: 600; font-size: .9em; padding: calc(6 * var(--xf-u)) calc(10 * var(--xf-u)); border-bottom: 1px solid var(--xf-border); color: var(--xf-muted); }
|
|
176
244
|
.xeplr-factory-list-mock td { padding: calc(8 * var(--xf-u)) calc(10 * var(--xf-u)); border-bottom: 1px solid var(--xf-border); }
|
|
177
|
-
.xeplr-factory-list-bar { display: block; height: calc(8 * var(--xf-u)); width: 70%; border-radius: 4px; background:
|
|
245
|
+
.xeplr-factory-list-bar { display: block; height: calc(8 * var(--xf-u)); width: 70%; border-radius: 4px; background: var(--xf-border); }
|
|
178
246
|
.xeplr-factory-list .xeplr-table-row.xeplr-factory-row-open td,
|
|
179
247
|
.xeplr-factory-list tr.xeplr-factory-row-open td { background: color-mix(in srgb, var(--xf-accent) 10%, transparent); }
|
|
180
248
|
|
|
181
249
|
/* ── the builder ────────────────────────────────────────────────────────── */
|
|
182
250
|
.xeplr-factory-builder {
|
|
183
251
|
display: flex; flex-direction: column; height: 100%; min-height: 560px;
|
|
184
|
-
color: var(--
|
|
185
|
-
--xf-accent: var(--xeplr-accent, #2563eb);
|
|
186
|
-
--xf-border: var(--xeplr-border, #d9d8d0);
|
|
187
|
-
--xf-muted: var(--xeplr-muted, #6b6a63);
|
|
188
|
-
--xf-danger: var(--xeplr-danger, #c2372e);
|
|
252
|
+
color: var(--xf-ink); font-size: 13px;
|
|
189
253
|
}
|
|
190
254
|
.xeplr-factory-bar {
|
|
191
255
|
display: flex; align-items: center; gap: 10px; padding: 8px 12px;
|
|
192
|
-
border-bottom: 1px solid var(--xf-border); background: var(--
|
|
256
|
+
border-bottom: 1px solid var(--xf-border); background: var(--xf-surface);
|
|
193
257
|
}
|
|
194
258
|
.xeplr-factory-name {
|
|
195
259
|
flex: 1; min-width: 0; font: inherit; font-size: 16px; font-weight: 600;
|
|
@@ -200,27 +264,41 @@
|
|
|
200
264
|
.xeplr-factory-problems { font-size: 12px; color: var(--xf-muted); white-space: nowrap; cursor: help; }
|
|
201
265
|
.xeplr-factory-problems.is-loud { color: var(--xf-danger); font-weight: 600; }
|
|
202
266
|
.xeplr-factory-primary, .xeplr-factory-secondary, .xeplr-factory-danger {
|
|
203
|
-
font: inherit; font-weight: 600; padding:
|
|
267
|
+
font: inherit; font-weight: 600; padding: 7px 16px; border-radius: 6px; cursor: pointer; border: 1px solid var(--xf-accent);
|
|
268
|
+
}
|
|
269
|
+
.xeplr-factory-primary { background: var(--xf-accent); color: var(--xf-accent-ink); }
|
|
270
|
+
.xeplr-factory-primary, .xeplr-factory-secondary, .xeplr-factory-danger, .xeplr-factory-list-new {
|
|
271
|
+
transition: background .12s, filter .12s, transform .08s, box-shadow .12s;
|
|
272
|
+
}
|
|
273
|
+
.xeplr-factory-primary:hover:not(:disabled) { filter: brightness(1.08); }
|
|
274
|
+
.xeplr-factory-secondary:hover:not(:disabled) { background: color-mix(in srgb, var(--xf-accent) 12%, transparent); }
|
|
275
|
+
.xeplr-factory-list-new:hover { filter: brightness(1.08); }
|
|
276
|
+
.xeplr-factory-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--xf-danger) 12%, transparent); }
|
|
277
|
+
/* A press you can see — the button gives a little. */
|
|
278
|
+
.xeplr-factory-primary:active:not(:disabled), .xeplr-factory-secondary:active:not(:disabled),
|
|
279
|
+
.xeplr-factory-danger:active:not(:disabled), .xeplr-factory-list-new:active { transform: scale(.96); }
|
|
280
|
+
.xeplr-factory-primary:focus-visible, .xeplr-factory-secondary:focus-visible,
|
|
281
|
+
.xeplr-factory-danger:focus-visible, .xeplr-factory-list-new:focus-visible {
|
|
282
|
+
outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--xf-accent) 35%, transparent);
|
|
204
283
|
}
|
|
205
|
-
.xeplr-factory-primary { background: var(--xf-accent); color: #fff; }
|
|
206
284
|
.xeplr-factory-primary:disabled { opacity: .6; }
|
|
207
285
|
.xeplr-factory-secondary { background: transparent; color: var(--xf-accent); }
|
|
208
286
|
.xeplr-factory-danger { background: transparent; color: var(--xf-danger); border-color: var(--xf-danger); }
|
|
209
287
|
.xeplr-factory-save-error { padding: 6px 12px; color: var(--xf-danger); background: color-mix(in srgb, var(--xf-danger) 7%, transparent); font-size: 12px; }
|
|
210
288
|
|
|
211
289
|
.xeplr-factory-body { flex: 1; min-height: 0; display: flex; }
|
|
212
|
-
.xeplr-factory-bar-status--saved { color: var(--
|
|
290
|
+
.xeplr-factory-bar-status--saved { color: var(--xf-good); }
|
|
213
291
|
.xeplr-factory-bar-status--invalid, .xeplr-factory-bar-status--error { color: var(--xf-danger); }
|
|
214
|
-
.xeplr-factory-preview { flex: 1; min-height: 0; overflow: auto; padding: 16px; background: var(--
|
|
215
|
-
.xeplr-factory-preview > .xeplr-factory-screen { max-width: 960px; margin: 0 auto; background: var(--
|
|
292
|
+
.xeplr-factory-preview { flex: 1; min-height: 0; overflow: auto; padding: 16px; background: var(--xf-plane); }
|
|
293
|
+
.xeplr-factory-preview > .xeplr-factory-screen { max-width: 960px; margin: 0 auto; background: var(--xf-surface); border: 1px solid var(--xf-border); border-radius: 8px; }
|
|
216
294
|
|
|
217
295
|
/* palette */
|
|
218
|
-
.xeplr-factory-palette { width: 168px; flex: 0 0 auto; overflow-y: auto; padding: 10px; border-right: 1px solid var(--xf-border); background: var(--
|
|
296
|
+
.xeplr-factory-palette { width: 168px; flex: 0 0 auto; overflow-y: auto; padding: 10px; border-right: 1px solid var(--xf-border); background: var(--xf-surface); }
|
|
219
297
|
.xeplr-factory-palette-group + .xeplr-factory-palette-group { margin-top: 12px; }
|
|
220
298
|
.xeplr-factory-palette-title { margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--xf-muted); }
|
|
221
299
|
.xeplr-factory-palette-item {
|
|
222
300
|
display: flex; align-items: center; gap: 8px; width: 100%; margin-bottom: 4px; padding: 7px 8px;
|
|
223
|
-
font: inherit; text-align: left; color: inherit; background: var(--
|
|
301
|
+
font: inherit; text-align: left; color: inherit; background: var(--xf-surface);
|
|
224
302
|
border: 1px solid var(--xf-border); border-radius: 6px; cursor: grab;
|
|
225
303
|
}
|
|
226
304
|
.xeplr-factory-palette-item:hover { border-color: var(--xf-accent); }
|
|
@@ -236,14 +314,14 @@
|
|
|
236
314
|
.xeplr-factory-palette-icon--textarea { height: 14px; }
|
|
237
315
|
|
|
238
316
|
/* canvas */
|
|
239
|
-
.xeplr-factory-stage { flex: 1; min-width: 0; min-height: 0; display: flex; justify-content: center; padding: 12px; box-sizing: border-box; background: var(--
|
|
317
|
+
.xeplr-factory-stage { flex: 1; min-width: 0; min-height: 0; display: flex; justify-content: center; padding: 12px; box-sizing: border-box; background: var(--xf-plane); }
|
|
240
318
|
/* Exactly the screen's design width, never wider: what you design is the size it is shown at. */
|
|
241
319
|
.xeplr-factory-frame { flex: 1; min-width: 0; height: 100%; display: flex; box-shadow: 0 1px 4px rgba(0,0,0,.08); }
|
|
242
320
|
.xeplr-factory-canvas {
|
|
243
321
|
flex: 1; height: 100%;
|
|
244
322
|
background:
|
|
245
323
|
radial-gradient(circle, var(--xeplr-border-secondary, #e4e3db) 1px, transparent 1px) 0 0 / 20px 20px,
|
|
246
|
-
var(--
|
|
324
|
+
var(--xf-surface);
|
|
247
325
|
}
|
|
248
326
|
.xeplr-factory-design-node { width: 100%; height: 100%; box-sizing: border-box; border-radius: 4px; outline: 1px dashed transparent; cursor: move; }
|
|
249
327
|
.xeplr-factory-design-node:hover { outline-color: color-mix(in srgb, var(--xf-accent) 45%, transparent); }
|
|
@@ -257,7 +335,21 @@
|
|
|
257
335
|
}
|
|
258
336
|
|
|
259
337
|
/* property panel */
|
|
260
|
-
.xeplr-factory-panel { width: 280px; flex: 0 0 auto; overflow-y: auto; padding: 10px 12px; border-left: 1px solid var(--xf-border); background: var(--
|
|
338
|
+
.xeplr-factory-panel { width: 280px; flex: 0 0 auto; overflow-y: auto; padding: 10px 12px; border-left: 1px solid var(--xf-border); background: var(--xf-surface); }
|
|
339
|
+
.xeplr-factory-fieldtype { padding: 2px 0 12px; margin-bottom: 4px; border-bottom: 1px solid var(--xf-border); }
|
|
340
|
+
.xeplr-factory-suggest {
|
|
341
|
+
margin-top: 8px; padding: 8px 10px; border-radius: 6px; font-size: 12px; line-height: 1.45;
|
|
342
|
+
color: var(--xf-ink); background: color-mix(in srgb, var(--xf-accent) 10%, transparent);
|
|
343
|
+
border: 1px solid color-mix(in srgb, var(--xf-accent) 35%, transparent);
|
|
344
|
+
}
|
|
345
|
+
.xeplr-factory-suggest-actions { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
|
|
346
|
+
.xeplr-factory-suggest-actions button { padding: 4px 10px; font-size: 12px; }
|
|
347
|
+
.xeplr-factory-hints {
|
|
348
|
+
font: inherit; font-size: 12px; white-space: nowrap; cursor: pointer; padding: 3px 10px; border-radius: 999px;
|
|
349
|
+
color: var(--xf-accent); background: color-mix(in srgb, var(--xf-accent) 10%, transparent);
|
|
350
|
+
border: 1px solid color-mix(in srgb, var(--xf-accent) 35%, transparent);
|
|
351
|
+
}
|
|
352
|
+
.xeplr-factory-hints:hover { background: color-mix(in srgb, var(--xf-accent) 18%, transparent); }
|
|
261
353
|
.xeplr-factory-panel-empty { color: var(--xf-muted); line-height: 1.5; }
|
|
262
354
|
.xeplr-factory-panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
|
|
263
355
|
.xeplr-factory-panel-type { font-weight: 700; font-size: 14px; }
|
|
@@ -268,7 +360,7 @@
|
|
|
268
360
|
.xeplr-factory-prop-label { display: block; margin-bottom: 3px; font-weight: 600; font-size: 12px; }
|
|
269
361
|
.xeplr-factory-prop-input {
|
|
270
362
|
width: 100%; box-sizing: border-box; font: inherit; padding: 5px 7px;
|
|
271
|
-
border: 1px solid var(--xf-border); border-radius: 5px; background:
|
|
363
|
+
border: 1px solid var(--xf-field-border); border-radius: 5px; background: var(--xf-field-bg); color: var(--xf-ink);
|
|
272
364
|
}
|
|
273
365
|
.xeplr-factory-prop.has-error .xeplr-factory-prop-input { border-color: var(--xf-danger); }
|
|
274
366
|
/* An editor made of two inputs — a set to pick from, and the list it fills in. */
|
|
@@ -281,7 +373,7 @@
|
|
|
281
373
|
|
|
282
374
|
.xeplr-factory-segment { display: flex; margin-bottom: 8px; border: 1px solid var(--xf-border); border-radius: 6px; overflow: hidden; }
|
|
283
375
|
.xeplr-factory-segment button { flex: 1; font: inherit; font-size: 12px; padding: 5px; border: none; background: transparent; cursor: pointer; color: inherit; }
|
|
284
|
-
.xeplr-factory-segment button.is-on { background: var(--xf-accent); color:
|
|
376
|
+
.xeplr-factory-segment button.is-on { background: var(--xf-accent); color: var(--xf-accent-ink); }
|
|
285
377
|
.xeplr-factory-options-head, .xeplr-factory-option-row { display: grid; grid-template-columns: 1fr 1fr 24px; gap: 4px; align-items: center; }
|
|
286
378
|
.xeplr-factory-options-head { font-size: 11px; color: var(--xf-muted); margin-bottom: 3px; }
|
|
287
379
|
.xeplr-factory-option-row { margin-bottom: 4px; }
|
|
@@ -298,24 +390,26 @@
|
|
|
298
390
|
.xeplr-factory-modal-backdrop {
|
|
299
391
|
position: fixed; inset: 0; z-index: 10000; display: flex; align-items: flex-start; justify-content: center;
|
|
300
392
|
padding: 4vh 2vw; box-sizing: border-box; overflow: auto;
|
|
301
|
-
background: rgba(20, 20, 18, .45);
|
|
393
|
+
background: var(--xeplr-bg-overlay, rgba(20, 20, 18, .45));
|
|
302
394
|
}
|
|
303
395
|
.xeplr-factory-modal {
|
|
304
|
-
max-width: 96vw; background: var(--
|
|
396
|
+
max-width: 96vw; background: var(--xf-surface); color: var(--xf-ink);
|
|
305
397
|
border-radius: 10px; box-shadow: 0 12px 40px rgba(0, 0, 0, .25); outline: none;
|
|
306
398
|
}
|
|
307
|
-
.xeplr-factory-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--
|
|
399
|
+
.xeplr-factory-modal-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--xf-border); }
|
|
308
400
|
.xeplr-factory-modal-title { font-weight: 700; font-size: 15px; }
|
|
309
|
-
.xeplr-factory-modal-close { border: none; background: none; font-size: 22px; line-height: 1; cursor: pointer; color: var(--
|
|
401
|
+
.xeplr-factory-modal-close { border: none; background: none; font-size: 22px; line-height: 1; cursor: pointer; color: var(--xf-muted); padding: 2px 6px; }
|
|
310
402
|
.xeplr-factory-modal-body { padding: 8px 24px 16px; }
|
|
403
|
+
/* The popup is the frame; the screen inside it does not draw a second one. */
|
|
404
|
+
.xeplr-factory-modal-body > .xeplr-factory-screen { border: none; border-radius: 0; }
|
|
311
405
|
|
|
312
406
|
/* ── publish result ─────────────────────────────────────────────────────── */
|
|
313
407
|
.xeplr-factory-publish { display: flex; align-items: flex-start; gap: 10px; padding: 8px 12px; font-size: 12.5px; border-bottom: 1px solid var(--xf-border); }
|
|
314
408
|
.xeplr-factory-publish > span { flex: 1; }
|
|
315
|
-
.xeplr-factory-publish.is-ok { color: var(--
|
|
409
|
+
.xeplr-factory-publish.is-ok { color: var(--xf-good); background: color-mix(in srgb, var(--xf-good) 7%, transparent); }
|
|
316
410
|
.xeplr-factory-publish.is-error { color: var(--xf-danger); background: color-mix(in srgb, var(--xf-danger) 7%, transparent); flex-wrap: wrap; }
|
|
317
|
-
.xeplr-factory-publish-detail { flex-basis: 100%; order: 3; margin: 4px 0 0; padding: 8px; max-height: 220px; overflow: auto; font-size: 11.5px; color: var(--
|
|
318
|
-
.xeplr-factory-publish.is-confirm { align-items: center; color: var(--
|
|
411
|
+
.xeplr-factory-publish-detail { flex-basis: 100%; order: 3; margin: 4px 0 0; padding: 8px; max-height: 220px; overflow: auto; font-size: 11.5px; color: var(--xf-ink); background: var(--xf-surface); border: 1px solid var(--xf-border); border-radius: 6px; white-space: pre; }
|
|
412
|
+
.xeplr-factory-publish.is-confirm { align-items: center; color: var(--xf-ink); background: color-mix(in srgb, var(--xf-warn) 10%, transparent); border-bottom-color: color-mix(in srgb, var(--xf-warn) 40%, transparent); }
|
|
319
413
|
.xeplr-factory-confirm-text { flex: 1; line-height: 1.5; }
|
|
320
414
|
.xeplr-factory-confirm-text ul { margin: 4px 0; padding-left: 18px; }
|
|
321
415
|
.xeplr-factory-confirm-actions { display: flex; gap: 8px; flex-shrink: 0; }
|
package/src/designs/index.js
CHANGED
|
@@ -9,3 +9,7 @@ export { default as FlowBuilderSample } from './FlowBuilderSample.jsx'
|
|
|
9
9
|
export { default as ControlView, VIEWS } from './ControlView.jsx'
|
|
10
10
|
export { default as Palette, DRAG_TYPE } from './Palette.jsx'
|
|
11
11
|
export { default as PropertyPanel, EDITORS } from './PropertyPanel.jsx'
|
|
12
|
+
|
|
13
|
+
// The body of another package's "design this screen" window — see
|
|
14
|
+
// ../useScreenSource.js, which is how it is normally reached.
|
|
15
|
+
export { ScreenEditorSample } from './ScreenEditorSample.jsx'
|