@gramlot/gramlot-examples 0.2.15 → 0.3.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 +12 -5
- package/package.json +4 -3
- package/src/gramlot_examples/catalog.json +163 -1
- package/src/gramlot_examples/gallery/collections.js +4 -0
- package/src/gramlot_examples/gallery/dist/frame.js +88 -28
- package/src/gramlot_examples/gallery/dist/gallery.js +211 -52
- package/src/gramlot_examples/gallery/dist/inputs.js +24456 -0
- package/src/gramlot_examples/gallery/dist/inspector.js +42 -18
- package/src/gramlot_examples/gallery/dist/layout.js +541 -0
- package/src/gramlot_examples/gallery/dist/theme-styles.js +2 -2
- package/src/gramlot_examples/gallery/gallery-page.js +49 -17
- package/src/gramlot_examples/gallery/gallery.css +50 -4
- package/src/gramlot_examples/index.js +3 -2
- package/src/gramlot_examples/pages/README.md +5 -2
- package/src/gramlot_examples/pages/binding/06_style_and_visibility.md +77 -12
- package/src/gramlot_examples/pages/binding/09_forms.js +1 -1
- package/src/gramlot_examples/pages/components/01_textbox.js +32 -0
- package/src/gramlot_examples/pages/components/01_textbox.md +14 -0
- package/src/gramlot_examples/pages/components/02_checkbox.js +25 -0
- package/src/gramlot_examples/pages/components/02_checkbox.md +14 -0
- package/src/gramlot_examples/pages/components/03_radio_button_text.js +30 -0
- package/src/gramlot_examples/pages/components/03_radio_button_text.md +16 -0
- package/src/gramlot_examples/pages/components/04_text_box_area.js +23 -0
- package/src/gramlot_examples/pages/components/04_text_box_area.md +14 -0
- package/src/gramlot_examples/pages/components/05_passwordbox.js +23 -0
- package/src/gramlot_examples/pages/components/05_passwordbox.md +14 -0
- package/src/gramlot_examples/pages/components/06_radio_button.js +28 -0
- package/src/gramlot_examples/pages/components/06_radio_button.md +14 -0
- package/src/gramlot_examples/pages/components/07_colorpicker.js +24 -0
- package/src/gramlot_examples/pages/components/07_colorpicker.md +14 -0
- package/src/gramlot_examples/pages/components/08_horizontal_slider.js +24 -0
- package/src/gramlot_examples/pages/components/08_horizontal_slider.md +14 -0
- package/src/gramlot_examples/pages/components/09_vertical_slider.js +24 -0
- package/src/gramlot_examples/pages/components/09_vertical_slider.md +14 -0
- package/src/gramlot_examples/pages/components/10_number_textbox.js +30 -0
- package/src/gramlot_examples/pages/components/10_number_textbox.md +16 -0
- package/src/gramlot_examples/pages/components/11_date_textbox.js +23 -0
- package/src/gramlot_examples/pages/components/11_date_textbox.md +18 -0
- package/src/gramlot_examples/pages/components/12_time_textbox.js +23 -0
- package/src/gramlot_examples/pages/components/12_time_textbox.md +16 -0
- package/src/gramlot_examples/pages/components/13_filtering_select.js +23 -0
- package/src/gramlot_examples/pages/components/13_filtering_select.md +20 -0
- package/src/gramlot_examples/pages/components/14_callback_select.js +45 -0
- package/src/gramlot_examples/pages/components/14_callback_select.md +24 -0
- package/src/gramlot_examples/pages/components/15_combo_box.js +23 -0
- package/src/gramlot_examples/pages/components/15_combo_box.md +22 -0
- package/src/gramlot_examples/pages/components/16_checkbox_text.js +23 -0
- package/src/gramlot_examples/pages/components/16_checkbox_text.md +23 -0
- package/src/gramlot_examples/pages/components/17_date_calendar.js +25 -0
- package/src/gramlot_examples/pages/components/17_date_calendar.md +22 -0
- package/src/gramlot_examples/pages/components/18_validation.js +31 -0
- package/src/gramlot_examples/pages/components/18_validation.md +29 -0
- package/src/gramlot_examples/pages/components/19_formlet.js +26 -0
- package/src/gramlot_examples/pages/components/19_formlet.md +12 -0
- package/src/gramlot_examples/pages/components/20_group_box.js +26 -0
- package/src/gramlot_examples/pages/components/20_group_box.md +12 -0
- package/src/gramlot_examples/pages/components/21_switch.js +19 -0
- package/src/gramlot_examples/pages/components/21_switch.md +12 -0
- package/src/gramlot_examples/pages/components/22_button.js +18 -0
- package/src/gramlot_examples/pages/components/22_button.md +12 -0
- package/src/gramlot_examples/pages/components/23_toggle_button.js +18 -0
- package/src/gramlot_examples/pages/components/23_toggle_button.md +12 -0
- package/src/gramlot_examples/pages/components/24_dropdown_button.js +21 -0
- package/src/gramlot_examples/pages/components/24_dropdown_button.md +12 -0
- package/src/gramlot_examples/pages/components/25_validation_engine.js +51 -0
- package/src/gramlot_examples/pages/components/25_validation_engine.md +26 -0
- package/src/gramlot_examples/pages/components/README.md +40 -0
- package/src/gramlot_examples/pages/controllers/02_controller.js +1 -1
- package/src/gramlot_examples/pages/controllers/16_attribute_projection.js +3 -3
- package/src/gramlot_examples/pages/controllers/16_attribute_projection.md +8 -8
- package/src/gramlot_examples/pages/controllers/22_pointer_transforms.js +39 -0
- package/src/gramlot_examples/pages/controllers/22_pointer_transforms.md +64 -0
- package/src/gramlot_examples/pages/controllers/README.md +3 -0
- package/src/gramlot_examples/pages/html_svg/06_forms.js +3 -3
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
// node_modules/@gramlot/gramlot/src/webcomponent.js
|
|
2
|
+
var NAME = /^[a-z][.0-9_a-z\-\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u{10000}-\u{EFFFF}]*$/u;
|
|
3
|
+
var RESERVED = /* @__PURE__ */ new Set([
|
|
4
|
+
"annotation-xml",
|
|
5
|
+
"color-profile",
|
|
6
|
+
"font-face",
|
|
7
|
+
"font-face-src",
|
|
8
|
+
"font-face-uri",
|
|
9
|
+
"font-face-format",
|
|
10
|
+
"font-face-name",
|
|
11
|
+
"missing-glyph"
|
|
12
|
+
]);
|
|
13
|
+
function webcomponentCode(attrs) {
|
|
14
|
+
const code = attrs.code;
|
|
15
|
+
if (typeof code !== "string" || NAME.exec(code)?.[0] !== code || !code.includes("-") || RESERVED.has(code)) {
|
|
16
|
+
throw new TypeError("webcomponent: code must be a literal custom-element name");
|
|
17
|
+
}
|
|
18
|
+
return code;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// node_modules/@gramlot/gramlot/src/components/registry.js
|
|
22
|
+
function freeze(value) {
|
|
23
|
+
for (const child of Object.values(value)) if (child && typeof child === "object") freeze(child);
|
|
24
|
+
return Object.freeze(value);
|
|
25
|
+
}
|
|
26
|
+
function annotation(parameter) {
|
|
27
|
+
if (typeof parameter.dtype !== "string" && !Array.isArray(parameter.dtype)) throw new TypeError("Component parameter requires dtype");
|
|
28
|
+
const alternatives = (Array.isArray(parameter.dtype) ? parameter.dtype : [parameter.dtype]).map((dtype) => ({ kind: "dtype", dtype }));
|
|
29
|
+
if (parameter.nullable) alternatives.push({ kind: "dtype", dtype: "NN" });
|
|
30
|
+
if (parameter.binding && parameter.dtype !== "T") alternatives.push({
|
|
31
|
+
kind: "annotated",
|
|
32
|
+
base: { kind: "dtype", dtype: "T" },
|
|
33
|
+
metadata: [{ kind: "regex", pattern: "^[\\^=].+", flags: 0 }]
|
|
34
|
+
});
|
|
35
|
+
return alternatives.length === 1 ? alternatives[0] : { kind: "union", items: alternatives };
|
|
36
|
+
}
|
|
37
|
+
function defineComponent({ name, tag, base, mixins = [], parameters = {}, subTags = "", presentationDefaults = [], dataScope = false, command = false, implementation }) {
|
|
38
|
+
webcomponentCode({ code: tag });
|
|
39
|
+
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(name)) throw new TypeError("Invalid component authoring name");
|
|
40
|
+
const effective = { ...base.parameters };
|
|
41
|
+
for (const mixin of mixins) {
|
|
42
|
+
for (const [key, spec] of Object.entries(mixin.parameters)) {
|
|
43
|
+
if (Object.hasOwn(effective, key)) throw new Error(`Duplicate mixin parameter: ${key}`);
|
|
44
|
+
effective[key] = spec;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
Object.assign(effective, parameters);
|
|
48
|
+
for (const spec of Object.values(effective)) annotation(spec);
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
name,
|
|
51
|
+
tag,
|
|
52
|
+
base,
|
|
53
|
+
subTags,
|
|
54
|
+
presentationDefaults: Object.freeze([...presentationDefaults]),
|
|
55
|
+
dataScope,
|
|
56
|
+
command,
|
|
57
|
+
mixins: Object.freeze([...mixins]),
|
|
58
|
+
parameters: freeze(structuredClone(effective)),
|
|
59
|
+
implementation
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
var ComponentCollection = class {
|
|
63
|
+
#classes = /* @__PURE__ */ new WeakMap();
|
|
64
|
+
constructor({ name, components }) {
|
|
65
|
+
this.name = name;
|
|
66
|
+
this.components = Object.freeze([...components]);
|
|
67
|
+
const names = /* @__PURE__ */ new Set(), tags = /* @__PURE__ */ new Set();
|
|
68
|
+
for (const component of components) {
|
|
69
|
+
if (names.has(component.name.toLowerCase()) || tags.has(component.tag)) {
|
|
70
|
+
throw new Error(`Duplicate component: ${component.name}`);
|
|
71
|
+
}
|
|
72
|
+
names.add(component.name.toLowerCase());
|
|
73
|
+
tags.add(component.tag);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Same JSON consumed by Python and JavaScript Builders. */
|
|
77
|
+
toDocument() {
|
|
78
|
+
return {
|
|
79
|
+
document_format: { name: "builder_grammar", version: "1.1" },
|
|
80
|
+
grammar: { name: this.name, version: null, title: this.name, description: null },
|
|
81
|
+
abstracts: {},
|
|
82
|
+
elements: Object.fromEntries(this.components.map((component) => [component.name, {
|
|
83
|
+
doc: null,
|
|
84
|
+
sub_tags: component.subTags,
|
|
85
|
+
parent_tags: null,
|
|
86
|
+
inherits_from: null,
|
|
87
|
+
ns: null,
|
|
88
|
+
node_label: null,
|
|
89
|
+
collection_key: null,
|
|
90
|
+
attributes: {
|
|
91
|
+
parameters: [
|
|
92
|
+
...Object.entries(component.parameters).map(([name, spec]) => ({
|
|
93
|
+
name,
|
|
94
|
+
kind: "keyword_only",
|
|
95
|
+
role: "attribute",
|
|
96
|
+
annotation: annotation(spec),
|
|
97
|
+
has_default: true,
|
|
98
|
+
default: spec.default ?? null
|
|
99
|
+
})),
|
|
100
|
+
{
|
|
101
|
+
name: "attr",
|
|
102
|
+
kind: "var_keyword",
|
|
103
|
+
role: "attribute",
|
|
104
|
+
annotation: { kind: "any" },
|
|
105
|
+
has_default: false
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
accepts_var_keyword: true,
|
|
109
|
+
accepts_var_positional: false
|
|
110
|
+
},
|
|
111
|
+
_meta: { render_tag: component.tag, gramlot_component: {
|
|
112
|
+
parameters: structuredClone(component.parameters),
|
|
113
|
+
presentationDefaults: [...component.presentationDefaults],
|
|
114
|
+
dataScope: component.dataScope,
|
|
115
|
+
command: component.command
|
|
116
|
+
} }
|
|
117
|
+
}]))
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/** Register once per document realm, only when the collection is activated. */
|
|
121
|
+
register(window) {
|
|
122
|
+
if (this.#classes.has(window)) return;
|
|
123
|
+
const classes = [];
|
|
124
|
+
for (const component of this.components) {
|
|
125
|
+
if (window.customElements.get(component.tag)) throw new Error(`Custom element already registered: ${component.tag}`);
|
|
126
|
+
let Base = component.base(window.HTMLElement);
|
|
127
|
+
for (const mixin of component.mixins) Base = mixin(Base);
|
|
128
|
+
const Class = component.implementation(Base);
|
|
129
|
+
Object.defineProperty(Class, "gramlotComponent", { value: component });
|
|
130
|
+
classes.push([component.tag, Class]);
|
|
131
|
+
}
|
|
132
|
+
for (const [tag, Class] of classes) window.customElements.define(tag, Class);
|
|
133
|
+
this.#classes.set(window, classes);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
function defineCollection(options) {
|
|
137
|
+
return new ComponentCollection(options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// node_modules/@gramlot/gramlot/src/components/labelbox.js
|
|
141
|
+
var POSITIONS = ["TL", "TC", "TR", "L", "R", "BL", "BC", "BR"];
|
|
142
|
+
function project(element, attrs, adapt, previous) {
|
|
143
|
+
const next = adapt(attrs);
|
|
144
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)])) {
|
|
145
|
+
if (next[key] == null) element.removeAttribute(key);
|
|
146
|
+
else element.setAttribute(key, String(next[key]));
|
|
147
|
+
}
|
|
148
|
+
return next;
|
|
149
|
+
}
|
|
150
|
+
function Labelbox(Base) {
|
|
151
|
+
return class extends Base {
|
|
152
|
+
#previous = { box: {}, label: {}, labelBox: {}, controlBox: {} };
|
|
153
|
+
constructor() {
|
|
154
|
+
super();
|
|
155
|
+
this.box = this.ownerDocument.createElement("div");
|
|
156
|
+
this.box.className = "labelbox";
|
|
157
|
+
this.labelBox = this.ownerDocument.createElement("div");
|
|
158
|
+
this.labelBox.className = "label-box";
|
|
159
|
+
this.fieldLabel = this.ownerDocument.createElement("label");
|
|
160
|
+
this.fieldLabel.id = "field-label";
|
|
161
|
+
this.fieldLabel.htmlFor = "control";
|
|
162
|
+
this.labelBox.append(this.fieldLabel);
|
|
163
|
+
this.box.append(this.labelBox, this.controlBox);
|
|
164
|
+
const style = this.ownerDocument.createElement("style");
|
|
165
|
+
style.textContent = `.labelbox{--gramlot-label-width:initial;--gramlot-formlet-label-width:initial;display:grid;gap:var(--gramlot-label-gap,.25em);grid-template-areas:'label' 'control'}
|
|
166
|
+
.label-box{grid-area:label;color:var(--gramlot-label-color,var(--gramlot-text-secondary,inherit));font-size:var(--gramlot-label-size,1em)} .control-box{grid-area:control}
|
|
167
|
+
.labelbox[data-position='L']{grid-template-areas:'label control';align-items:center}
|
|
168
|
+
.labelbox[data-position='R']{grid-template-areas:'control label';align-items:center}
|
|
169
|
+
.labelbox[data-position^='B']{grid-template-areas:'control' 'label'}
|
|
170
|
+
.labelbox[data-position$='C']>.label-box{text-align:center}
|
|
171
|
+
.labelbox[data-position$='R']>.label-box{text-align:right}
|
|
172
|
+
.label-box[hidden]{display:none} .label-box label{display:block}
|
|
173
|
+
.labelbox[data-position='L']>.label-box{text-align:right;padding-right:2px}
|
|
174
|
+
.labelbox[data-formlet-aligned]>.label-box>label{width:max-content;margin-left:auto}
|
|
175
|
+
.labelbox[data-position='L']{grid-template-columns:var(--gramlot-label-width,var(--gramlot-formlet-label-width,max-content)) minmax(0,1fr)}`;
|
|
176
|
+
this.shadowRoot.append(style, this.box);
|
|
177
|
+
}
|
|
178
|
+
projectAttributes(attrs, adapt) {
|
|
179
|
+
const rest = super.projectAttributes(attrs, adapt);
|
|
180
|
+
const { lbl, lbl_position = "TL" } = this.parameters;
|
|
181
|
+
if (!POSITIONS.includes(lbl_position)) throw new TypeError(`Invalid lbl_position: ${lbl_position}`);
|
|
182
|
+
const groups = { box: {}, label: {}, labelBox: {}, controlBox: {} };
|
|
183
|
+
const prefixes = [["box_l_", "labelBox"], ["box_c_", "controlBox"], ["lbl_", "label"], ["box_", "box"]];
|
|
184
|
+
for (const [name, value] of Object.entries(rest)) {
|
|
185
|
+
const match = prefixes.find(([prefix]) => name.startsWith(prefix));
|
|
186
|
+
if (match) {
|
|
187
|
+
groups[match[1]][name.slice(match[0].length)] = value;
|
|
188
|
+
delete rest[name];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const elements = { box: this.box, label: this.fieldLabel, labelBox: this.labelBox, controlBox: this.controlBox };
|
|
192
|
+
for (const [name, element] of Object.entries(elements)) {
|
|
193
|
+
this.#previous[name] = project(element, groups[name], adapt, this.#previous[name]);
|
|
194
|
+
}
|
|
195
|
+
if (attrs.lbl_width == null) this.box.style.removeProperty("--gramlot-label-width");
|
|
196
|
+
else this.box.style.setProperty("--gramlot-label-width", attrs.lbl_width);
|
|
197
|
+
this.box.classList.add("labelbox");
|
|
198
|
+
this.labelBox.classList.add("label-box");
|
|
199
|
+
this.controlBox.classList.add("control-box");
|
|
200
|
+
this.box.dataset.position = lbl_position;
|
|
201
|
+
this.fieldLabel.textContent = lbl ?? "";
|
|
202
|
+
this.labelBox.hidden = lbl == null || lbl === "";
|
|
203
|
+
if (this.control.localName !== "input") {
|
|
204
|
+
if (this.labelBox.hidden) this.control.removeAttribute("aria-labelledby");
|
|
205
|
+
else this.control.setAttribute("aria-labelledby", "field-label");
|
|
206
|
+
}
|
|
207
|
+
this.dispatchEvent(new this.ownerDocument.defaultView.Event("gramlot-label-layout", { bubbles: true }));
|
|
208
|
+
return rest;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
Labelbox.parameters = {
|
|
213
|
+
lbl: { dtype: "T", qualification: "intl_str", nullable: true, binding: "read" },
|
|
214
|
+
lbl_position: { dtype: "T", default: "TL", binding: "read", enum: POSITIONS }
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// node_modules/@gramlot/gramlot/src/components/parameters.js
|
|
218
|
+
function projectParameters(element, attrs) {
|
|
219
|
+
element.parameters = Object.fromEntries(Object.entries(element.constructor.gramlotComponent.parameters).map(([name, spec]) => [name, Object.hasOwn(attrs, name) ? attrs[name] : spec.default]));
|
|
220
|
+
return Object.fromEntries(Object.entries(attrs).filter(([name]) => !Object.hasOwn(element.parameters, name)));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// node_modules/@gramlot/gramlot/src/components/popup.js
|
|
224
|
+
var VIEWPORT_MARGIN = 8;
|
|
225
|
+
var ANCHOR_GAP = 3;
|
|
226
|
+
function positionPopup(popup, anchor, { align = "start", matchWidth = false } = {}) {
|
|
227
|
+
const view = anchor.ownerDocument.defaultView;
|
|
228
|
+
const field = anchor.getBoundingClientRect();
|
|
229
|
+
const availableWidth = Math.max(0, view.innerWidth - 2 * VIEWPORT_MARGIN);
|
|
230
|
+
popup.style.setProperty("--gramlot-popup-available-width", `${availableWidth}px`);
|
|
231
|
+
if (matchWidth) popup.style.width = `${Math.min(field.width, availableWidth)}px`;
|
|
232
|
+
popup.style.removeProperty("--gramlot-popup-available-height");
|
|
233
|
+
const natural = popup.getBoundingClientRect();
|
|
234
|
+
const below = Math.max(0, view.innerHeight - VIEWPORT_MARGIN - field.bottom - ANCHOR_GAP);
|
|
235
|
+
const above = Math.max(0, field.top - VIEWPORT_MARGIN - ANCHOR_GAP);
|
|
236
|
+
const upwards = natural.height > below && above > below;
|
|
237
|
+
popup.style.setProperty("--gramlot-popup-available-height", `${upwards ? above : below}px`);
|
|
238
|
+
const size = popup.getBoundingClientRect();
|
|
239
|
+
const left = align === "end" ? field.right - size.width : field.left;
|
|
240
|
+
const top = upwards ? field.top - size.height - ANCHOR_GAP : field.bottom + ANCHOR_GAP;
|
|
241
|
+
popup.style.left = `${Math.max(VIEWPORT_MARGIN, Math.min(left, view.innerWidth - size.width - VIEWPORT_MARGIN))}px`;
|
|
242
|
+
popup.style.top = `${Math.max(VIEWPORT_MARGIN, Math.min(top, view.innerHeight - size.height - VIEWPORT_MARGIN))}px`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// node_modules/@gramlot/gramlot/src/components/formlet-labels.js
|
|
246
|
+
var FormletLabels = class {
|
|
247
|
+
#fields = /* @__PURE__ */ new Set();
|
|
248
|
+
#observer;
|
|
249
|
+
#frame;
|
|
250
|
+
constructor(formlet) {
|
|
251
|
+
this.formlet = formlet;
|
|
252
|
+
this.window = formlet.ownerDocument.defaultView;
|
|
253
|
+
this.slot = formlet.control.querySelector("slot");
|
|
254
|
+
}
|
|
255
|
+
connect() {
|
|
256
|
+
this.#observer = new this.window.ResizeObserver(this.schedule);
|
|
257
|
+
this.#observer.observe(this.formlet.control);
|
|
258
|
+
this.slot.addEventListener("slotchange", this.schedule);
|
|
259
|
+
this.formlet.addEventListener("gramlot-label-layout", this.schedule);
|
|
260
|
+
this.formlet.ownerDocument.fonts.addEventListener("loadingdone", this.schedule);
|
|
261
|
+
this.schedule();
|
|
262
|
+
}
|
|
263
|
+
disconnect() {
|
|
264
|
+
this.#observer.disconnect();
|
|
265
|
+
this.window.cancelAnimationFrame(this.#frame);
|
|
266
|
+
this.#frame = void 0;
|
|
267
|
+
this.slot.removeEventListener("slotchange", this.schedule);
|
|
268
|
+
this.formlet.removeEventListener("gramlot-label-layout", this.schedule);
|
|
269
|
+
this.formlet.ownerDocument.fonts.removeEventListener("loadingdone", this.schedule);
|
|
270
|
+
for (const field of this.#fields) this.#clear(field);
|
|
271
|
+
this.#fields.clear();
|
|
272
|
+
}
|
|
273
|
+
schedule = () => {
|
|
274
|
+
if (!this.formlet.isConnected || this.#frame !== void 0) return;
|
|
275
|
+
this.#frame = this.window.requestAnimationFrame(() => {
|
|
276
|
+
this.#frame = void 0;
|
|
277
|
+
this.#align();
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
#clear(field) {
|
|
281
|
+
delete field.box.dataset.formletAligned;
|
|
282
|
+
field.box.style.removeProperty("--gramlot-formlet-label-width");
|
|
283
|
+
}
|
|
284
|
+
#align() {
|
|
285
|
+
const fields = new Set(this.slot.assignedElements().filter((field) => "fieldLabel" in field && field.box.dataset.position === "L"));
|
|
286
|
+
for (const field of this.#fields) if (!fields.has(field)) {
|
|
287
|
+
this.#observer.unobserve(field.fieldLabel);
|
|
288
|
+
this.#clear(field);
|
|
289
|
+
}
|
|
290
|
+
for (const field of fields) {
|
|
291
|
+
field.box.dataset.formletAligned = "";
|
|
292
|
+
if (!this.#fields.has(field)) this.#observer.observe(field.fieldLabel);
|
|
293
|
+
}
|
|
294
|
+
this.#fields = fields;
|
|
295
|
+
const columns = /* @__PURE__ */ new Map();
|
|
296
|
+
for (const field of fields) {
|
|
297
|
+
if (!field.getClientRects().length) continue;
|
|
298
|
+
const host = field.getBoundingClientRect();
|
|
299
|
+
const label = field.labelBox.getBoundingClientRect();
|
|
300
|
+
const style = this.window.getComputedStyle(field.labelBox);
|
|
301
|
+
const boxStyle = this.window.getComputedStyle(field.box);
|
|
302
|
+
const gap = parseFloat(boxStyle.columnGap) || 0;
|
|
303
|
+
const labelLeft = field.labelBox.hidden ? field.box.getBoundingClientRect().left + parseFloat(boxStyle.paddingLeft) + parseFloat(boxStyle.borderLeftWidth) : label.left;
|
|
304
|
+
const inset = labelLeft - host.left + gap;
|
|
305
|
+
const width = field.box.style.getPropertyValue("--gramlot-label-width") ? label.width : field.fieldLabel.getBoundingClientRect().width + parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth);
|
|
306
|
+
const column = columns.get(host.left) ?? [];
|
|
307
|
+
column.push({ field, inset, width });
|
|
308
|
+
columns.set(host.left, column);
|
|
309
|
+
}
|
|
310
|
+
for (const column of columns.values()) {
|
|
311
|
+
const edge = Math.max(...column.map(({ inset, width }) => inset + width));
|
|
312
|
+
for (const { field, inset } of column) {
|
|
313
|
+
field.box.style.setProperty("--gramlot-formlet-label-width", `${edge - inset}px`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// node_modules/@gramlot/gramlot/src/components/layout.js
|
|
320
|
+
var COPY_FEEDBACK_DURATION = 1600;
|
|
321
|
+
function Container(HTMLElement) {
|
|
322
|
+
return class extends HTMLElement {
|
|
323
|
+
constructor() {
|
|
324
|
+
super();
|
|
325
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
326
|
+
const style = this.ownerDocument.createElement("style");
|
|
327
|
+
style.textContent = `:host{display:block;min-width:0;font:inherit;color:var(--gramlot-ink,CanvasText)}
|
|
328
|
+
*,*::before,*::after{box-sizing:border-box}.control-box{min-width:0}slot{display:contents}
|
|
329
|
+
::slotted(*){min-width:0;max-width:100%}`;
|
|
330
|
+
this.controlBox = this.ownerDocument.createElement("div");
|
|
331
|
+
this.controlBox.className = "control-box";
|
|
332
|
+
this.control = this.controlBox;
|
|
333
|
+
this.control.id = "control";
|
|
334
|
+
this.control.append(this.ownerDocument.createElement("slot"));
|
|
335
|
+
shadow.append(style, this.controlBox);
|
|
336
|
+
}
|
|
337
|
+
projectAttributes(attrs) {
|
|
338
|
+
return projectParameters(this, attrs);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
Container.parameters = {};
|
|
343
|
+
var Formlet = defineComponent({
|
|
344
|
+
name: "formlet",
|
|
345
|
+
tag: "gramlot-formlet",
|
|
346
|
+
base: Container,
|
|
347
|
+
subTags: "*",
|
|
348
|
+
presentationDefaults: ["lbl_", "box_"],
|
|
349
|
+
parameters: {
|
|
350
|
+
columns: { dtype: ["L", "T"], default: 1, binding: "read" },
|
|
351
|
+
col_min_width: { dtype: "T", nullable: true, binding: "read" }
|
|
352
|
+
},
|
|
353
|
+
implementation: (Base) => class extends Base {
|
|
354
|
+
#labels;
|
|
355
|
+
constructor() {
|
|
356
|
+
super();
|
|
357
|
+
const style = this.ownerDocument.createElement("style");
|
|
358
|
+
style.textContent = `.control-box{display:grid;gap:inherit;align-items:start}
|
|
359
|
+
:host{gap:var(--gramlot-formlet-gap,8px 16px)}`;
|
|
360
|
+
this.shadowRoot.append(style);
|
|
361
|
+
this.#labels = new FormletLabels(this);
|
|
362
|
+
}
|
|
363
|
+
connectedCallback() {
|
|
364
|
+
this.#labels.connect();
|
|
365
|
+
}
|
|
366
|
+
disconnectedCallback() {
|
|
367
|
+
this.#labels.disconnect();
|
|
368
|
+
}
|
|
369
|
+
projectAttributes(attrs, adapt) {
|
|
370
|
+
const rest = super.projectAttributes(attrs, adapt);
|
|
371
|
+
const { columns, col_min_width } = this.parameters;
|
|
372
|
+
if (typeof columns === "number" && (!Number.isInteger(columns) || columns < 1)) {
|
|
373
|
+
throw new TypeError("formlet columns must be a positive integer or CSS track list");
|
|
374
|
+
}
|
|
375
|
+
this.control.style.gridTemplateColumns = col_min_width ? `repeat(auto-fit, minmax(min(100%, ${col_min_width}), 1fr))` : typeof columns === "number" ? `repeat(${columns}, minmax(0, 1fr))` : columns;
|
|
376
|
+
this.#labels.schedule();
|
|
377
|
+
return Object.fromEntries(Object.entries(rest).filter(([name]) => !/^(lbl_|box_)/.test(name)));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
var GroupBox = defineComponent({
|
|
382
|
+
name: "groupBox",
|
|
383
|
+
tag: "gramlot-group-box",
|
|
384
|
+
base: Container,
|
|
385
|
+
subTags: "*",
|
|
386
|
+
dataScope: true,
|
|
387
|
+
mixins: [Labelbox],
|
|
388
|
+
parameters: {
|
|
389
|
+
copy: { dtype: "B", default: false, binding: "read" },
|
|
390
|
+
lbl_variant: { dtype: "T", default: "bar", binding: "read", enum: ["bar", "underline"] }
|
|
391
|
+
},
|
|
392
|
+
implementation: (Base) => class extends Base {
|
|
393
|
+
#generation = 0;
|
|
394
|
+
#timer;
|
|
395
|
+
#busy = false;
|
|
396
|
+
#menuEvents;
|
|
397
|
+
#menuOpen = false;
|
|
398
|
+
constructor() {
|
|
399
|
+
super();
|
|
400
|
+
this.control.setAttribute("role", "group");
|
|
401
|
+
this.fieldLabel.removeAttribute("for");
|
|
402
|
+
const doc = this.ownerDocument;
|
|
403
|
+
const style = doc.createElement("style");
|
|
404
|
+
style.textContent = `.labelbox{gap:0;border:1px solid var(--gramlot-border-control,#aaa);border-radius:var(--gramlot-radius-sm,4px)}
|
|
405
|
+
.label-box{display:flex;align-items:center;gap:.4em;padding:var(--gramlot-group-header-padding,.35em .65em);background:var(--gramlot-surface-subtle,#eef1f5)}
|
|
406
|
+
.label-box[hidden]{display:none}.label-box label{flex:1;min-width:0}
|
|
407
|
+
.control-box{padding:var(--gramlot-group-padding,.65em)}.labelbox[data-variant=underline]{border:0}
|
|
408
|
+
[data-variant=underline]>.label-box{background:transparent;border-bottom:1px solid var(--gramlot-border-control,#aaa)}
|
|
409
|
+
.actions{display:flex;align-items:center}.actions[hidden]{display:none}
|
|
410
|
+
button{font:inherit;color:inherit;cursor:pointer;border:0;border-radius:var(--gramlot-radius-sm,4px);background:transparent;
|
|
411
|
+
padding:var(--gramlot-group-action-padding,.2em .4em);min-width:var(--gramlot-touch-target,var(--gramlot-group-action-size,24px));min-height:var(--gramlot-touch-target,var(--gramlot-group-action-size,24px))}
|
|
412
|
+
button:hover{background:var(--gramlot-field-readonly-background,#ddd)}button:disabled{opacity:.5}
|
|
413
|
+
button:focus-visible{outline:2px solid var(--gramlot-focus-ring,Highlight)}
|
|
414
|
+
.copy-menu{max-width:var(--gramlot-popup-available-width,100vw);position:fixed;inset:auto;margin:0;padding:var(--gramlot-popup-padding,4px);max-height:var(--gramlot-popup-available-height,100vh);overflow:auto;border:1px solid var(--gramlot-border-control,#aaa);
|
|
415
|
+
background:var(--gramlot-field-background,Canvas);color:var(--gramlot-ink,CanvasText);border-radius:var(--gramlot-radius-sm,4px)}
|
|
416
|
+
.copy-menu button{display:block;width:100%;text-align:left}
|
|
417
|
+
.status{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%)}`;
|
|
418
|
+
this.actions = doc.createElement("span");
|
|
419
|
+
this.actions.className = "actions";
|
|
420
|
+
this.copyButton = doc.createElement("button");
|
|
421
|
+
this.copyButton.type = "button";
|
|
422
|
+
this.copyButton.textContent = "\u29C9";
|
|
423
|
+
this.copyButton.title = "Copy TYTX";
|
|
424
|
+
this.copyButton.setAttribute("aria-label", "Copy TYTX");
|
|
425
|
+
this.menuButton = doc.createElement("button");
|
|
426
|
+
this.menuButton.type = "button";
|
|
427
|
+
this.menuButton.textContent = "\u25BE";
|
|
428
|
+
this.menuButton.setAttribute("aria-label", "Choose copy format");
|
|
429
|
+
this.menuButton.setAttribute("aria-expanded", "false");
|
|
430
|
+
this.copyMenu = doc.createElement("div");
|
|
431
|
+
this.copyMenu.className = "copy-menu";
|
|
432
|
+
this.copyMenu.id = "copy-menu";
|
|
433
|
+
this.copyMenu.setAttribute("popover", "auto");
|
|
434
|
+
this.menuButton.setAttribute("aria-controls", "copy-menu");
|
|
435
|
+
for (const format of ["TYTX", "JSON"]) {
|
|
436
|
+
const button = doc.createElement("button");
|
|
437
|
+
button.type = "button";
|
|
438
|
+
button.textContent = `Copy ${format}`;
|
|
439
|
+
button.addEventListener("click", () => {
|
|
440
|
+
this.closeCopyMenu();
|
|
441
|
+
this.copyData(format);
|
|
442
|
+
});
|
|
443
|
+
this.copyMenu.append(button);
|
|
444
|
+
}
|
|
445
|
+
this.status = doc.createElement("span");
|
|
446
|
+
this.status.className = "status";
|
|
447
|
+
this.status.setAttribute("role", "status");
|
|
448
|
+
this.copyButton.addEventListener("click", () => this.copyData("TYTX"));
|
|
449
|
+
this.menuButton.addEventListener("click", () => {
|
|
450
|
+
if (this.#menuOpen) {
|
|
451
|
+
this.closeCopyMenu();
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
this.copyMenu.showPopover();
|
|
455
|
+
this.#menuOpen = true;
|
|
456
|
+
positionPopup(this.copyMenu, this.menuButton, { align: "end" });
|
|
457
|
+
const view = doc.defaultView;
|
|
458
|
+
this.#menuEvents = new view.AbortController();
|
|
459
|
+
const options = { capture: true, signal: this.#menuEvents.signal };
|
|
460
|
+
view.addEventListener("scroll", (event) => {
|
|
461
|
+
if (!event.composedPath().includes(this.copyMenu)) this.closeCopyMenu();
|
|
462
|
+
}, options);
|
|
463
|
+
view.addEventListener("resize", () => this.closeCopyMenu(), options);
|
|
464
|
+
this.copyMenu.firstElementChild.focus();
|
|
465
|
+
});
|
|
466
|
+
this.copyMenu.addEventListener("toggle", (event) => {
|
|
467
|
+
this.menuButton.setAttribute("aria-expanded", String(event.newState === "open"));
|
|
468
|
+
if (event.newState === "closed") {
|
|
469
|
+
this.#menuOpen = false;
|
|
470
|
+
this.#menuEvents?.abort();
|
|
471
|
+
this.#menuEvents = null;
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
this.actions.append(this.copyButton, this.menuButton, this.status);
|
|
475
|
+
this.labelBox.append(this.actions);
|
|
476
|
+
this.shadowRoot.append(style, this.copyMenu);
|
|
477
|
+
}
|
|
478
|
+
closeCopyMenu() {
|
|
479
|
+
if (this.#menuOpen) this.copyMenu.hidePopover();
|
|
480
|
+
this.#menuOpen = false;
|
|
481
|
+
this.#menuEvents?.abort();
|
|
482
|
+
this.#menuEvents = null;
|
|
483
|
+
this.menuButton.setAttribute("aria-expanded", "false");
|
|
484
|
+
}
|
|
485
|
+
projectAttributes(attrs, adapt) {
|
|
486
|
+
const rest = super.projectAttributes(attrs, adapt);
|
|
487
|
+
if (!this.constructor.gramlotComponent.parameters.lbl_variant.enum.includes(this.parameters.lbl_variant)) throw new TypeError("groupBox lbl_variant must be bar or underline");
|
|
488
|
+
this.box.dataset.variant = this.parameters.lbl_variant;
|
|
489
|
+
this.actions.hidden = !this.parameters.copy;
|
|
490
|
+
if (!this.parameters.copy) this.closeCopyMenu();
|
|
491
|
+
this.labelBox.hidden = this.labelBox.hidden && !this.parameters.copy;
|
|
492
|
+
this.copyButton.disabled = this.#busy;
|
|
493
|
+
this.menuButton.disabled = this.#busy;
|
|
494
|
+
return rest;
|
|
495
|
+
}
|
|
496
|
+
async copyData(format = "TYTX") {
|
|
497
|
+
if (!["TYTX", "JSON"].includes(format)) throw new TypeError("Copy format must be TYTX or JSON");
|
|
498
|
+
if (!this.parameters.copy || this.#busy || !this.isConnected) return;
|
|
499
|
+
const generation = ++this.#generation;
|
|
500
|
+
this.copyButton.title = "Copy TYTX";
|
|
501
|
+
this.copyButton.textContent = "\u29C9";
|
|
502
|
+
this.status.textContent = "";
|
|
503
|
+
this.#busy = true;
|
|
504
|
+
this.copyButton.disabled = this.menuButton.disabled = true;
|
|
505
|
+
this.ownerDocument.defaultView.clearTimeout(this.#timer);
|
|
506
|
+
try {
|
|
507
|
+
const bag = this.readDataScope();
|
|
508
|
+
const text = format === "TYTX" ? bag.toTytx() : JSON.stringify(bag.asDict(false, false, true), null, 2);
|
|
509
|
+
await this.ownerDocument.defaultView.navigator.clipboard.writeText(text);
|
|
510
|
+
if (generation !== this.#generation || !this.isConnected) return;
|
|
511
|
+
this.copyButton.textContent = "\u2713";
|
|
512
|
+
this.status.textContent = `Copied ${format}`;
|
|
513
|
+
this.#timer = this.ownerDocument.defaultView.setTimeout(() => {
|
|
514
|
+
this.copyButton.textContent = "\u29C9";
|
|
515
|
+
this.status.textContent = "";
|
|
516
|
+
}, COPY_FEEDBACK_DURATION);
|
|
517
|
+
} catch (error) {
|
|
518
|
+
if (generation !== this.#generation || !this.isConnected) return;
|
|
519
|
+
this.copyButton.textContent = "!";
|
|
520
|
+
this.copyButton.title = this.status.textContent = `Copy failed: ${error.message}`;
|
|
521
|
+
} finally {
|
|
522
|
+
if (generation === this.#generation) {
|
|
523
|
+
this.#busy = false;
|
|
524
|
+
this.copyButton.disabled = this.menuButton.disabled = false;
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
disconnectedCallback() {
|
|
529
|
+
this.closeCopyMenu();
|
|
530
|
+
this.#generation++;
|
|
531
|
+
this.#busy = false;
|
|
532
|
+
this.ownerDocument.defaultView.clearTimeout(this.#timer);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
var layout_default = defineCollection({ name: "layout", components: [Formlet, GroupBox] });
|
|
537
|
+
export {
|
|
538
|
+
Formlet,
|
|
539
|
+
GroupBox,
|
|
540
|
+
layout_default as default
|
|
541
|
+
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// Generated from Gramlot
|
|
2
|
-
export const compactStyle = "/* Gramlot Compact: load after gramlot-base/theme.css. Colors and behavior stay in Base. */\n@layer gramlot.tokens {\n :root {\n --gramlot-space-1: .2rem;\n --gramlot-space-2: .4rem;\n --gramlot-space-3: .5rem;\n --gramlot-space-4: .6rem;\n --gramlot-space-6: .9rem;\n --gramlot-space-8: 1.2rem;\n --gramlot-space-12: 1.8rem;\n --gramlot-radius-sm: .25rem;\n --gramlot-radius: .35rem;\n --gramlot-radius-lg: .4rem;\n }\n}\n\n@layer gramlot.base {\n html { font-size: 13px; }\n body { line-height: 1.35; }\n :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, figure) { margin-block: 0 .5rem; }\n h1 { font-size: 1.35rem; }\n h2 { font-size: 1.15rem; }\n h3 { font-size: 1.05rem; }\n pre { padding: .5rem; }\n blockquote { padding-inline-start: .6rem; }\n hr { margin-block: 1rem; }\n}\n\n@layer gramlot.components {\n :where(button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], .button) {\n min-block-size: 28px;\n padding: .25rem .5rem;\n font-size: 1rem;\n line-height: 1.35;\n }\n :where(label) { font-size: .95rem; }\n :where(input:not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]):not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"range\"]):not([type=\"color\"]):not([type=\"file\"]):not([type=\"hidden\"]), select, textarea) {\n min-block-size: 28px;\n padding: .25rem .5rem;\n font-size: 1rem;\n line-height: 1.35;\n }\n :where(textarea) { min-block-size: 5rem; }\n :where(input[type=\"color\"]) { min-block-size: 28px; }\n :where(input[type=\"file\"])::file-selector-button { margin-inline-end: .5rem; padding: .25rem .5rem; }\n :where(fieldset) { margin-block-end: .6rem; padding: .5rem; }\n :where(table) { font-size: 1rem; }\n :where(th, td) { padding: .4rem .5rem; }\n :where(details) { padding: .4rem .5rem; }\n :where(details[open] > summary) { margin-block-end: .5rem; }\n :where(dialog) { padding: .75rem; }\n}\n\n@layer gramlot.utilities {\n .example-page { inline-size: min(100% - 1.2rem, var(--gramlot-page-width)); padding-block: .6rem; }\n .card { padding: .6rem; box-shadow: none; }\n}\n";
|
|
1
|
+
// Generated from canonical Gramlot themes.
|
|
2
|
+
export const appearanceStyles = {"base":"","compact":"/* Gramlot Compact: load after gramlot-base/theme.css. Colors and behavior stay in Base. */\n@layer gramlot.tokens {\n :root {\n --gramlot-field-height: 28px;\n --gramlot-field-line-height: 1.35;\n --gramlot-field-padding: .25rem .5rem;\n --gramlot-field-font-size: 1rem;\n --gramlot-space-1: .2rem;\n --gramlot-space-2: .4rem;\n --gramlot-space-3: .5rem;\n --gramlot-space-4: .6rem;\n --gramlot-space-6: .9rem;\n --gramlot-space-8: 1.2rem;\n --gramlot-space-12: 1.8rem;\n --gramlot-radius-sm: .25rem;\n --gramlot-radius: .35rem;\n --gramlot-radius-lg: .4rem;\n }\n}\n\n@layer gramlot.base {\n html { font-size: 13px; }\n body { line-height: 1.35; }\n :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, figure) { margin-block: 0 .5rem; }\n h1 { font-size: 1.35rem; }\n h2 { font-size: 1.15rem; }\n h3 { font-size: 1.05rem; }\n pre { padding: .5rem; }\n blockquote { padding-inline-start: .6rem; }\n hr { margin-block: 1rem; }\n}\n\n@layer gramlot.components {\n :where(button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], .button) {\n min-block-size: 28px;\n padding: .25rem .5rem;\n font-size: 1rem;\n line-height: 1.35;\n }\n :where(label) { font-size: .95rem; }\n :where(input:not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]):not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"range\"]):not([type=\"color\"]):not([type=\"file\"]):not([type=\"hidden\"]), select, textarea) {\n min-block-size: var(--gramlot-field-height);\n padding: var(--gramlot-field-padding);\n font-size: var(--gramlot-field-font-size);\n line-height: var(--gramlot-field-line-height);\n }\n :where(textarea) { min-block-size: 5rem; }\n :where(input[type=\"color\"]) { min-block-size: 28px; }\n :where(input[type=\"file\"])::file-selector-button { margin-inline-end: .5rem; padding: .25rem .5rem; }\n :where(fieldset) { margin-block-end: .6rem; padding: .5rem; }\n :where(table) { font-size: 1rem; }\n :where(th, td) { padding: .4rem .5rem; }\n :where(details) { padding: .4rem .5rem; }\n :where(details[open] > summary) { margin-block-end: .5rem; }\n :where(dialog) { padding: .75rem; }\n}\n\n@layer gramlot.utilities {\n .example-page { inline-size: min(100% - 1.2rem, var(--gramlot-page-width)); padding-block: .6rem; }\n .card { padding: .6rem; box-shadow: none; }\n}\n","mimi":"/* Gramlot Compact: load after gramlot-base/theme.css. Colors and behavior stay in Base. */\n@layer gramlot.tokens {\n :root {\n --gramlot-field-height: 28px;\n --gramlot-field-line-height: 1.35;\n --gramlot-field-padding: .25rem .5rem;\n --gramlot-field-font-size: 1rem;\n --gramlot-space-1: .2rem;\n --gramlot-space-2: .4rem;\n --gramlot-space-3: .5rem;\n --gramlot-space-4: .6rem;\n --gramlot-space-6: .9rem;\n --gramlot-space-8: 1.2rem;\n --gramlot-space-12: 1.8rem;\n --gramlot-radius-sm: .25rem;\n --gramlot-radius: .35rem;\n --gramlot-radius-lg: .4rem;\n }\n}\n\n@layer gramlot.base {\n html { font-size: 13px; }\n body { line-height: 1.35; }\n :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, figure) { margin-block: 0 .5rem; }\n h1 { font-size: 1.35rem; }\n h2 { font-size: 1.15rem; }\n h3 { font-size: 1.05rem; }\n pre { padding: .5rem; }\n blockquote { padding-inline-start: .6rem; }\n hr { margin-block: 1rem; }\n}\n\n@layer gramlot.components {\n :where(button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], .button) {\n min-block-size: 28px;\n padding: .25rem .5rem;\n font-size: 1rem;\n line-height: 1.35;\n }\n :where(label) { font-size: .95rem; }\n :where(input:not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]):not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"range\"]):not([type=\"color\"]):not([type=\"file\"]):not([type=\"hidden\"]), select, textarea) {\n min-block-size: var(--gramlot-field-height);\n padding: var(--gramlot-field-padding);\n font-size: var(--gramlot-field-font-size);\n line-height: var(--gramlot-field-line-height);\n }\n :where(textarea) { min-block-size: 5rem; }\n :where(input[type=\"color\"]) { min-block-size: 28px; }\n :where(input[type=\"file\"])::file-selector-button { margin-inline-end: .5rem; padding: .25rem .5rem; }\n :where(fieldset) { margin-block-end: .6rem; padding: .5rem; }\n :where(table) { font-size: 1rem; }\n :where(th, td) { padding: .4rem .5rem; }\n :where(details) { padding: .4rem .5rem; }\n :where(details[open] > summary) { margin-block-end: .5rem; }\n :where(dialog) { padding: .75rem; }\n}\n\n@layer gramlot.utilities {\n .example-page { inline-size: min(100% - 1.2rem, var(--gramlot-page-width)); padding-block: .6rem; }\n .card { padding: .6rem; box-shadow: none; }\n}\n\n/* Gramlot adaptation of legacy mimi; load after Base and Compact. */\n@layer gramlot.tokens {\n :root {\n --gramlot-background: #EDEEF0;\n --gramlot-surface: #F5F5F6;\n --gramlot-surface-subtle: #E0E1E3;\n --gramlot-ink: #3C3D40;\n --gramlot-text-secondary: #6B6E72;\n --gramlot-border: #BDBFC2;\n --gramlot-border-control: #9EA0A3;\n --gramlot-navigation: #48494C;\n --gramlot-navigation-text: #FFFFFF;\n --gramlot-action: #0071E3;\n --gramlot-action-hover: #005BBB;\n --gramlot-brand-blue: #0071E3;\n --gramlot-brand-yellow: #9EA0A3;\n --gramlot-selected: #E8F1FC;\n --gramlot-focus-ring: #0071E3;\n --gramlot-field-background: #FFFFFF;\n --gramlot-field-readonly-background: #E0E1E3;\n --gramlot-error: #B3261E;\n --gramlot-error-background: #FFF1F0;\n --gramlot-shadow: 0 1px 4px rgb(0 0 0 / 6%);\n --gramlot-radius-sm: 4px;\n --gramlot-radius: 3px;\n --gramlot-radius-lg: 3px;\n --gramlot-field-height: 18px;\n --gramlot-field-padding: 1px 3px;\n --gramlot-field-line-height: 1.15;\n --gramlot-field-font-size: 1rem;\n --gramlot-label-size: .9em;\n --gramlot-label-gap: 2px;\n --gramlot-label-color: var(--gramlot-text-secondary);\n --gramlot-space-4: 6px;\n --gramlot-font-ui: -apple-system, BlinkMacSystemFont, \"Helvetica Neue\", Arial, sans-serif;\n }\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --gramlot-background: #242527;\n --gramlot-surface: #303134;\n --gramlot-surface-subtle: #3C3D40;\n --gramlot-ink: #F2F2F7;\n --gramlot-text-secondary: #C7C7CC;\n --gramlot-border: #6B6E72;\n --gramlot-border-control: #8E8E93;\n --gramlot-navigation: #1C1C1E;\n --gramlot-action: #78B7FF;\n --gramlot-action-hover: #A6CFFF;\n --gramlot-brand-blue: #3478CA;\n --gramlot-selected: #254163;\n --gramlot-focus-ring: #78B7FF;\n --gramlot-field-background: #202124;\n --gramlot-field-readonly-background: #3C3D40;\n --gramlot-error: #FFB4AB;\n --gramlot-error-background: #482524;\n --gramlot-shadow: 0 1px 4px rgb(0 0 0 / 20%);\n }\n }\n :root[data-theme=\"dark\"] {\n --gramlot-background: #242527;\n --gramlot-surface: #303134;\n --gramlot-surface-subtle: #3C3D40;\n --gramlot-ink: #F2F2F7;\n --gramlot-text-secondary: #C7C7CC;\n --gramlot-border: #6B6E72;\n --gramlot-border-control: #8E8E93;\n --gramlot-navigation: #1C1C1E;\n --gramlot-action: #78B7FF;\n --gramlot-action-hover: #A6CFFF;\n --gramlot-brand-blue: #3478CA;\n --gramlot-selected: #254163;\n --gramlot-focus-ring: #78B7FF;\n --gramlot-field-background: #202124;\n --gramlot-field-readonly-background: #3C3D40;\n --gramlot-error: #FFB4AB;\n --gramlot-error-background: #482524;\n --gramlot-shadow: 0 1px 4px rgb(0 0 0 / 20%);\n }\n}\n@layer gramlot.base {\n html { font-size: 12px; }\n}\n@layer gramlot.components {\n :where(label) { font-size: var(--gramlot-label-size); font-weight: normal; color: var(--gramlot-label-color); margin-block-end: var(--gramlot-label-gap); }\n :where(button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], .button) { min-block-size: 24px; padding: 2px 8px; font-weight: 500; }\n}\n@layer gramlot.utilities {\n .card { box-shadow: var(--gramlot-shadow); }\n}\n","joanna":"/* Gramlot Compact: load after gramlot-base/theme.css. Colors and behavior stay in Base. */\n@layer gramlot.tokens {\n :root {\n --gramlot-field-height: 28px;\n --gramlot-field-line-height: 1.35;\n --gramlot-field-padding: .25rem .5rem;\n --gramlot-field-font-size: 1rem;\n --gramlot-space-1: .2rem;\n --gramlot-space-2: .4rem;\n --gramlot-space-3: .5rem;\n --gramlot-space-4: .6rem;\n --gramlot-space-6: .9rem;\n --gramlot-space-8: 1.2rem;\n --gramlot-space-12: 1.8rem;\n --gramlot-radius-sm: .25rem;\n --gramlot-radius: .35rem;\n --gramlot-radius-lg: .4rem;\n }\n}\n\n@layer gramlot.base {\n html { font-size: 13px; }\n body { line-height: 1.35; }\n :where(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, blockquote, pre, figure) { margin-block: 0 .5rem; }\n h1 { font-size: 1.35rem; }\n h2 { font-size: 1.15rem; }\n h3 { font-size: 1.05rem; }\n pre { padding: .5rem; }\n blockquote { padding-inline-start: .6rem; }\n hr { margin-block: 1rem; }\n}\n\n@layer gramlot.components {\n :where(button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], .button) {\n min-block-size: 28px;\n padding: .25rem .5rem;\n font-size: 1rem;\n line-height: 1.35;\n }\n :where(label) { font-size: .95rem; }\n :where(input:not([type=\"button\"]):not([type=\"submit\"]):not([type=\"reset\"]):not([type=\"checkbox\"]):not([type=\"radio\"]):not([type=\"range\"]):not([type=\"color\"]):not([type=\"file\"]):not([type=\"hidden\"]), select, textarea) {\n min-block-size: var(--gramlot-field-height);\n padding: var(--gramlot-field-padding);\n font-size: var(--gramlot-field-font-size);\n line-height: var(--gramlot-field-line-height);\n }\n :where(textarea) { min-block-size: 5rem; }\n :where(input[type=\"color\"]) { min-block-size: 28px; }\n :where(input[type=\"file\"])::file-selector-button { margin-inline-end: .5rem; padding: .25rem .5rem; }\n :where(fieldset) { margin-block-end: .6rem; padding: .5rem; }\n :where(table) { font-size: 1rem; }\n :where(th, td) { padding: .4rem .5rem; }\n :where(details) { padding: .4rem .5rem; }\n :where(details[open] > summary) { margin-block-end: .5rem; }\n :where(dialog) { padding: .75rem; }\n}\n\n@layer gramlot.utilities {\n .example-page { inline-size: min(100% - 1.2rem, var(--gramlot-page-width)); padding-block: .6rem; }\n .card { padding: .6rem; box-shadow: none; }\n}\n\n/* Gramlot adaptation of legacy joanna; load after Base and Compact. */\n@layer gramlot.tokens {\n :root {\n --gramlot-background: #F2F2F7;\n --gramlot-surface: #FFFFFF;\n --gramlot-surface-subtle: #F2F2F7;\n --gramlot-ink: #3A3A3C;\n --gramlot-text-secondary: #636366;\n --gramlot-border: #D1D1D6;\n --gramlot-border-control: #AEAEB2;\n --gramlot-navigation: #3A3A3C;\n --gramlot-navigation-text: #FFFFFF;\n --gramlot-action: #007AFF;\n --gramlot-action-hover: #0061CC;\n --gramlot-brand-blue: #007AFF;\n --gramlot-brand-yellow: #AEAEB2;\n --gramlot-selected: #E0EFFF;\n --gramlot-focus-ring: #007AFF;\n --gramlot-field-background: #FFFFFF;\n --gramlot-field-readonly-background: #F2F2F7;\n --gramlot-error: #B3261E;\n --gramlot-error-background: #FFF1F0;\n --gramlot-shadow: 0 1px 4px rgb(0 0 0 / 6%);\n --gramlot-radius-sm: 6px;\n --gramlot-radius: 6px;\n --gramlot-radius-lg: 6px;\n --gramlot-field-height: 20px;\n --gramlot-field-padding: 1px 3px;\n --gramlot-field-line-height: 1.15;\n --gramlot-field-font-size: 1rem;\n --gramlot-label-size: .8em;\n --gramlot-label-gap: 2px;\n --gramlot-label-color: var(--gramlot-text-secondary);\n --gramlot-space-4: 6px;\n --gramlot-font-ui: -apple-system, BlinkMacSystemFont, \"Helvetica Neue\", Arial, sans-serif;\n }\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --gramlot-background: #1C1C1E;\n --gramlot-surface: #2C2C2E;\n --gramlot-surface-subtle: #3A3A3C;\n --gramlot-ink: #F2F2F7;\n --gramlot-text-secondary: #C7C7CC;\n --gramlot-border: #636366;\n --gramlot-border-control: #8E8E93;\n --gramlot-navigation: #1C1C1E;\n --gramlot-action: #78B7FF;\n --gramlot-action-hover: #A6CFFF;\n --gramlot-brand-blue: #3478CA;\n --gramlot-selected: #254163;\n --gramlot-focus-ring: #78B7FF;\n --gramlot-field-background: #202124;\n --gramlot-field-readonly-background: #3A3A3C;\n --gramlot-error: #FFB4AB;\n --gramlot-error-background: #482524;\n --gramlot-shadow: 0 1px 4px rgb(0 0 0 / 20%);\n }\n }\n :root[data-theme=\"dark\"] {\n --gramlot-background: #1C1C1E;\n --gramlot-surface: #2C2C2E;\n --gramlot-surface-subtle: #3A3A3C;\n --gramlot-ink: #F2F2F7;\n --gramlot-text-secondary: #C7C7CC;\n --gramlot-border: #636366;\n --gramlot-border-control: #8E8E93;\n --gramlot-navigation: #1C1C1E;\n --gramlot-action: #78B7FF;\n --gramlot-action-hover: #A6CFFF;\n --gramlot-brand-blue: #3478CA;\n --gramlot-selected: #254163;\n --gramlot-focus-ring: #78B7FF;\n --gramlot-field-background: #202124;\n --gramlot-field-readonly-background: #3A3A3C;\n --gramlot-error: #FFB4AB;\n --gramlot-error-background: #482524;\n --gramlot-shadow: 0 1px 4px rgb(0 0 0 / 20%);\n }\n}\n@layer gramlot.base {\n html { font-size: 13px; }\n}\n@layer gramlot.components {\n :where(label) { font-size: var(--gramlot-label-size); font-weight: normal; color: var(--gramlot-label-color); margin-block-end: var(--gramlot-label-gap); }\n :where(button, input[type=\"button\"], input[type=\"submit\"], input[type=\"reset\"], .button) { min-block-size: 26px; padding: 2px 8px; font-weight: 500; }\n}\n@layer gramlot.utilities {\n .card { box-shadow: var(--gramlot-shadow); }\n}\n"};
|