@fieldia/widgets 0.0.0-stage → 0.1.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +19 -2
  3. package/package.json +35 -3
  4. package/src/index.d.mts +7 -0
  5. package/src/index.d.ts +7 -0
  6. package/src/index.js +11 -0
  7. package/src/index.js.map +1 -0
  8. package/src/index.mjs +8 -0
  9. package/src/index.mjs.map +1 -0
  10. package/src/lib/extras.d.mts +5 -0
  11. package/src/lib/extras.d.ts +5 -0
  12. package/src/lib/extras.js +130 -0
  13. package/src/lib/extras.js.map +1 -0
  14. package/src/lib/extras.mjs +124 -0
  15. package/src/lib/extras.mjs.map +1 -0
  16. package/src/lib/files.d.mts +3 -0
  17. package/src/lib/files.d.ts +3 -0
  18. package/src/lib/files.js +115 -0
  19. package/src/lib/files.js.map +1 -0
  20. package/src/lib/files.mjs +112 -0
  21. package/src/lib/files.mjs.map +1 -0
  22. package/src/lib/labels.d.mts +17 -0
  23. package/src/lib/labels.d.ts +17 -0
  24. package/src/lib/labels.js +62 -0
  25. package/src/lib/labels.js.map +1 -0
  26. package/src/lib/labels.mjs +59 -0
  27. package/src/lib/labels.mjs.map +1 -0
  28. package/src/lib/lines.d.mts +2 -0
  29. package/src/lib/lines.d.ts +2 -0
  30. package/src/lib/lines.js +140 -0
  31. package/src/lib/lines.js.map +1 -0
  32. package/src/lib/lines.mjs +136 -0
  33. package/src/lib/lines.mjs.map +1 -0
  34. package/src/lib/relations.d.mts +6 -0
  35. package/src/lib/relations.d.ts +6 -0
  36. package/src/lib/relations.js +362 -0
  37. package/src/lib/relations.js.map +1 -0
  38. package/src/lib/relations.mjs +355 -0
  39. package/src/lib/relations.mjs.map +1 -0
  40. package/src/lib/styles.d.mts +16 -0
  41. package/src/lib/styles.d.ts +16 -0
  42. package/src/lib/styles.js +394 -0
  43. package/src/lib/styles.js.map +1 -0
  44. package/src/lib/styles.mjs +390 -0
  45. package/src/lib/styles.mjs.map +1 -0
  46. package/src/lib/widgets.d.mts +44 -0
  47. package/src/lib/widgets.d.ts +44 -0
  48. package/src/lib/widgets.js +356 -0
  49. package/src/lib/widgets.js.map +1 -0
  50. package/src/lib/widgets.mjs +351 -0
  51. package/src/lib/widgets.mjs.map +1 -0
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Fieldia's stylesheet: two skins and the layout chrome, all scoped to the
3
+ * form's own root (`.fd-form[data-fd-skin=…]`), so two forms on one page can
4
+ * wear different skins and the host page's own controls are never touched.
5
+ *
6
+ * underline — the Flectra and Odoo style: flat inputs on a single line, labels
7
+ * beside the field, navy accents.
8
+ * outlined — the Ant Design style: boxed inputs with rounded corners, labels
9
+ * above the field, a blue focus glow.
10
+ *
11
+ * Written with logical properties (inline/block, start/end), so a right-to-left
12
+ * form mirrors without a second stylesheet.
13
+ */
14
+ export const FIELDIA_CSS = /* css */ `
15
+ .fd-form {
16
+ --fd-font: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", "Noto Sans Arabic", sans-serif;
17
+ --fd-text: #212529;
18
+ --fd-muted: #6b7280;
19
+ --fd-page: #f2f3f5;
20
+ --fd-surface: #ffffff;
21
+ --fd-border: #cfd4da;
22
+ --fd-border-strong: #9aa3ad;
23
+ --fd-accent: #002855;
24
+ --fd-accent-text: #ffffff;
25
+ --fd-accent-soft: #e6ebf2;
26
+ --fd-focus: #002855;
27
+ --fd-focus-ring: none;
28
+ --fd-error: #c62828;
29
+ --fd-error-soft: #fdecea;
30
+ --fd-success: #2e7d32;
31
+ --fd-success-soft: #e8f5e9;
32
+ --fd-warning: #8a5300;
33
+ --fd-warning-soft: #fff4e0;
34
+ --fd-info: #0b5cad;
35
+ --fd-info-soft: #e7f0fb;
36
+ --fd-radius: 0px;
37
+ --fd-control-radius: 0px;
38
+ --fd-pad-y: 3px;
39
+ --fd-pad-x: 4px;
40
+ --fd-input-border: 0 0 1px 0;
41
+ --fd-label-weight: 600;
42
+ --fd-gap-x: 32px;
43
+ --fd-gap-y: 14px;
44
+ container-type: inline-size;
45
+ font-family: var(--fd-font);
46
+ font-size: 14px;
47
+ line-height: 1.45;
48
+ color: var(--fd-text);
49
+ -webkit-text-size-adjust: 100%;
50
+ }
51
+ .fd-form[data-fd-skin="outlined"] {
52
+ --fd-text: rgba(0, 0, 0, 0.88);
53
+ --fd-muted: rgba(0, 0, 0, 0.55);
54
+ --fd-page: #f5f5f5;
55
+ --fd-border: #d9d9d9;
56
+ --fd-border-strong: #bfbfbf;
57
+ --fd-accent: #1677ff;
58
+ --fd-accent-soft: #e6f4ff;
59
+ --fd-focus: #1677ff;
60
+ --fd-focus-ring: 0 0 0 2px rgba(5, 145, 255, 0.12);
61
+ --fd-error: #ff4d4f;
62
+ --fd-error-soft: #fff2f0;
63
+ --fd-radius: 8px;
64
+ --fd-control-radius: 6px;
65
+ --fd-pad-y: 4px;
66
+ --fd-pad-x: 11px;
67
+ --fd-input-border: 1px;
68
+ --fd-label-weight: 400;
69
+ --fd-gap-x: 24px;
70
+ --fd-gap-y: 18px;
71
+ }
72
+ .fd-form *, .fd-form *::before, .fd-form *::after { box-sizing: border-box; }
73
+ .fd-form [hidden] { display: none !important; }
74
+
75
+ /* ---- page frame ---------------------------------------------------------- */
76
+ .fd-page-head { display: grid; gap: 4px; margin-block-end: 20px; }
77
+ .fd-page-title { font-size: 22px; font-weight: 650; margin: 0; line-height: 1.25; }
78
+ .fd-page-description { color: var(--fd-muted); margin: 0; }
79
+
80
+ /* ---- fields ---------------------------------------------------------------- */
81
+ .fd-grid {
82
+ display: grid;
83
+ grid-template-columns: repeat(var(--fd-columns, 1), minmax(0, 1fr));
84
+ gap: var(--fd-gap-y) var(--fd-gap-x);
85
+ align-items: start;
86
+ }
87
+ .fd-field { display: grid; gap: 4px; min-width: 0; grid-column: span min(var(--fd-span, 1), var(--fd-columns, 1)); }
88
+ .fd-label { font-weight: var(--fd-label-weight); color: var(--fd-text); }
89
+ .fd-field.fd-required > .fd-label::after { content: " *"; color: var(--fd-error); }
90
+ .fd-form[data-fd-skin="outlined"] .fd-field.fd-required > .fd-label::after { content: ""; }
91
+ .fd-form[data-fd-skin="outlined"] .fd-field.fd-required > .fd-label::before { content: "* "; color: var(--fd-error); }
92
+ .fd-help { color: var(--fd-muted); font-size: 12.5px; }
93
+ .fd-error { color: var(--fd-error); font-size: 12.5px; }
94
+
95
+ /* underline: label beside the value, the way a Flectra sheet reads */
96
+ .fd-form[data-fd-skin="underline"] .fd-field {
97
+ /* One label width for every field, so a field spanning two columns lines up with its neighbours. */
98
+ grid-template-columns: var(--fd-label-width, 11em) minmax(0, 1fr);
99
+ column-gap: 12px;
100
+ align-items: baseline;
101
+ }
102
+ .fd-form[data-fd-skin="underline"] .fd-field > .fd-label { grid-column: 1; }
103
+ /* A table of lines needs the full width: its label sits above it. */
104
+ .fd-form[data-fd-skin="underline"] .fd-field[data-type="one2many"] { grid-template-columns: minmax(0, 1fr); }
105
+ .fd-form[data-fd-skin="underline"] .fd-field[data-type="one2many"] > * { grid-column: 1 !important; }
106
+ .fd-form[data-fd-skin="underline"] .fd-field > :not(.fd-label) { grid-column: 2; }
107
+ /* A label beside every value needs room twice over: the underline skin stacks its columns sooner. */
108
+ @container (max-width: 760px) {
109
+ .fd-form[data-fd-skin="underline"] .fd-grid { grid-template-columns: minmax(0, 1fr); }
110
+ .fd-form[data-fd-skin="underline"] .fd-field { grid-column: auto; }
111
+ }
112
+ @container (max-width: 520px) {
113
+ .fd-grid { grid-template-columns: minmax(0, 1fr); }
114
+ .fd-field { grid-column: auto; }
115
+ .fd-form[data-fd-skin="underline"] .fd-field { grid-template-columns: minmax(0, 1fr); }
116
+ .fd-form[data-fd-skin="underline"] .fd-field > * { grid-column: 1 !important; }
117
+ }
118
+
119
+ /* ---- inputs ---------------------------------------------------------------- */
120
+ .fd-input {
121
+ width: 100%;
122
+ min-width: 0;
123
+ font: inherit;
124
+ color: inherit;
125
+ background: var(--fd-surface);
126
+ border: solid var(--fd-border);
127
+ border-width: var(--fd-input-border);
128
+ border-radius: var(--fd-control-radius);
129
+ padding: var(--fd-pad-y) var(--fd-pad-x);
130
+ min-height: 30px;
131
+ transition: border-color 0.15s ease, box-shadow 0.15s ease;
132
+ }
133
+ .fd-input::placeholder { color: var(--fd-muted); opacity: 0.8; }
134
+ .fd-input:hover { border-color: var(--fd-border-strong); }
135
+ .fd-input:focus { outline: none; border-color: var(--fd-focus); box-shadow: var(--fd-focus-ring); }
136
+ .fd-form[data-fd-skin="underline"] .fd-input { background: transparent; }
137
+ .fd-form[data-fd-skin="underline"] .fd-input:focus { box-shadow: 0 1px 0 0 var(--fd-focus); }
138
+ .fd-form[data-fd-skin="underline"] .fd-field.fd-required .fd-input { border-color: var(--fd-accent); }
139
+ .fd-input[readonly] { background: transparent; border-color: transparent; padding-inline: 0; }
140
+ .fd-form[data-fd-skin="outlined"] .fd-input[readonly] { background: rgba(0, 0, 0, 0.04); border-color: var(--fd-border); padding-inline: var(--fd-pad-x); color: var(--fd-muted); }
141
+ .fd-input[aria-invalid="true"] { border-color: var(--fd-error); }
142
+ .fd-form[data-fd-skin="outlined"] .fd-input[aria-invalid="true"]:focus { box-shadow: 0 0 0 2px rgba(255, 38, 5, 0.06); }
143
+ .fd-textarea { resize: vertical; min-height: 64px; }
144
+ .fd-select { appearance: auto; }
145
+ .fd-number { display: flex; align-items: baseline; gap: 6px; }
146
+ .fd-number-input { font-variant-numeric: tabular-nums; }
147
+ .fd-currency { color: var(--fd-muted); font-size: 12.5px; font-weight: 600; }
148
+ .fd-pending { color: var(--fd-muted); font-style: italic; }
149
+ .fd-pending:empty::before { content: "—"; }
150
+
151
+ .fd-checkbox, .fd-choice input { width: 16px; height: 16px; accent-color: var(--fd-accent); margin: 0; }
152
+ .fd-switch {
153
+ appearance: none; width: 34px; height: 20px; margin: 0; border-radius: 999px; position: relative;
154
+ background: var(--fd-border-strong); cursor: pointer; transition: background 0.2s ease;
155
+ }
156
+ .fd-switch::after {
157
+ content: ""; position: absolute; inset-block-start: 2px; inset-inline-start: 2px; width: 16px; height: 16px;
158
+ border-radius: 50%; background: #fff; transition: transform 0.2s ease; box-shadow: 0 1px 2px rgba(0,0,0,0.25);
159
+ }
160
+ .fd-switch:checked { background: var(--fd-accent); }
161
+ .fd-switch:checked::after { transform: translateX(14px); }
162
+ [dir="rtl"] .fd-switch:checked::after { transform: translateX(-14px); }
163
+ .fd-choices { display: flex; flex-wrap: wrap; gap: 6px 18px; }
164
+ .fd-choice { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
165
+ .fd-points { display: flex; flex-wrap: wrap; gap: 6px; }
166
+ .fd-points button {
167
+ font: inherit; cursor: pointer; border: 1px solid var(--fd-border); background: var(--fd-surface);
168
+ color: var(--fd-text); border-radius: var(--fd-control-radius); min-width: 34px; height: 34px; padding: 0 8px;
169
+ }
170
+ .fd-points button.fd-on { background: var(--fd-accent); color: var(--fd-accent-text); border-color: var(--fd-accent); }
171
+ .fd-rating button { border: none; background: none; font-size: 24px; color: var(--fd-border-strong); padding: 0 2px; min-width: 0; }
172
+ .fd-rating button.fd-on { background: none; color: #f5a623; }
173
+ .fd-points button:disabled, .fd-choice input:disabled { cursor: default; }
174
+ .fd-form :focus-visible { outline: 2px solid var(--fd-focus); outline-offset: 2px; }
175
+ .fd-form .fd-input:focus-visible { outline: none; }
176
+
177
+ /* ---- pickers: many2one, many2many, reference ------------------------------ */
178
+ .fd-combo { position: relative; display: flex; align-items: center; min-width: 0; }
179
+ .fd-combo-input { padding-inline-end: 28px; }
180
+ .fd-combo-clear {
181
+ position: absolute; inset-inline-end: 4px; border: none; background: none; cursor: pointer; color: var(--fd-muted);
182
+ font-size: 16px; line-height: 1; padding: 2px 6px; border-radius: 4px;
183
+ }
184
+ .fd-combo-clear:hover { color: var(--fd-text); background: var(--fd-page); }
185
+ .fd-listbox {
186
+ position: absolute; inset-inline: 0; inset-block-start: calc(100% + 2px); z-index: 20; margin: 0; padding: 4px 0;
187
+ list-style: none; background: var(--fd-surface); border: 1px solid var(--fd-border); border-radius: max(var(--fd-control-radius), 4px);
188
+ box-shadow: 0 8px 24px rgba(15, 20, 25, 0.12); max-height: 240px; overflow-y: auto;
189
+ }
190
+ .fd-option { padding: 6px 12px; cursor: pointer; }
191
+ .fd-option:hover, .fd-option.fd-active { background: var(--fd-accent-soft); }
192
+ .fd-empty { padding: 6px 12px; color: var(--fd-muted); font-style: italic; }
193
+ .fd-tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; min-width: 0; }
194
+ .fd-chips { display: contents; list-style: none; margin: 0; padding: 0; }
195
+ .fd-chip {
196
+ display: inline-flex; align-items: center; gap: 4px; padding: 1px 4px 1px 10px; border-radius: 999px;
197
+ background: var(--fd-accent-soft); color: var(--fd-text); font-size: 13px; line-height: 22px;
198
+ }
199
+ .fd-chip-remove { border: none; background: none; cursor: pointer; color: var(--fd-muted); font-size: 15px; line-height: 1; padding: 0 4px; border-radius: 999px; }
200
+ .fd-chip-remove:hover { color: var(--fd-error); }
201
+ .fd-tags .fd-combo { flex: 1 1 140px; }
202
+ .fd-reference { display: grid; grid-template-columns: minmax(7em, 34%) minmax(0, 1fr); gap: 8px; }
203
+
204
+ /* ---- one2many lines ------------------------------------------------------- */
205
+ .fd-lines { display: grid; gap: 6px; min-width: 0; }
206
+ .fd-lines-scroll { overflow-x: auto; }
207
+ .fd-lines-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
208
+ .fd-lines-table th { text-align: start; font-weight: 600; color: var(--fd-muted); font-size: 12.5px; padding: 6px 8px; border-block-end: 1px solid var(--fd-border); white-space: nowrap; }
209
+ .fd-lines-table td { padding: 4px 8px; border-block-end: 1px solid var(--fd-border); vertical-align: top; min-width: 7em; }
210
+ .fd-lines-table td.fd-lines-tools, .fd-lines-table th.fd-lines-tools { width: 32px; min-width: 32px; padding-inline: 0; text-align: center; }
211
+ .fd-lines-table .fd-input { min-height: 28px; }
212
+ .fd-line-delete { border: none; background: none; cursor: pointer; color: var(--fd-muted); font-size: 16px; line-height: 1; padding: 4px 6px; border-radius: 4px; }
213
+ .fd-line-delete:hover { color: var(--fd-error); background: var(--fd-error-soft); }
214
+ .fd-lines-add { justify-self: start; }
215
+ .fd-cell-error { color: var(--fd-error); font-size: 12px; }
216
+ .fd-sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
217
+
218
+ /* ---- files, images, rich text, json ------------------------------------ */
219
+ .fd-file { display: grid; gap: 8px; justify-items: start; padding: 6px 0; border-radius: var(--fd-control-radius); }
220
+ .fd-file.fd-dragging { outline: 2px dashed var(--fd-accent); outline-offset: 4px; }
221
+ .fd-file-pick { display: inline-flex; flex-wrap: wrap; gap: 8px; align-items: center; cursor: pointer; }
222
+ .fd-file-chosen { display: inline-flex; flex-wrap: wrap; gap: 4px 10px; align-items: center; }
223
+ .fd-file-name { font-variant-numeric: tabular-nums; }
224
+ .fd-image-pick {
225
+ width: 120px; height: 120px; border: 1px dashed var(--fd-border-strong); border-radius: var(--fd-control-radius);
226
+ display: grid; place-content: center; justify-items: center; text-align: center; padding: 8px; background: var(--fd-page);
227
+ }
228
+ .fd-image-pick .fd-button { border: none; background: none; color: var(--fd-accent); padding: 0; min-height: 0; }
229
+ .fd-image-pick:hover { border-color: var(--fd-accent); }
230
+ .fd-avatar .fd-image-pick { width: 88px; height: 88px; }
231
+ .fd-image-preview { max-width: 160px; max-height: 160px; border-radius: var(--fd-control-radius); border: 1px solid var(--fd-border); object-fit: cover; background: var(--fd-page); }
232
+ .fd-richtext { min-height: 96px; line-height: 1.5; overflow-wrap: anywhere; }
233
+ .fd-richtext[contenteditable="false"] { background: transparent; border-color: transparent; padding-inline: 0; min-height: 0; }
234
+ .fd-richtext p { margin: 0 0 6px; }
235
+ .fd-code { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 12.5px; min-height: 96px; }
236
+
237
+ /* ---- sections, tabs ---------------------------------------------------- */
238
+ .fd-sections { display: grid; gap: 24px; }
239
+ .fd-section { border: 0; margin: 0; padding: 0; min-width: 0; display: grid; gap: 14px; }
240
+ .fd-section-title { font-size: 15px; font-weight: 650; padding: 0; margin: 0; float: inline-start; width: 100%; }
241
+ .fd-section-title + * { clear: both; }
242
+ .fd-form[data-fd-skin="underline"] .fd-section-title {
243
+ text-transform: uppercase; letter-spacing: 0.05em; font-size: 12.5px; color: var(--fd-muted);
244
+ border-block-end: 1px solid var(--fd-border); padding-block-end: 6px; width: 100%;
245
+ }
246
+ .fd-section-description { color: var(--fd-muted); margin: 0; }
247
+ .fd-form[data-fd-skin="outlined"] .fd-sections > .fd-section {
248
+ background: var(--fd-surface); border: 1px solid var(--fd-border); border-radius: var(--fd-radius); padding: 20px 24px;
249
+ }
250
+ .fd-tablist { display: flex; gap: 2px; border-block-end: 1px solid var(--fd-border); overflow-x: auto; }
251
+ .fd-tab {
252
+ font: inherit; background: none; border: none; cursor: pointer; padding: 8px 14px; color: var(--fd-muted);
253
+ border-block-end: 2px solid transparent; margin-block-end: -1px; white-space: nowrap;
254
+ }
255
+ .fd-tab[aria-selected="true"] { color: var(--fd-accent); border-block-end-color: var(--fd-accent); font-weight: 600; }
256
+ .fd-tabpanel { padding-block: 16px; display: grid; gap: 16px; }
257
+
258
+ /* ---- buttons ---------------------------------------------------------- */
259
+ .fd-button {
260
+ font: inherit; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; justify-content: center;
261
+ min-height: 32px; padding: 4px 14px; border-radius: var(--fd-control-radius);
262
+ border: 1px solid var(--fd-border); background: var(--fd-surface); color: var(--fd-text);
263
+ }
264
+ .fd-button:hover { border-color: var(--fd-accent); color: var(--fd-accent); }
265
+ .fd-button-primary { background: var(--fd-accent); border-color: var(--fd-accent); color: var(--fd-accent-text); }
266
+ .fd-button-primary:hover { color: var(--fd-accent-text); filter: brightness(1.12); }
267
+ .fd-button-danger { color: var(--fd-error); border-color: var(--fd-error); }
268
+ .fd-button-danger:hover { background: var(--fd-error); color: #fff; }
269
+ .fd-button-link { border-color: transparent; background: none; color: var(--fd-accent); padding-inline: 4px; }
270
+ .fd-form[data-fd-skin="underline"] .fd-button { border-radius: 3px; }
271
+ .fd-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
272
+ .fd-actions-end { justify-content: flex-end; margin-block-start: 24px; }
273
+
274
+ /* ---- record sheet --------------------------------------------------------- */
275
+ .fd-sheet-page { background: var(--fd-page); padding: 0 0 32px; min-width: 0; }
276
+ .fd-header {
277
+ display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: center; justify-content: space-between;
278
+ background: var(--fd-surface); border-block-end: 1px solid var(--fd-border); padding: 8px 16px;
279
+ }
280
+ .fd-statusbar { display: flex; list-style: none; margin: 0; padding: 0; overflow-x: auto; }
281
+ .fd-statusbar li { display: flex; }
282
+ .fd-statusbar button, .fd-statusbar span {
283
+ font: inherit; font-size: 13px; border: none; background: var(--fd-page); color: var(--fd-muted);
284
+ padding: 5px 18px 5px 22px; margin-inline-start: -6px; white-space: nowrap;
285
+ clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 50%, calc(100% - 10px) 100%, 0 100%, 10px 50%);
286
+ }
287
+ .fd-statusbar li:first-child button, .fd-statusbar li:first-child span { margin-inline-start: 0; padding-inline-start: 14px;
288
+ clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 50%, calc(100% - 10px) 100%, 0 100%); }
289
+ .fd-statusbar button { cursor: pointer; }
290
+ .fd-statusbar [aria-current="step"] { background: var(--fd-accent); color: var(--fd-accent-text); font-weight: 600; }
291
+ [dir="rtl"] .fd-statusbar button, [dir="rtl"] .fd-statusbar span { transform: scaleX(-1); }
292
+ [dir="rtl"] .fd-statusbar button > *, [dir="rtl"] .fd-statusbar span > * { display: inline-block; transform: scaleX(-1); }
293
+ .fd-sheet-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; max-width: 1180px; margin: 16px auto 0; padding-inline: 16px; }
294
+ .fd-sheet-layout.fd-has-side { grid-template-columns: minmax(0, 1fr) minmax(220px, 300px); }
295
+ @container (max-width: 860px) { .fd-sheet-layout.fd-has-side { grid-template-columns: minmax(0, 1fr); } }
296
+ .fd-card {
297
+ position: relative; overflow: hidden; background: var(--fd-surface); border: 1px solid var(--fd-border);
298
+ border-radius: var(--fd-radius); padding: 24px 28px 28px; display: grid; gap: 18px; min-width: 0;
299
+ }
300
+ .fd-ribbon {
301
+ position: absolute; inset-block-start: 18px; inset-inline-end: -42px; transform: rotate(45deg); width: 160px;
302
+ text-align: center; font-size: 12px; font-weight: 700; padding: 4px 0; color: #fff; background: var(--fd-muted);
303
+ text-transform: uppercase; letter-spacing: 0.06em; pointer-events: none;
304
+ }
305
+ [dir="rtl"] .fd-ribbon { transform: rotate(-45deg); }
306
+ .fd-ribbon.fd-tone-danger { background: var(--fd-error); }
307
+ .fd-ribbon.fd-tone-success { background: var(--fd-success); }
308
+ .fd-ribbon.fd-tone-warning { background: #d97706; }
309
+ .fd-ribbon.fd-tone-info { background: var(--fd-info); }
310
+ .fd-alert { padding: 10px 14px; border-radius: var(--fd-control-radius); border: 1px solid; font-size: 13.5px; }
311
+ .fd-alert.fd-tone-warning { background: var(--fd-warning-soft); border-color: #f0c47a; color: var(--fd-warning); }
312
+ .fd-alert.fd-tone-danger { background: var(--fd-error-soft); border-color: #f1a7a7; color: var(--fd-error); }
313
+ .fd-alert.fd-tone-success { background: var(--fd-success-soft); border-color: #a7d7ab; color: var(--fd-success); }
314
+ .fd-alert.fd-tone-info, .fd-alert:not([class*="fd-tone-"]) { background: var(--fd-info-soft); border-color: #a9c8ef; color: var(--fd-info); }
315
+ .fd-stats { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0; margin: -24px -28px 0; border-block-end: 1px solid var(--fd-border); }
316
+ .fd-stats:empty { display: none; }
317
+ /* A visible ribbon owns the corner: stat buttons keep clear of it. */
318
+ .fd-card:has(> .fd-ribbon:not([hidden])) .fd-stats { padding-inline-end: 104px; }
319
+ .fd-stat {
320
+ font: inherit; background: none; border: none; border-inline-start: 1px solid var(--fd-border); cursor: pointer;
321
+ padding: 8px 18px; display: grid; justify-items: start; line-height: 1.2; color: var(--fd-text); min-width: 120px;
322
+ }
323
+ .fd-stat:hover { background: var(--fd-page); }
324
+ .fd-stat-value { font-weight: 700; font-size: 15px; color: var(--fd-accent); font-variant-numeric: tabular-nums; }
325
+ .fd-stat-label { font-size: 12.5px; color: var(--fd-muted); }
326
+ .fd-title-row { display: flex; gap: 16px; align-items: flex-start; justify-content: space-between; }
327
+ .fd-title { display: grid; gap: 4px; flex: 1 1 auto; min-width: 0; }
328
+ .fd-avatar { flex: none; }
329
+ .fd-avatar > .fd-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
330
+ .fd-form[data-fd-skin="underline"] .fd-avatar { grid-template-columns: minmax(0, 1fr); }
331
+ .fd-form[data-fd-skin="underline"] .fd-avatar > * { grid-column: 1 !important; }
332
+ .fd-avatar .fd-image-pick {
333
+ width: 120px; height: 120px; border: 1px dashed var(--fd-border-strong); border-radius: var(--fd-control-radius);
334
+ display: grid; place-content: center; justify-items: center; text-align: center; padding: 8px; background: var(--fd-page);
335
+ }
336
+ .fd-image-pick .fd-button { border: none; background: none; color: var(--fd-accent); padding: 0; min-height: 0; }
337
+ .fd-image-pick:hover { border-color: var(--fd-accent); }
338
+ .fd-avatar .fd-image-pick { width: 88px; height: 88px; }
339
+ .fd-image-preview { width: 88px; height: 88px; }
340
+ .fd-avatar .fd-file-pick .fd-help { display: none; }
341
+ .fd-avatar .fd-file-name { display: none; }
342
+ .fd-avatar .fd-file { justify-items: center; gap: 2px; padding: 0; }
343
+ .fd-avatar .fd-file-chosen { gap: 2px 8px; justify-content: center; font-size: 12.5px; }
344
+ .fd-title .fd-input { font-size: 24px; font-weight: 600; min-height: 40px; }
345
+ .fd-form[data-fd-skin="underline"] .fd-title .fd-field { grid-template-columns: minmax(0, 1fr); }
346
+ .fd-form[data-fd-skin="underline"] .fd-title .fd-field > * { grid-column: 1; }
347
+ .fd-title .fd-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
348
+ .fd-side { display: grid; gap: 12px; align-content: start; min-width: 0; }
349
+
350
+ /* ---- wizard -------------------------------------------------------------- */
351
+ .fd-wizard { display: grid; gap: 20px; }
352
+ .fd-progress { display: grid; gap: 8px; }
353
+ .fd-progress-text { color: var(--fd-muted); font-size: 13px; }
354
+ .fd-progress-bar { height: 4px; background: var(--fd-border); border-radius: 999px; overflow: hidden; }
355
+ .fd-progress-bar > span { display: block; height: 100%; background: var(--fd-accent); transition: width 0.25s ease; }
356
+ .fd-step { display: grid; gap: 18px; }
357
+ .fd-step-title { font-size: 18px; font-weight: 650; margin: 0; }
358
+ .fd-wizard-nav { display: flex; gap: 8px; justify-content: space-between; }
359
+ .fd-wizard-nav .fd-spacer { flex: 1; }
360
+
361
+ /* ---- text, status, dialog ------------------------------------------------- */
362
+ .fd-text-heading { font-size: 16px; font-weight: 650; margin: 0; }
363
+ .fd-text-paragraph { margin: 0; }
364
+ .fd-text-note { margin: 0; color: var(--fd-muted); font-size: 13px; }
365
+ .fd-status { min-height: 1.4em; color: var(--fd-muted); font-size: 13px; }
366
+ .fd-status.fd-status-error { color: var(--fd-error); }
367
+ .fd-status.fd-status-saved { color: var(--fd-success); }
368
+ .fd-draft { display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center; padding: 10px 14px; margin-block-end: 16px;
369
+ background: var(--fd-info-soft); border: 1px solid #a9c8ef; border-radius: var(--fd-control-radius); }
370
+ .fd-done { padding: 40px 0; text-align: center; display: grid; gap: 12px; justify-items: center; }
371
+ .fd-done-title { font-size: 20px; font-weight: 650; margin: 0; }
372
+ .fd-dialog-backdrop { position: fixed; inset: 0; background: rgba(15, 20, 25, 0.45); display: grid; place-items: center; z-index: 1000; padding: 16px; }
373
+ .fd-dialog {
374
+ background: var(--fd-surface); color: var(--fd-text); border-radius: max(var(--fd-radius), 6px); max-width: 420px; width: 100%;
375
+ padding: 20px 22px; display: grid; gap: 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); font-family: var(--fd-font);
376
+ }
377
+ .fd-dialog p { margin: 0; }
378
+ @media (prefers-reduced-motion: reduce) { .fd-form *, .fd-form *::before, .fd-form *::after { transition: none !important; } }
379
+ `;
380
+ const STYLE_ID = 'fieldia-styles';
381
+ /** Add Fieldia's stylesheet to a document, once. */
382
+ export function installStyles(document) {
383
+ if (document.getElementById(STYLE_ID))
384
+ return;
385
+ const style = document.createElement('style');
386
+ style.id = STYLE_ID;
387
+ style.textContent = FIELDIA_CSS;
388
+ (document.head ?? document.documentElement).append(style);
389
+ }
390
+ //# sourceMappingURL=styles.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.mjs","sourceRoot":"","sources":["../../../../../libs/widgets/src/lib/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6WpC,CAAC;AAEF,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAElC,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,QAAkB;IAC9C,IAAI,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAAC;QAAE,OAAO;IAC9C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,GAAG,QAAQ,CAAC;IACpB,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;IAChC,CAAC,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { Field, FieldNode, Form, Value, Values } from '@fieldia/core';
2
+ import type { WidgetLabels } from './labels.mjs';
3
+ /**
4
+ * Field inputs in plain DOM. Each widget builds its element once and then only
5
+ * patches it from state, so typing never loses focus or the caret, and the
6
+ * same widget serves every framework binding.
7
+ */
8
+ export interface WidgetContext {
9
+ form: Form;
10
+ /** The field's name in the page. */
11
+ name: string;
12
+ field: Field;
13
+ node: FieldNode;
14
+ /** The id the field's label points at. */
15
+ id: string;
16
+ document: Document;
17
+ /** Words the widget shows itself, in the page's language. English when left out. */
18
+ labels?: WidgetLabels;
19
+ }
20
+ export interface WidgetState {
21
+ value: Value | undefined;
22
+ /** Every value of the record, for widgets that show one field beside another. */
23
+ values: Readonly<Values>;
24
+ readonly: boolean;
25
+ required: boolean;
26
+ invalid: boolean;
27
+ /** Ids of the help and error text, for assistive technology. */
28
+ describedBy?: string;
29
+ }
30
+ export interface Widget {
31
+ element: HTMLElement;
32
+ update(state: WidgetState): void;
33
+ focus(): void;
34
+ destroy?(): void;
35
+ }
36
+ export type WidgetFactory = (context: WidgetContext) => Widget;
37
+ /**
38
+ * Pick the widget for a field: the app's or the built-in one for
39
+ * `type.widget` (such as `selection.radio`), then for the type alone. A type
40
+ * with no editor yet shows its value read-only.
41
+ */
42
+ export declare function createWidget(context: WidgetContext, registry?: Record<string, WidgetFactory>): Widget;
43
+ export declare function summary(value: Value | undefined, field: Field): string;
44
+ export declare const builtInWidgets: Record<string, WidgetFactory>;
@@ -0,0 +1,44 @@
1
+ import type { Field, FieldNode, Form, Value, Values } from '@fieldia/core';
2
+ import type { WidgetLabels } from './labels';
3
+ /**
4
+ * Field inputs in plain DOM. Each widget builds its element once and then only
5
+ * patches it from state, so typing never loses focus or the caret, and the
6
+ * same widget serves every framework binding.
7
+ */
8
+ export interface WidgetContext {
9
+ form: Form;
10
+ /** The field's name in the page. */
11
+ name: string;
12
+ field: Field;
13
+ node: FieldNode;
14
+ /** The id the field's label points at. */
15
+ id: string;
16
+ document: Document;
17
+ /** Words the widget shows itself, in the page's language. English when left out. */
18
+ labels?: WidgetLabels;
19
+ }
20
+ export interface WidgetState {
21
+ value: Value | undefined;
22
+ /** Every value of the record, for widgets that show one field beside another. */
23
+ values: Readonly<Values>;
24
+ readonly: boolean;
25
+ required: boolean;
26
+ invalid: boolean;
27
+ /** Ids of the help and error text, for assistive technology. */
28
+ describedBy?: string;
29
+ }
30
+ export interface Widget {
31
+ element: HTMLElement;
32
+ update(state: WidgetState): void;
33
+ focus(): void;
34
+ destroy?(): void;
35
+ }
36
+ export type WidgetFactory = (context: WidgetContext) => Widget;
37
+ /**
38
+ * Pick the widget for a field: the app's or the built-in one for
39
+ * `type.widget` (such as `selection.radio`), then for the type alone. A type
40
+ * with no editor yet shows its value read-only.
41
+ */
42
+ export declare function createWidget(context: WidgetContext, registry?: Record<string, WidgetFactory>): Widget;
43
+ export declare function summary(value: Value | undefined, field: Field): string;
44
+ export declare const builtInWidgets: Record<string, WidgetFactory>;