elvish-css 1.0.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/LICENSE +21 -0
- package/README.md +518 -0
- package/dist/elvish.css +2194 -0
- package/dist/elvish.d.ts +78 -0
- package/dist/elvish.esm.js +2185 -0
- package/dist/elvish.iife.js +2169 -0
- package/dist/elvish.min.css +9 -0
- package/dist/elvish.umd.js +2173 -0
- package/elvish.css +28 -0
- package/elvish.js +81 -0
- package/global/global.css +16 -0
- package/global/modern.css +305 -0
- package/global/reset.css +507 -0
- package/global/tokens.css +154 -0
- package/global/transitions.css +288 -0
- package/global/transitions.js +289 -0
- package/global/utilities.css +151 -0
- package/package.json +61 -0
- package/primitives/adleithian/adleithian.css +16 -0
- package/primitives/adleithian/adleithian.js +63 -0
- package/primitives/bau/bau.css +86 -0
- package/primitives/bau/bau.js +127 -0
- package/primitives/enedh/enedh.css +38 -0
- package/primitives/enedh/enedh.js +110 -0
- package/primitives/esgal/esgal.css +39 -0
- package/primitives/esgal/esgal.js +115 -0
- package/primitives/fano/fano.css +28 -0
- package/primitives/fano/fano.js +108 -0
- package/primitives/gant-thala/gant-thala.css +32 -0
- package/primitives/gant-thala/gant-thala.js +69 -0
- package/primitives/glan-tholl/glan-tholl.css +71 -0
- package/primitives/glan-tholl/glan-tholl.js +147 -0
- package/primitives/glan-veleg/glan-veleg.css +45 -0
- package/primitives/glan-veleg/glan-veleg.js +138 -0
- package/primitives/gonath/gonath.css +57 -0
- package/primitives/gonath/gonath.js +113 -0
- package/primitives/gwistindor/gwistindor.css +52 -0
- package/primitives/gwistindor/gwistindor.js +96 -0
- package/primitives/hath/hath.css +39 -0
- package/primitives/hath/hath.js +107 -0
- package/primitives/him/him.css +43 -0
- package/primitives/him/him.js +169 -0
- package/primitives/miriant/miriant.css +75 -0
- package/primitives/miriant/miriant.js +158 -0
- package/primitives/thann/thann.css +57 -0
- package/primitives/thann/thann.js +96 -0
- package/primitives/tiniath/tiniath.css +16 -0
- package/primitives/tiniath/tiniath.js +88 -0
- package/primitives/vircantie/vircantie.css +24 -0
- package/primitives/vircantie/vircantie.js +83 -0
|
@@ -0,0 +1,2169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elvish CSS Layout System v1.0.0
|
|
3
|
+
* Custom Elements for intrinsic CSS layouts
|
|
4
|
+
*
|
|
5
|
+
* https://github.com/star-this/elvish-css
|
|
6
|
+
* License: MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
(function() {
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
// Transitions
|
|
14
|
+
/**
|
|
15
|
+
* Elvish - View Transitions Helper
|
|
16
|
+
*
|
|
17
|
+
* Utilities for smooth view transitions between layout states.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
*
|
|
21
|
+
* import { transition, transitionTo } from './transitions.js';
|
|
22
|
+
*
|
|
23
|
+
* // Wrap any DOM change in a transition
|
|
24
|
+
* transition(() => {
|
|
25
|
+
* element.classList.toggle('collapsed');
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // With custom options
|
|
29
|
+
* transition(() => {
|
|
30
|
+
* sidebar.hidden = true;
|
|
31
|
+
* }, { duration: 500 });
|
|
32
|
+
*
|
|
33
|
+
* // Transition with callback after completion
|
|
34
|
+
* transition(() => {
|
|
35
|
+
* grid.setAttribute('columns', '4');
|
|
36
|
+
* }).then(() => {
|
|
37
|
+
* console.log('Transition complete!');
|
|
38
|
+
* });
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if View Transitions API is supported
|
|
43
|
+
*/
|
|
44
|
+
var supportsViewTransitions = () =>
|
|
45
|
+
typeof document !== 'undefined' &&
|
|
46
|
+
'startViewTransition' in document;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get the currently active view transition (if any)
|
|
50
|
+
* @returns {ViewTransition|null}
|
|
51
|
+
*/
|
|
52
|
+
var getActiveTransition = () =>
|
|
53
|
+
document.activeViewTransition ?? null;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Wrap a DOM mutation in a view transition
|
|
57
|
+
*
|
|
58
|
+
* @param {Function} updateCallback - Function that performs DOM changes
|
|
59
|
+
* @param {Object} options - Configuration options
|
|
60
|
+
* @param {number} options.duration - Override transition duration (ms)
|
|
61
|
+
* @param {string} options.easing - Override easing function
|
|
62
|
+
* @param {string[]} options.types - Transition types for :active-view-transition-type()
|
|
63
|
+
* @returns {Promise} Resolves when transition completes
|
|
64
|
+
*/
|
|
65
|
+
function transition(updateCallback, options = {}) {
|
|
66
|
+
// If View Transitions not supported, just run the callback
|
|
67
|
+
if (!supportsViewTransitions()) {
|
|
68
|
+
updateCallback();
|
|
69
|
+
return Promise.resolve();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Apply custom duration/easing if provided
|
|
73
|
+
var root = document.documentElement;
|
|
74
|
+
var originalDuration = root.style.getPropertyValue('--transition-duration');
|
|
75
|
+
var originalEasing = root.style.getPropertyValue('--transition-ease');
|
|
76
|
+
|
|
77
|
+
if (options.duration) {
|
|
78
|
+
root.style.setProperty('--transition-duration', `${options.duration}ms`);
|
|
79
|
+
}
|
|
80
|
+
if (options.easing) {
|
|
81
|
+
root.style.setProperty('--transition-ease', options.easing);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Start the view transition with optional types
|
|
85
|
+
var transitionOptions = options.types
|
|
86
|
+
? { update: updateCallback, types: options.types }
|
|
87
|
+
: updateCallback;
|
|
88
|
+
|
|
89
|
+
var viewTransition = document.startViewTransition(transitionOptions);
|
|
90
|
+
|
|
91
|
+
// Restore original values after transition
|
|
92
|
+
return viewTransition.finished.then(() => {
|
|
93
|
+
if (options.duration) {
|
|
94
|
+
root.style.setProperty('--transition-duration', originalDuration || '');
|
|
95
|
+
}
|
|
96
|
+
if (options.easing) {
|
|
97
|
+
root.style.setProperty('--transition-ease', originalEasing || '');
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Transition an element to a new state by changing attributes/classes
|
|
104
|
+
*
|
|
105
|
+
* @param {HTMLElement} element - Element to transition
|
|
106
|
+
* @param {Object} changes - Attribute/class changes to apply
|
|
107
|
+
* @param {Object} changes.attrs - Attributes to set (use null to remove)
|
|
108
|
+
* @param {string[]} changes.addClass - Classes to add
|
|
109
|
+
* @param {string[]} changes.removeClass - Classes to remove
|
|
110
|
+
* @param {string[]} changes.toggleClass - Classes to toggle
|
|
111
|
+
* @param {Object} changes.style - Inline styles to set
|
|
112
|
+
* @param {Object} options - Transition options
|
|
113
|
+
* @returns {Promise}
|
|
114
|
+
*/
|
|
115
|
+
function transitionTo(element, changes = {}, options = {}) {
|
|
116
|
+
return transition(() => {
|
|
117
|
+
// Apply attribute changes
|
|
118
|
+
if (changes.attrs) {
|
|
119
|
+
Object.entries(changes.attrs).forEach(([key, value]) => {
|
|
120
|
+
if (value === null) {
|
|
121
|
+
element.removeAttribute(key);
|
|
122
|
+
} else {
|
|
123
|
+
element.setAttribute(key, value);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Apply class changes
|
|
129
|
+
if (changes.addClass) {
|
|
130
|
+
element.classList.add(...changes.addClass);
|
|
131
|
+
}
|
|
132
|
+
if (changes.removeClass) {
|
|
133
|
+
element.classList.remove(...changes.removeClass);
|
|
134
|
+
}
|
|
135
|
+
if (changes.toggleClass) {
|
|
136
|
+
changes.toggleClass.forEach(cls => element.classList.toggle(cls));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Apply style changes
|
|
140
|
+
if (changes.style) {
|
|
141
|
+
Object.entries(changes.style).forEach(([prop, value]) => {
|
|
142
|
+
element.style.setProperty(prop, value);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}, options);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Transition between two elements (e.g., swap content)
|
|
150
|
+
*
|
|
151
|
+
* @param {HTMLElement} outElement - Element leaving
|
|
152
|
+
* @param {HTMLElement} inElement - Element entering
|
|
153
|
+
* @param {Object} options - Transition options
|
|
154
|
+
* @returns {Promise}
|
|
155
|
+
*/
|
|
156
|
+
function crossfade(outElement, inElement, options = {}) {
|
|
157
|
+
return transition(() => {
|
|
158
|
+
outElement.hidden = true;
|
|
159
|
+
inElement.hidden = false;
|
|
160
|
+
}, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Create a named transition group for an element
|
|
165
|
+
* Elements with the same view-transition-name animate together
|
|
166
|
+
*
|
|
167
|
+
* @param {HTMLElement} element - Element to name
|
|
168
|
+
* @param {string} name - Transition name
|
|
169
|
+
*/
|
|
170
|
+
function setTransitionName(element, name) {
|
|
171
|
+
element.style.viewTransitionName = name;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Remove transition name from element
|
|
176
|
+
*
|
|
177
|
+
* @param {HTMLElement} element
|
|
178
|
+
*/
|
|
179
|
+
function clearTransitionName(element) {
|
|
180
|
+
element.style.viewTransitionName = '';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Batch multiple elements with unique transition names
|
|
185
|
+
* Useful for list/grid items that should animate independently
|
|
186
|
+
*
|
|
187
|
+
* @param {NodeList|HTMLElement[]} elements - Elements to name
|
|
188
|
+
* @param {string} prefix - Name prefix (will add index)
|
|
189
|
+
*/
|
|
190
|
+
function nameTransitionGroup(elements, prefix = 'item') {
|
|
191
|
+
elements.forEach((el, i) => {
|
|
192
|
+
el.style.viewTransitionName = `${prefix}-${i}`;
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Enable auto-naming with match-element for a container's children
|
|
198
|
+
* Each child gets a unique browser-generated name automatically
|
|
199
|
+
*
|
|
200
|
+
* @param {HTMLElement} container - Parent element
|
|
201
|
+
* @param {string} [transitionClass] - Optional view-transition-class for group styling
|
|
202
|
+
*/
|
|
203
|
+
function enableAutoNaming(container, transitionClass) {
|
|
204
|
+
Array.from(container.children).forEach(child => {
|
|
205
|
+
child.style.viewTransitionName = 'match-element';
|
|
206
|
+
if (transitionClass) {
|
|
207
|
+
child.style.viewTransitionClass = transitionClass;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Disable auto-naming for a container's children
|
|
214
|
+
*
|
|
215
|
+
* @param {HTMLElement} container - Parent element
|
|
216
|
+
*/
|
|
217
|
+
function disableAutoNaming(container) {
|
|
218
|
+
Array.from(container.children).forEach(child => {
|
|
219
|
+
child.style.viewTransitionName = '';
|
|
220
|
+
child.style.viewTransitionClass = '';
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Elvish-specific: Transition ratio change
|
|
226
|
+
*
|
|
227
|
+
* @param {'golden'|'silver'|'fifth'} ratio - New ratio
|
|
228
|
+
* @param {Object} options - Transition options
|
|
229
|
+
*/
|
|
230
|
+
function transitionRatio(ratio, options = {}) {
|
|
231
|
+
var ratioVar = `var(--ratio-${ratio})`;
|
|
232
|
+
var measures = { golden: '60ch', fifth: '70ch', silver: '80ch' };
|
|
233
|
+
|
|
234
|
+
return transition(() => {
|
|
235
|
+
var root = document.documentElement;
|
|
236
|
+
root.style.setProperty('--ratio', ratioVar);
|
|
237
|
+
root.style.setProperty('--measure', measures[ratio]);
|
|
238
|
+
root.dataset.ratio = ratio;
|
|
239
|
+
}, { duration: 400, types: ['layout', 'ratio'], ...options });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Elvish-specific: Transition theme change
|
|
244
|
+
*
|
|
245
|
+
* @param {'light'|'dark'|'auto'} theme - New theme
|
|
246
|
+
* @param {Object} options - Transition options
|
|
247
|
+
*/
|
|
248
|
+
function transitionTheme(theme, options = {}) {
|
|
249
|
+
return transition(() => {
|
|
250
|
+
var root = document.documentElement;
|
|
251
|
+
if (theme === 'auto') {
|
|
252
|
+
root.style.colorScheme = 'light dark';
|
|
253
|
+
} else {
|
|
254
|
+
root.style.colorScheme = theme;
|
|
255
|
+
}
|
|
256
|
+
localStorage.setItem('elvish-theme', theme);
|
|
257
|
+
}, { duration: 300, types: ['theme'], ...options });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Elvish-specific: Transition layout primitive state
|
|
262
|
+
*
|
|
263
|
+
* @param {HTMLElement} primitive - Elvish layout element
|
|
264
|
+
* @param {Object} attrs - New attribute values
|
|
265
|
+
* @param {Object} options - Transition options
|
|
266
|
+
*/
|
|
267
|
+
function transitionLayout(primitive, attrs, options = {}) {
|
|
268
|
+
// Give the primitive a transition name if it doesn't have one
|
|
269
|
+
if (!primitive.style.viewTransitionName) {
|
|
270
|
+
primitive.style.viewTransitionName = primitive.tagName.toLowerCase();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return transitionTo(primitive, { attrs }, { types: ['layout'], ...options });
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Skip/cancel the currently active view transition
|
|
278
|
+
*/
|
|
279
|
+
function skipTransition() {
|
|
280
|
+
var active = getActiveTransition();
|
|
281
|
+
if (active) {
|
|
282
|
+
active.skipTransition();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Default export for convenient importing
|
|
287
|
+
default {
|
|
288
|
+
transition,
|
|
289
|
+
transitionTo,
|
|
290
|
+
crossfade,
|
|
291
|
+
setTransitionName,
|
|
292
|
+
clearTransitionName,
|
|
293
|
+
nameTransitionGroup,
|
|
294
|
+
enableAutoNaming,
|
|
295
|
+
disableAutoNaming,
|
|
296
|
+
transitionRatio,
|
|
297
|
+
transitionTheme,
|
|
298
|
+
transitionLayout,
|
|
299
|
+
skipTransition,
|
|
300
|
+
supportsViewTransitions,
|
|
301
|
+
getActiveTransition,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
// Primitives (auto-register)
|
|
306
|
+
// hath
|
|
307
|
+
/**
|
|
308
|
+
* Stack Layout Custom Element
|
|
309
|
+
*
|
|
310
|
+
* Injects vertical margin between successive child elements.
|
|
311
|
+
*
|
|
312
|
+
* @property {string} space - A CSS margin value (default: var(--s1))
|
|
313
|
+
* @property {boolean} recursive - Apply to all descendants, not just children
|
|
314
|
+
* @property {number} splitAfter - Element index after which to split with auto margin
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* <i-hath space="var(--s2)">
|
|
318
|
+
* <h2>Heading</h2>
|
|
319
|
+
* <p>Paragraph one</p>
|
|
320
|
+
* <p>Paragraph two</p>
|
|
321
|
+
* </i-hath>
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
class HathLayout extends HTMLElement {
|
|
325
|
+
static get observedAttributes() {
|
|
326
|
+
return ['space', 'recursive', 'split-after'];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
constructor() {
|
|
330
|
+
super();
|
|
331
|
+
this.render = this.render.bind(this);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
connectedCallback() {
|
|
335
|
+
this.render();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
attributeChangedCallback() {
|
|
339
|
+
this.render();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
get space() {
|
|
343
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
set space(val) {
|
|
347
|
+
this.setAttribute('space', val);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
get recursive() {
|
|
351
|
+
return this.hasAttribute('recursive');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
set recursive(val) {
|
|
355
|
+
if (val) {
|
|
356
|
+
this.setAttribute('recursive', '');
|
|
357
|
+
} else {
|
|
358
|
+
this.removeAttribute('recursive');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
get splitAfter() {
|
|
363
|
+
const val = this.getAttribute('split-after');
|
|
364
|
+
return val ? parseInt(val, 10) : null;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
set splitAfter(val) {
|
|
368
|
+
if (val) {
|
|
369
|
+
this.setAttribute('split-after', val);
|
|
370
|
+
} else {
|
|
371
|
+
this.removeAttribute('split-after');
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
render() {
|
|
376
|
+
// Generate unique identifier for this configuration
|
|
377
|
+
const recursive = this.recursive ? '-recursive' : '';
|
|
378
|
+
const split = this.splitAfter ? `-split${this.splitAfter}` : '';
|
|
379
|
+
const id = `Hath-${this.space}${recursive}${split}`.replace(/[^\w-]/g, '');
|
|
380
|
+
|
|
381
|
+
this.dataset.i = id;
|
|
382
|
+
this.style.setProperty('--hath-space', this.space);
|
|
383
|
+
|
|
384
|
+
// Check if styles already exist for this configuration
|
|
385
|
+
if (!document.getElementById(id)) {
|
|
386
|
+
const styleEl = document.createElement('style');
|
|
387
|
+
styleEl.id = id;
|
|
388
|
+
|
|
389
|
+
const selector = `[data-i="${id}"]`;
|
|
390
|
+
let css = '';
|
|
391
|
+
|
|
392
|
+
if (this.recursive) {
|
|
393
|
+
css = `${selector} * + * { margin-block-start: ${this.space}; }`;
|
|
394
|
+
} else {
|
|
395
|
+
css = `${selector} > * + * { margin-block-start: ${this.space}; }`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (this.splitAfter) {
|
|
399
|
+
css += `${selector} > :nth-child(${this.splitAfter}) { margin-block-end: auto; }`;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
styleEl.textContent = css;
|
|
403
|
+
document.head.appendChild(styleEl);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Define the custom element
|
|
409
|
+
if ('customElements' in window) {
|
|
410
|
+
customElements.define('i-hath', HathLayout);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
// bau
|
|
417
|
+
/**
|
|
418
|
+
* Box Layout Custom Element
|
|
419
|
+
*
|
|
420
|
+
* A simple container with padding and optional border/background.
|
|
421
|
+
*
|
|
422
|
+
* @property {string} padding - A CSS padding value (default: var(--s1))
|
|
423
|
+
* @property {string} borderWidth - A CSS border-width value (default: var(--border-thin))
|
|
424
|
+
* @property {boolean} invert - Swap foreground and background colors
|
|
425
|
+
* @property {boolean} borderless - Remove border
|
|
426
|
+
* @property {boolean} compact - Remove padding
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* <i-bau padding="var(--s2)" invert>
|
|
430
|
+
* <p>Content in a box</p>
|
|
431
|
+
* </i-bau>
|
|
432
|
+
*/
|
|
433
|
+
|
|
434
|
+
class BauLayout extends HTMLElement {
|
|
435
|
+
static get observedAttributes() {
|
|
436
|
+
return ['padding', 'border-width', 'invert', 'borderless', 'compact'];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
constructor() {
|
|
440
|
+
super();
|
|
441
|
+
this.render = this.render.bind(this);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
connectedCallback() {
|
|
445
|
+
this.render();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
attributeChangedCallback() {
|
|
449
|
+
this.render();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
get padding() {
|
|
453
|
+
return this.getAttribute('padding') || 'var(--s1)';
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
set padding(val) {
|
|
457
|
+
this.setAttribute('padding', val);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
get borderWidth() {
|
|
461
|
+
return this.getAttribute('border-width') || 'var(--border-thin)';
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
set borderWidth(val) {
|
|
465
|
+
this.setAttribute('border-width', val);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
get invert() {
|
|
469
|
+
return this.hasAttribute('invert');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
set invert(val) {
|
|
473
|
+
if (val) {
|
|
474
|
+
this.setAttribute('invert', '');
|
|
475
|
+
} else {
|
|
476
|
+
this.removeAttribute('invert');
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
get borderless() {
|
|
481
|
+
return this.hasAttribute('borderless');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
set borderless(val) {
|
|
485
|
+
if (val) {
|
|
486
|
+
this.setAttribute('borderless', '');
|
|
487
|
+
} else {
|
|
488
|
+
this.removeAttribute('borderless');
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
get compact() {
|
|
493
|
+
return this.hasAttribute('compact');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
set compact(val) {
|
|
497
|
+
if (val) {
|
|
498
|
+
this.setAttribute('compact', '');
|
|
499
|
+
} else {
|
|
500
|
+
this.removeAttribute('compact');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
render() {
|
|
505
|
+
// Set CSS custom properties for this instance
|
|
506
|
+
this.style.setProperty('--bau-padding', this.padding);
|
|
507
|
+
this.style.setProperty('--bau-border-width', this.borderWidth);
|
|
508
|
+
|
|
509
|
+
// Generate unique identifier
|
|
510
|
+
const invertStr = this.invert ? '-invert' : '';
|
|
511
|
+
const borderlessStr = this.borderless ? '-borderless' : '';
|
|
512
|
+
const compactStr = this.compact ? '-compact' : '';
|
|
513
|
+
const id = `Bau-${this.padding}-${this.borderWidth}${invertStr}${borderlessStr}${compactStr}`.replace(/[^\w-]/g, '');
|
|
514
|
+
|
|
515
|
+
this.dataset.i = id;
|
|
516
|
+
|
|
517
|
+
// Check if styles already exist
|
|
518
|
+
if (!document.getElementById(id)) {
|
|
519
|
+
const styleEl = document.createElement('style');
|
|
520
|
+
styleEl.id = id;
|
|
521
|
+
|
|
522
|
+
const selector = `[data-i="${id}"]`;
|
|
523
|
+
let css = `${selector} { padding: ${this.padding}; border-width: ${this.borderWidth}; }`;
|
|
524
|
+
|
|
525
|
+
if (this.borderless) {
|
|
526
|
+
css += `${selector} { border-width: 0; }`;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (this.compact) {
|
|
530
|
+
css += `${selector} { padding: 0; }`;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
styleEl.textContent = css;
|
|
534
|
+
document.head.appendChild(styleEl);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if ('customElements' in window) {
|
|
540
|
+
customElements.define('i-bau', BauLayout);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
// enedh
|
|
547
|
+
/**
|
|
548
|
+
* Center Layout Custom Element
|
|
549
|
+
*
|
|
550
|
+
* Horizontally centers content with a maximum width.
|
|
551
|
+
*
|
|
552
|
+
* @property {string} max - Maximum width (default: var(--measure))
|
|
553
|
+
* @property {string} gutters - Minimum space on sides (default: 0)
|
|
554
|
+
* @property {boolean} intrinsic - Center children based on their content width
|
|
555
|
+
* @property {boolean} andText - Also center text alignment
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* <i-enedh max="40ch" gutters="var(--s1)">
|
|
559
|
+
* <p>Centered content</p>
|
|
560
|
+
* </i-enedh>
|
|
561
|
+
*/
|
|
562
|
+
|
|
563
|
+
class EnedhLayout extends HTMLElement {
|
|
564
|
+
static get observedAttributes() {
|
|
565
|
+
return ['max', 'gutters', 'intrinsic', 'and-text'];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
constructor() {
|
|
569
|
+
super();
|
|
570
|
+
this.render = this.render.bind(this);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
connectedCallback() {
|
|
574
|
+
this.render();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
attributeChangedCallback() {
|
|
578
|
+
this.render();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
get max() {
|
|
582
|
+
return this.getAttribute('max') || 'var(--measure)';
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
set max(val) {
|
|
586
|
+
this.setAttribute('max', val);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
get gutters() {
|
|
590
|
+
return this.getAttribute('gutters') || '0';
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
set gutters(val) {
|
|
594
|
+
this.setAttribute('gutters', val);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
get intrinsic() {
|
|
598
|
+
return this.hasAttribute('intrinsic');
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
set intrinsic(val) {
|
|
602
|
+
if (val) {
|
|
603
|
+
this.setAttribute('intrinsic', '');
|
|
604
|
+
} else {
|
|
605
|
+
this.removeAttribute('intrinsic');
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
get andText() {
|
|
610
|
+
return this.hasAttribute('and-text');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
set andText(val) {
|
|
614
|
+
if (val) {
|
|
615
|
+
this.setAttribute('and-text', '');
|
|
616
|
+
} else {
|
|
617
|
+
this.removeAttribute('and-text');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
render() {
|
|
622
|
+
this.style.setProperty('--enedh-max', this.max);
|
|
623
|
+
this.style.setProperty('--enedh-gutters', this.gutters);
|
|
624
|
+
|
|
625
|
+
const intrinsicStr = this.intrinsic ? '-intrinsic' : '';
|
|
626
|
+
const textStr = this.andText ? '-andText' : '';
|
|
627
|
+
const id = `Enedh-${this.max}-${this.gutters}${intrinsicStr}${textStr}`.replace(/[^\w-]/g, '');
|
|
628
|
+
|
|
629
|
+
this.dataset.i = id;
|
|
630
|
+
|
|
631
|
+
if (!document.getElementById(id)) {
|
|
632
|
+
const styleEl = document.createElement('style');
|
|
633
|
+
styleEl.id = id;
|
|
634
|
+
|
|
635
|
+
const selector = `[data-i="${id}"]`;
|
|
636
|
+
let css = `${selector} { max-inline-size: ${this.max}; padding-inline: ${this.gutters}; }`;
|
|
637
|
+
|
|
638
|
+
if (this.intrinsic) {
|
|
639
|
+
css += `${selector} { display: flex; flex-direction: column; align-items: center; }`;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
if (this.andText) {
|
|
643
|
+
css += `${selector} { text-align: center; }`;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
styleEl.textContent = css;
|
|
647
|
+
document.head.appendChild(styleEl);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if ('customElements' in window) {
|
|
653
|
+
customElements.define('i-enedh', EnedhLayout);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
// tiniath
|
|
660
|
+
/**
|
|
661
|
+
* Cluster Layout Custom Element
|
|
662
|
+
*
|
|
663
|
+
* Groups elements that differ in length and wrap naturally.
|
|
664
|
+
*
|
|
665
|
+
* @property {string} space - Gap between items (default: var(--s1))
|
|
666
|
+
* @property {string} justify - justify-content value (default: flex-start)
|
|
667
|
+
* @property {string} align - align-items value (default: center)
|
|
668
|
+
*
|
|
669
|
+
* @example
|
|
670
|
+
* <i-tiniath space="var(--s0)" justify="space-between">
|
|
671
|
+
* <button>Save</button>
|
|
672
|
+
* <button>Cancel</button>
|
|
673
|
+
* </i-tiniath>
|
|
674
|
+
*/
|
|
675
|
+
|
|
676
|
+
class TiniathLayout extends HTMLElement {
|
|
677
|
+
static get observedAttributes() {
|
|
678
|
+
return ['space', 'justify', 'align'];
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
constructor() {
|
|
682
|
+
super();
|
|
683
|
+
this.render = this.render.bind(this);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
connectedCallback() {
|
|
687
|
+
this.render();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
attributeChangedCallback() {
|
|
691
|
+
this.render();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
get space() {
|
|
695
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
set space(val) {
|
|
699
|
+
this.setAttribute('space', val);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
get justify() {
|
|
703
|
+
return this.getAttribute('justify') || 'flex-start';
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
set justify(val) {
|
|
707
|
+
this.setAttribute('justify', val);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
get align() {
|
|
711
|
+
return this.getAttribute('align') || 'center';
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
set align(val) {
|
|
715
|
+
this.setAttribute('align', val);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
render() {
|
|
719
|
+
this.style.setProperty('--tiniath-space', this.space);
|
|
720
|
+
this.style.setProperty('--tiniath-justify', this.justify);
|
|
721
|
+
this.style.setProperty('--tiniath-align', this.align);
|
|
722
|
+
|
|
723
|
+
const id = `Tiniath-${this.space}-${this.justify}-${this.align}`.replace(/[^\w-]/g, '');
|
|
724
|
+
this.dataset.i = id;
|
|
725
|
+
|
|
726
|
+
if (!document.getElementById(id)) {
|
|
727
|
+
const styleEl = document.createElement('style');
|
|
728
|
+
styleEl.id = id;
|
|
729
|
+
|
|
730
|
+
const selector = `[data-i="${id}"]`;
|
|
731
|
+
styleEl.textContent = `
|
|
732
|
+
${selector} {
|
|
733
|
+
gap: ${this.space};
|
|
734
|
+
justify-content: ${this.justify};
|
|
735
|
+
align-items: ${this.align};
|
|
736
|
+
}
|
|
737
|
+
`;
|
|
738
|
+
document.head.appendChild(styleEl);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if ('customElements' in window) {
|
|
744
|
+
customElements.define('i-tiniath', TiniathLayout);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
// glan-veleg
|
|
751
|
+
/**
|
|
752
|
+
* GlanVeleg Layout Custom Element
|
|
753
|
+
*
|
|
754
|
+
* A quantum layout with one fixed-width element and one fluid element.
|
|
755
|
+
*
|
|
756
|
+
* @property {string} side - Which side is the sidebar: "left" or "right" (default: left)
|
|
757
|
+
* @property {string} sideWidth - Width of sidebar when horizontal (default: 20rem)
|
|
758
|
+
* @property {string} contentMin - Min width of content before wrapping (default: 50%)
|
|
759
|
+
* @property {string} space - Gap between elements (default: var(--s1))
|
|
760
|
+
* @property {boolean} noStretch - Disable equal height (default: false)
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* <i-glan-veleg side-width="15rem" content-min="60%">
|
|
764
|
+
* <nav>GlanVeleg content</nav>
|
|
765
|
+
* <main>Main content</main>
|
|
766
|
+
* </i-glan-veleg>
|
|
767
|
+
*/
|
|
768
|
+
|
|
769
|
+
class GlanVelegLayout extends HTMLElement {
|
|
770
|
+
static get observedAttributes() {
|
|
771
|
+
return ['side', 'side-width', 'content-min', 'space', 'no-stretch'];
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
constructor() {
|
|
775
|
+
super();
|
|
776
|
+
this.render = this.render.bind(this);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
connectedCallback() {
|
|
780
|
+
this.render();
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
attributeChangedCallback() {
|
|
784
|
+
this.render();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
get side() {
|
|
788
|
+
return this.getAttribute('side') || 'left';
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
set side(val) {
|
|
792
|
+
this.setAttribute('side', val);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
get sideWidth() {
|
|
796
|
+
return this.getAttribute('side-width') || '20rem';
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
set sideWidth(val) {
|
|
800
|
+
this.setAttribute('side-width', val);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
get contentMin() {
|
|
804
|
+
return this.getAttribute('content-min') || '50%';
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
set contentMin(val) {
|
|
808
|
+
this.setAttribute('content-min', val);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
get space() {
|
|
812
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
set space(val) {
|
|
816
|
+
this.setAttribute('space', val);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
get noStretch() {
|
|
820
|
+
return this.hasAttribute('no-stretch');
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
set noStretch(val) {
|
|
824
|
+
if (val) {
|
|
825
|
+
this.setAttribute('no-stretch', '');
|
|
826
|
+
} else {
|
|
827
|
+
this.removeAttribute('no-stretch');
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
render() {
|
|
832
|
+
this.style.setProperty('--glan-veleg-width', this.sideWidth);
|
|
833
|
+
this.style.setProperty('--glan-veleg-content-min', this.contentMin);
|
|
834
|
+
this.style.setProperty('--glan-veleg-space', this.space);
|
|
835
|
+
|
|
836
|
+
const sideStr = this.side === 'right' ? '-right' : '-left';
|
|
837
|
+
const stretchStr = this.noStretch ? '-noStretch' : '';
|
|
838
|
+
const id = `GlanVeleg-${this.sideWidth}-${this.contentMin}-${this.space}${sideStr}${stretchStr}`.replace(/[^\w-]/g, '');
|
|
839
|
+
|
|
840
|
+
this.dataset.i = id;
|
|
841
|
+
|
|
842
|
+
if (!document.getElementById(id)) {
|
|
843
|
+
const styleEl = document.createElement('style');
|
|
844
|
+
styleEl.id = id;
|
|
845
|
+
|
|
846
|
+
const selector = `[data-i="${id}"]`;
|
|
847
|
+
let css = `${selector} { gap: ${this.space}; }`;
|
|
848
|
+
|
|
849
|
+
if (this.side === 'right') {
|
|
850
|
+
css += `
|
|
851
|
+
${selector} > :first-child {
|
|
852
|
+
flex-basis: 0;
|
|
853
|
+
flex-grow: 999;
|
|
854
|
+
min-inline-size: ${this.contentMin};
|
|
855
|
+
}
|
|
856
|
+
${selector} > :last-child {
|
|
857
|
+
flex-basis: ${this.sideWidth};
|
|
858
|
+
flex-grow: 1;
|
|
859
|
+
}
|
|
860
|
+
`;
|
|
861
|
+
} else {
|
|
862
|
+
css += `
|
|
863
|
+
${selector} > :first-child {
|
|
864
|
+
flex-basis: ${this.sideWidth};
|
|
865
|
+
}
|
|
866
|
+
${selector} > :last-child {
|
|
867
|
+
flex-basis: 0;
|
|
868
|
+
flex-grow: 999;
|
|
869
|
+
min-inline-size: ${this.contentMin};
|
|
870
|
+
}
|
|
871
|
+
`;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (this.noStretch) {
|
|
875
|
+
css += `${selector} { align-items: flex-start; }`;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
styleEl.textContent = css;
|
|
879
|
+
document.head.appendChild(styleEl);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if ('customElements' in window) {
|
|
885
|
+
customElements.define('i-glan-veleg', GlanVelegLayout);
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
|
|
891
|
+
// gwistindor
|
|
892
|
+
/**
|
|
893
|
+
* Switcher Layout Custom Element
|
|
894
|
+
*
|
|
895
|
+
* Switches between horizontal and vertical at a container threshold.
|
|
896
|
+
* Uses the "Holy Albatross" technique - no intermediary states.
|
|
897
|
+
*
|
|
898
|
+
* @property {string} threshold - Container width to switch at (default: var(--measure))
|
|
899
|
+
* @property {string} space - Gap between elements (default: var(--s1))
|
|
900
|
+
* @property {number} limit - Max items for horizontal layout (default: 4)
|
|
901
|
+
*
|
|
902
|
+
* @example
|
|
903
|
+
* <i-gwistindor threshold="30rem" limit="3">
|
|
904
|
+
* <div>One</div>
|
|
905
|
+
* <div>Two</div>
|
|
906
|
+
* <div>Three</div>
|
|
907
|
+
* </i-gwistindor>
|
|
908
|
+
*/
|
|
909
|
+
|
|
910
|
+
class GwistindorLayout extends HTMLElement {
|
|
911
|
+
static get observedAttributes() {
|
|
912
|
+
return ['threshold', 'space', 'limit'];
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
constructor() {
|
|
916
|
+
super();
|
|
917
|
+
this.render = this.render.bind(this);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
connectedCallback() {
|
|
921
|
+
this.render();
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
attributeChangedCallback() {
|
|
925
|
+
this.render();
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
get threshold() {
|
|
929
|
+
return this.getAttribute('threshold') || 'var(--measure)';
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
set threshold(val) {
|
|
933
|
+
this.setAttribute('threshold', val);
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
get space() {
|
|
937
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
set space(val) {
|
|
941
|
+
this.setAttribute('space', val);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
get limit() {
|
|
945
|
+
return parseInt(this.getAttribute('limit'), 10) || 4;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
set limit(val) {
|
|
949
|
+
this.setAttribute('limit', val);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
render() {
|
|
953
|
+
this.style.setProperty('--gwistindor-threshold', this.threshold);
|
|
954
|
+
this.style.setProperty('--gwistindor-space', this.space);
|
|
955
|
+
|
|
956
|
+
const id = `Gwistindor-${this.threshold}-${this.space}-${this.limit}`.replace(/[^\w-]/g, '');
|
|
957
|
+
this.dataset.i = id;
|
|
958
|
+
|
|
959
|
+
if (!document.getElementById(id)) {
|
|
960
|
+
const styleEl = document.createElement('style');
|
|
961
|
+
styleEl.id = id;
|
|
962
|
+
|
|
963
|
+
const selector = `[data-i="${id}"]`;
|
|
964
|
+
const limitPlusOne = this.limit + 1;
|
|
965
|
+
|
|
966
|
+
styleEl.textContent = `
|
|
967
|
+
${selector} {
|
|
968
|
+
gap: ${this.space};
|
|
969
|
+
}
|
|
970
|
+
${selector} > * {
|
|
971
|
+
flex-basis: calc((${this.threshold} - 100%) * 999);
|
|
972
|
+
}
|
|
973
|
+
${selector} > :nth-last-child(n+${limitPlusOne}),
|
|
974
|
+
${selector} > :nth-last-child(n+${limitPlusOne}) ~ * {
|
|
975
|
+
flex-basis: 100%;
|
|
976
|
+
}
|
|
977
|
+
`;
|
|
978
|
+
document.head.appendChild(styleEl);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if ('customElements' in window) {
|
|
984
|
+
customElements.define('i-gwistindor', GwistindorLayout);
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
|
|
990
|
+
// esgal
|
|
991
|
+
/**
|
|
992
|
+
* Cover Layout Custom Element
|
|
993
|
+
*
|
|
994
|
+
* Vertically centers a principal element with optional header/footer.
|
|
995
|
+
*
|
|
996
|
+
* @property {string} centered - Selector for the centered element (default: h1)
|
|
997
|
+
* @property {string} space - Minimum space around elements (default: var(--s1))
|
|
998
|
+
* @property {string} minHeight - Minimum height of cover (default: 100vh)
|
|
999
|
+
* @property {boolean} noPad - Remove padding from container
|
|
1000
|
+
*
|
|
1001
|
+
* @example
|
|
1002
|
+
* <i-esgal centered="h2" min-height="80vh">
|
|
1003
|
+
* <header>Logo</header>
|
|
1004
|
+
* <h2>Main Title</h2>
|
|
1005
|
+
* <footer>Scroll down</footer>
|
|
1006
|
+
* </i-esgal>
|
|
1007
|
+
*/
|
|
1008
|
+
|
|
1009
|
+
class EsgalLayout extends HTMLElement {
|
|
1010
|
+
static get observedAttributes() {
|
|
1011
|
+
return ['centered', 'space', 'min-height', 'no-pad'];
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
constructor() {
|
|
1015
|
+
super();
|
|
1016
|
+
this.render = this.render.bind(this);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
connectedCallback() {
|
|
1020
|
+
this.render();
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
attributeChangedCallback() {
|
|
1024
|
+
this.render();
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
get centered() {
|
|
1028
|
+
return this.getAttribute('centered') || 'h1';
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
set centered(val) {
|
|
1032
|
+
this.setAttribute('centered', val);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
get space() {
|
|
1036
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
set space(val) {
|
|
1040
|
+
this.setAttribute('space', val);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
get minHeight() {
|
|
1044
|
+
return this.getAttribute('min-height') || '100vh';
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
set minHeight(val) {
|
|
1048
|
+
this.setAttribute('min-height', val);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
get noPad() {
|
|
1052
|
+
return this.hasAttribute('no-pad');
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
set noPad(val) {
|
|
1056
|
+
if (val) {
|
|
1057
|
+
this.setAttribute('no-pad', '');
|
|
1058
|
+
} else {
|
|
1059
|
+
this.removeAttribute('no-pad');
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
render() {
|
|
1064
|
+
this.style.setProperty('--esgal-min-height', this.minHeight);
|
|
1065
|
+
this.style.setProperty('--esgal-space', this.space);
|
|
1066
|
+
|
|
1067
|
+
const noPadStr = this.noPad ? '-noPad' : '';
|
|
1068
|
+
const id = `Esgal-${this.centered}-${this.minHeight}-${this.space}${noPadStr}`.replace(/[^\w-]/g, '');
|
|
1069
|
+
this.dataset.i = id;
|
|
1070
|
+
|
|
1071
|
+
if (!document.getElementById(id)) {
|
|
1072
|
+
const styleEl = document.createElement('style');
|
|
1073
|
+
styleEl.id = id;
|
|
1074
|
+
|
|
1075
|
+
const selector = `[data-i="${id}"]`;
|
|
1076
|
+
const padding = this.noPad ? '0' : this.space;
|
|
1077
|
+
|
|
1078
|
+
styleEl.textContent = `
|
|
1079
|
+
${selector} {
|
|
1080
|
+
min-block-size: ${this.minHeight};
|
|
1081
|
+
padding: ${padding};
|
|
1082
|
+
}
|
|
1083
|
+
${selector} > * {
|
|
1084
|
+
margin-block: ${this.space};
|
|
1085
|
+
}
|
|
1086
|
+
${selector} > :first-child:not(${this.centered}) {
|
|
1087
|
+
margin-block-start: 0;
|
|
1088
|
+
}
|
|
1089
|
+
${selector} > :last-child:not(${this.centered}) {
|
|
1090
|
+
margin-block-end: 0;
|
|
1091
|
+
}
|
|
1092
|
+
${selector} > ${this.centered} {
|
|
1093
|
+
margin-block: auto;
|
|
1094
|
+
}
|
|
1095
|
+
`;
|
|
1096
|
+
document.head.appendChild(styleEl);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if ('customElements' in window) {
|
|
1102
|
+
customElements.define('i-esgal', EsgalLayout);
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
|
|
1108
|
+
// vircantie
|
|
1109
|
+
/**
|
|
1110
|
+
* Vircantie Layout Custom Element
|
|
1111
|
+
*
|
|
1112
|
+
* Auto-flowing responsive grid with minimum column width.
|
|
1113
|
+
*
|
|
1114
|
+
* @property {string} min - Minimum column width (default: 250px)
|
|
1115
|
+
* @property {string} space - Gap between grid cells (default: var(--s1))
|
|
1116
|
+
*
|
|
1117
|
+
* @example
|
|
1118
|
+
* <i-vircantie min="300px" space="var(--s2)">
|
|
1119
|
+
* <div>Card 1</div>
|
|
1120
|
+
* <div>Card 2</div>
|
|
1121
|
+
* <div>Card 3</div>
|
|
1122
|
+
* </i-vircantie>
|
|
1123
|
+
*/
|
|
1124
|
+
|
|
1125
|
+
class VircantieLayout extends HTMLElement {
|
|
1126
|
+
static get observedAttributes() {
|
|
1127
|
+
return ['min', 'space'];
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
constructor() {
|
|
1131
|
+
super();
|
|
1132
|
+
this.render = this.render.bind(this);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
connectedCallback() {
|
|
1136
|
+
this.render();
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
attributeChangedCallback() {
|
|
1140
|
+
this.render();
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
get min() {
|
|
1144
|
+
return this.getAttribute('min') || '250px';
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
set min(val) {
|
|
1148
|
+
this.setAttribute('min', val);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
get space() {
|
|
1152
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
set space(val) {
|
|
1156
|
+
this.setAttribute('space', val);
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
render() {
|
|
1160
|
+
this.style.setProperty('--vircantie-min', this.min);
|
|
1161
|
+
this.style.setProperty('--vircantie-space', this.space);
|
|
1162
|
+
|
|
1163
|
+
const id = `Vircantie-${this.min}-${this.space}`.replace(/[^\w-]/g, '');
|
|
1164
|
+
this.dataset.i = id;
|
|
1165
|
+
|
|
1166
|
+
if (!document.getElementById(id)) {
|
|
1167
|
+
const styleEl = document.createElement('style');
|
|
1168
|
+
styleEl.id = id;
|
|
1169
|
+
|
|
1170
|
+
const selector = `[data-i="${id}"]`;
|
|
1171
|
+
|
|
1172
|
+
styleEl.textContent = `
|
|
1173
|
+
${selector} {
|
|
1174
|
+
gap: ${this.space};
|
|
1175
|
+
}
|
|
1176
|
+
@supports (width: min(${this.min}, 100%)) {
|
|
1177
|
+
${selector} {
|
|
1178
|
+
vircantie-template-columns: repeat(auto-fit, minmax(min(${this.min}, 100%), 1fr));
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
`;
|
|
1182
|
+
document.head.appendChild(styleEl);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
if ('customElements' in window) {
|
|
1188
|
+
customElements.define('i-vircantie', VircantieLayout);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
|
|
1194
|
+
// gant-thala
|
|
1195
|
+
/**
|
|
1196
|
+
* Frame Layout Custom Element
|
|
1197
|
+
*
|
|
1198
|
+
* Constrains content to a specific aspect ratio.
|
|
1199
|
+
*
|
|
1200
|
+
* @property {string} ratio - GantThala ratio as "n:d" (default: 16:9)
|
|
1201
|
+
*
|
|
1202
|
+
* @example
|
|
1203
|
+
* <i-gant-thala ratio="4:3">
|
|
1204
|
+
* <img src="photo.jpg" alt="A photo">
|
|
1205
|
+
* </i-gant-thala>
|
|
1206
|
+
*/
|
|
1207
|
+
|
|
1208
|
+
class GantThalaLayout extends HTMLElement {
|
|
1209
|
+
static get observedAttributes() {
|
|
1210
|
+
return ['ratio'];
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
constructor() {
|
|
1214
|
+
super();
|
|
1215
|
+
this.render = this.render.bind(this);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
connectedCallback() {
|
|
1219
|
+
this.render();
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
attributeChangedCallback() {
|
|
1223
|
+
this.render();
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
get ratio() {
|
|
1227
|
+
return this.getAttribute('ratio') || '16:9';
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
set ratio(val) {
|
|
1231
|
+
this.setAttribute('ratio', val);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
render() {
|
|
1235
|
+
const [n, d] = this.ratio.split(':').map(v => v.trim());
|
|
1236
|
+
|
|
1237
|
+
this.style.setProperty('--gant-thala-n', n);
|
|
1238
|
+
this.style.setProperty('--gant-thala-d', d);
|
|
1239
|
+
|
|
1240
|
+
const id = `GantThala-${n}-${d}`.replace(/[^\w-]/g, '');
|
|
1241
|
+
this.dataset.i = id;
|
|
1242
|
+
|
|
1243
|
+
if (!document.getElementById(id)) {
|
|
1244
|
+
const styleEl = document.createElement('style');
|
|
1245
|
+
styleEl.id = id;
|
|
1246
|
+
|
|
1247
|
+
const selector = `[data-i="${id}"]`;
|
|
1248
|
+
|
|
1249
|
+
styleEl.textContent = `
|
|
1250
|
+
${selector} {
|
|
1251
|
+
gant-thala-ratio: ${n} / ${d};
|
|
1252
|
+
}
|
|
1253
|
+
`;
|
|
1254
|
+
document.head.appendChild(styleEl);
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if ('customElements' in window) {
|
|
1260
|
+
customElements.define('i-gant-thala', GantThalaLayout);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
// glan-tholl
|
|
1267
|
+
/**
|
|
1268
|
+
* Reel Layout Custom Element
|
|
1269
|
+
*
|
|
1270
|
+
* Horizontal scrolling container with native browser scrolling.
|
|
1271
|
+
* Uses ResizeObserver and MutationObserver for overflow detection.
|
|
1272
|
+
*
|
|
1273
|
+
* @property {string} itemWidth - Width of each item (default: auto)
|
|
1274
|
+
* @property {string} space - Gap between items (default: var(--s1))
|
|
1275
|
+
* @property {string} height - Height of the reel (default: auto)
|
|
1276
|
+
* @property {boolean} noBar - Hide the scrollbar
|
|
1277
|
+
*
|
|
1278
|
+
* @example
|
|
1279
|
+
* <i-glan-tholl item-width="300px" space="var(--s2)">
|
|
1280
|
+
* <div>Card 1</div>
|
|
1281
|
+
* <div>Card 2</div>
|
|
1282
|
+
* <div>Card 3</div>
|
|
1283
|
+
* </i-glan-tholl>
|
|
1284
|
+
*/
|
|
1285
|
+
|
|
1286
|
+
class GlanThollLayout extends HTMLElement {
|
|
1287
|
+
static get observedAttributes() {
|
|
1288
|
+
return ['item-width', 'space', 'height', 'no-bar'];
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
constructor() {
|
|
1292
|
+
super();
|
|
1293
|
+
this.render = this.render.bind(this);
|
|
1294
|
+
this.toggleOverflowClass = this.toggleOverflowClass.bind(this);
|
|
1295
|
+
this.resizeObserver = null;
|
|
1296
|
+
this.mutationObserver = null;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
connectedCallback() {
|
|
1300
|
+
this.render();
|
|
1301
|
+
this.setupObservers();
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
disconnectedCallback() {
|
|
1305
|
+
if (this.resizeObserver) {
|
|
1306
|
+
this.resizeObserver.disconnect();
|
|
1307
|
+
}
|
|
1308
|
+
if (this.mutationObserver) {
|
|
1309
|
+
this.mutationObserver.disconnect();
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
attributeChangedCallback() {
|
|
1314
|
+
this.render();
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
get itemWidth() {
|
|
1318
|
+
return this.getAttribute('item-width') || 'auto';
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
set itemWidth(val) {
|
|
1322
|
+
this.setAttribute('item-width', val);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
get space() {
|
|
1326
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
set space(val) {
|
|
1330
|
+
this.setAttribute('space', val);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
get height() {
|
|
1334
|
+
return this.getAttribute('height') || 'auto';
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
set height(val) {
|
|
1338
|
+
this.setAttribute('height', val);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
get noBar() {
|
|
1342
|
+
return this.hasAttribute('no-bar');
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
set noBar(val) {
|
|
1346
|
+
if (val) {
|
|
1347
|
+
this.setAttribute('no-bar', '');
|
|
1348
|
+
} else {
|
|
1349
|
+
this.removeAttribute('no-bar');
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
toggleOverflowClass() {
|
|
1354
|
+
this.classList.toggle('overflowing', this.scrollWidth > this.clientWidth);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
setupObservers() {
|
|
1358
|
+
// ResizeObserver for container size changes
|
|
1359
|
+
if ('ResizeObserver' in window) {
|
|
1360
|
+
this.resizeObserver = new ResizeObserver(entries => {
|
|
1361
|
+
this.toggleOverflowClass();
|
|
1362
|
+
});
|
|
1363
|
+
this.resizeObserver.observe(this);
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// MutationObserver for child changes
|
|
1367
|
+
if ('MutationObserver' in window) {
|
|
1368
|
+
this.mutationObserver = new MutationObserver(entries => {
|
|
1369
|
+
this.toggleOverflowClass();
|
|
1370
|
+
});
|
|
1371
|
+
this.mutationObserver.observe(this, { childList: true });
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// Initial check
|
|
1375
|
+
this.toggleOverflowClass();
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
render() {
|
|
1379
|
+
this.style.setProperty('--glan-tholl-item-width', this.itemWidth);
|
|
1380
|
+
this.style.setProperty('--glan-tholl-space', this.space);
|
|
1381
|
+
this.style.setProperty('--glan-tholl-height', this.height);
|
|
1382
|
+
|
|
1383
|
+
const noBarStr = this.noBar ? '-noBar' : '';
|
|
1384
|
+
const id = `GlanTholl-${this.itemWidth}-${this.space}-${this.height}${noBarStr}`.replace(/[^\w-]/g, '');
|
|
1385
|
+
this.dataset.i = id;
|
|
1386
|
+
|
|
1387
|
+
if (!document.getElementById(id)) {
|
|
1388
|
+
const styleEl = document.createElement('style');
|
|
1389
|
+
styleEl.id = id;
|
|
1390
|
+
|
|
1391
|
+
const selector = `[data-i="${id}"]`;
|
|
1392
|
+
|
|
1393
|
+
styleEl.textContent = `
|
|
1394
|
+
${selector} {
|
|
1395
|
+
block-size: ${this.height};
|
|
1396
|
+
}
|
|
1397
|
+
${selector} > * {
|
|
1398
|
+
flex: 0 0 ${this.itemWidth};
|
|
1399
|
+
}
|
|
1400
|
+
${selector} > * + * {
|
|
1401
|
+
margin-inline-start: ${this.space};
|
|
1402
|
+
}
|
|
1403
|
+
`;
|
|
1404
|
+
document.head.appendChild(styleEl);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
if ('customElements' in window) {
|
|
1410
|
+
customElements.define('i-glan-tholl', GlanThollLayout);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
// fano
|
|
1417
|
+
/**
|
|
1418
|
+
* Imposter Layout Custom Element
|
|
1419
|
+
*
|
|
1420
|
+
* Positions an element centered over a positioning container.
|
|
1421
|
+
*
|
|
1422
|
+
* @property {boolean} fixed - Use fixed positioning (viewport-relative)
|
|
1423
|
+
* @property {boolean} contain - Prevent overflow outside container
|
|
1424
|
+
* @property {string} margin - Minimum gap from container edges (when contained)
|
|
1425
|
+
*
|
|
1426
|
+
* @example
|
|
1427
|
+
* <div style="position: relative;">
|
|
1428
|
+
* <p>Background content</p>
|
|
1429
|
+
* <i-fano contain margin="var(--s1)">
|
|
1430
|
+
* <dialog open>Modal content</dialog>
|
|
1431
|
+
* </i-fano>
|
|
1432
|
+
* </div>
|
|
1433
|
+
*/
|
|
1434
|
+
|
|
1435
|
+
class FanoLayout extends HTMLElement {
|
|
1436
|
+
static get observedAttributes() {
|
|
1437
|
+
return ['fixed', 'contain', 'margin'];
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
constructor() {
|
|
1441
|
+
super();
|
|
1442
|
+
this.render = this.render.bind(this);
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
connectedCallback() {
|
|
1446
|
+
this.render();
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
attributeChangedCallback() {
|
|
1450
|
+
this.render();
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
get fixed() {
|
|
1454
|
+
return this.hasAttribute('fixed');
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
set fixed(val) {
|
|
1458
|
+
if (val) {
|
|
1459
|
+
this.setAttribute('fixed', '');
|
|
1460
|
+
} else {
|
|
1461
|
+
this.removeAttribute('fixed');
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
get contain() {
|
|
1466
|
+
return this.hasAttribute('contain');
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
set contain(val) {
|
|
1470
|
+
if (val) {
|
|
1471
|
+
this.setAttribute('contain', '');
|
|
1472
|
+
} else {
|
|
1473
|
+
this.removeAttribute('contain');
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
get margin() {
|
|
1478
|
+
return this.getAttribute('margin') || '0px';
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
set margin(val) {
|
|
1482
|
+
this.setAttribute('margin', val);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
render() {
|
|
1486
|
+
this.style.setProperty('--fano-margin', this.margin);
|
|
1487
|
+
|
|
1488
|
+
const fixedStr = this.fixed ? '-fixed' : '';
|
|
1489
|
+
const containStr = this.contain ? '-contain' : '';
|
|
1490
|
+
const id = `Fano-${this.margin}${fixedStr}${containStr}`.replace(/[^\w-]/g, '');
|
|
1491
|
+
this.dataset.i = id;
|
|
1492
|
+
|
|
1493
|
+
if (!document.getElementById(id)) {
|
|
1494
|
+
const styleEl = document.createElement('style');
|
|
1495
|
+
styleEl.id = id;
|
|
1496
|
+
|
|
1497
|
+
const selector = `[data-i="${id}"]`;
|
|
1498
|
+
let css = '';
|
|
1499
|
+
|
|
1500
|
+
if (this.fixed) {
|
|
1501
|
+
css += `${selector} { position: fixed; }`;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
if (this.contain) {
|
|
1505
|
+
css += `
|
|
1506
|
+
${selector} {
|
|
1507
|
+
overflow: auto;
|
|
1508
|
+
max-inline-size: calc(100% - (${this.margin} * 2));
|
|
1509
|
+
max-block-size: calc(100% - (${this.margin} * 2));
|
|
1510
|
+
}
|
|
1511
|
+
`;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
styleEl.textContent = css;
|
|
1515
|
+
document.head.appendChild(styleEl);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if ('customElements' in window) {
|
|
1521
|
+
customElements.define('i-fano', FanoLayout);
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
|
|
1527
|
+
// thann
|
|
1528
|
+
/**
|
|
1529
|
+
* Thann Layout Custom Element
|
|
1530
|
+
*
|
|
1531
|
+
* Aligns an SVG icon with accompanying text.
|
|
1532
|
+
*
|
|
1533
|
+
* @property {string} space - Gap between icon and text (default: natural word space)
|
|
1534
|
+
* @property {string} label - Accessible label for standalone icons
|
|
1535
|
+
*
|
|
1536
|
+
* @example
|
|
1537
|
+
* <i-thann space="0.5em">
|
|
1538
|
+
* <svg>...</svg>
|
|
1539
|
+
* Close
|
|
1540
|
+
* </i-thann>
|
|
1541
|
+
*/
|
|
1542
|
+
|
|
1543
|
+
class ThannLayout extends HTMLElement {
|
|
1544
|
+
static get observedAttributes() {
|
|
1545
|
+
return ['space', 'label'];
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
constructor() {
|
|
1549
|
+
super();
|
|
1550
|
+
this.render = this.render.bind(this);
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
connectedCallback() {
|
|
1554
|
+
this.render();
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
attributeChangedCallback() {
|
|
1558
|
+
this.render();
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
get space() {
|
|
1562
|
+
return this.getAttribute('space');
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
set space(val) {
|
|
1566
|
+
if (val) {
|
|
1567
|
+
this.setAttribute('space', val);
|
|
1568
|
+
} else {
|
|
1569
|
+
this.removeAttribute('space');
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
get label() {
|
|
1574
|
+
return this.getAttribute('label');
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
set label(val) {
|
|
1578
|
+
if (val) {
|
|
1579
|
+
this.setAttribute('label', val);
|
|
1580
|
+
} else {
|
|
1581
|
+
this.removeAttribute('label');
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
render() {
|
|
1586
|
+
// Handle accessibility for standalone icons
|
|
1587
|
+
if (this.label) {
|
|
1588
|
+
this.setAttribute('role', 'img');
|
|
1589
|
+
this.setAttribute('aria-label', this.label);
|
|
1590
|
+
} else {
|
|
1591
|
+
this.removeAttribute('role');
|
|
1592
|
+
this.removeAttribute('aria-label');
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// Only set custom property if space is specified
|
|
1596
|
+
if (this.space) {
|
|
1597
|
+
this.style.setProperty('--thann-space', this.space);
|
|
1598
|
+
|
|
1599
|
+
const id = `Thann-${this.space}`.replace(/[^\w-]/g, '');
|
|
1600
|
+
this.dataset.i = id;
|
|
1601
|
+
|
|
1602
|
+
if (!document.getElementById(id)) {
|
|
1603
|
+
const styleEl = document.createElement('style');
|
|
1604
|
+
styleEl.id = id;
|
|
1605
|
+
|
|
1606
|
+
const selector = `[data-i="${id}"]`;
|
|
1607
|
+
|
|
1608
|
+
styleEl.textContent = `
|
|
1609
|
+
${selector} {
|
|
1610
|
+
gap: ${this.space};
|
|
1611
|
+
}
|
|
1612
|
+
`;
|
|
1613
|
+
document.head.appendChild(styleEl);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
if ('customElements' in window) {
|
|
1620
|
+
customElements.define('i-thann', ThannLayout);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
|
|
1625
|
+
|
|
1626
|
+
// adleithian
|
|
1627
|
+
/**
|
|
1628
|
+
* Adleithian Layout Custom Element
|
|
1629
|
+
*
|
|
1630
|
+
* Establishes a container query context.
|
|
1631
|
+
*
|
|
1632
|
+
* @property {string} name - Adleithian name for targeted queries (optional)
|
|
1633
|
+
*
|
|
1634
|
+
* @example
|
|
1635
|
+
* <i-adleithian name="card">
|
|
1636
|
+
* <div class="card">...</div>
|
|
1637
|
+
* </i-adleithian>
|
|
1638
|
+
*
|
|
1639
|
+
* CSS:
|
|
1640
|
+
* @container card (width < 300px) {
|
|
1641
|
+
* .card { flex-direction: column; }
|
|
1642
|
+
* }
|
|
1643
|
+
*/
|
|
1644
|
+
|
|
1645
|
+
class AdleithianLayout extends HTMLElement {
|
|
1646
|
+
static get observedAttributes() {
|
|
1647
|
+
return ['name'];
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
constructor() {
|
|
1651
|
+
super();
|
|
1652
|
+
this.render = this.render.bind(this);
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
connectedCallback() {
|
|
1656
|
+
this.render();
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
attributeChangedCallback() {
|
|
1660
|
+
this.render();
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
get name() {
|
|
1664
|
+
return this.getAttribute('name');
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
set name(val) {
|
|
1668
|
+
if (val) {
|
|
1669
|
+
this.setAttribute('name', val);
|
|
1670
|
+
} else {
|
|
1671
|
+
this.removeAttribute('name');
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
render() {
|
|
1676
|
+
// Set adleithian-name via style if provided
|
|
1677
|
+
if (this.name) {
|
|
1678
|
+
this.style.containerName = this.name;
|
|
1679
|
+
} else {
|
|
1680
|
+
this.style.containerName = '';
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
if ('customElements' in window) {
|
|
1686
|
+
customElements.define('i-adleithian', AdleithianLayout);
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
|
|
1692
|
+
// him
|
|
1693
|
+
/**
|
|
1694
|
+
* Him Layout Custom Element (NEW)
|
|
1695
|
+
*
|
|
1696
|
+
* Creates sticky positioning with configurable offset.
|
|
1697
|
+
* Optionally detects "stuck" state via IntersectionObserver.
|
|
1698
|
+
*
|
|
1699
|
+
* @property {string} to - Direction to stick: top, bottom, left, right (default: top)
|
|
1700
|
+
* @property {string} offset - Distance from edge when stuck (default: 0)
|
|
1701
|
+
* @property {boolean} sentinel - Enable stuck state detection
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* <i-him offset="var(--s1)" sentinel>
|
|
1705
|
+
* <header>This header sticks</header>
|
|
1706
|
+
* </i-him>
|
|
1707
|
+
*/
|
|
1708
|
+
|
|
1709
|
+
class HimLayout extends HTMLElement {
|
|
1710
|
+
static get observedAttributes() {
|
|
1711
|
+
return ['to', 'offset', 'sentinel'];
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
constructor() {
|
|
1715
|
+
super();
|
|
1716
|
+
this.render = this.render.bind(this);
|
|
1717
|
+
this.observer = null;
|
|
1718
|
+
this.sentinelEl = null;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
connectedCallback() {
|
|
1722
|
+
this.render();
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
disconnectedCallback() {
|
|
1726
|
+
this.cleanupSentinel();
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
attributeChangedCallback() {
|
|
1730
|
+
this.render();
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
get to() {
|
|
1734
|
+
return this.getAttribute('to') || 'top';
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
set to(val) {
|
|
1738
|
+
this.setAttribute('to', val);
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
get offset() {
|
|
1742
|
+
return this.getAttribute('offset') || '0';
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
set offset(val) {
|
|
1746
|
+
this.setAttribute('offset', val);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
get sentinel() {
|
|
1750
|
+
return this.hasAttribute('sentinel');
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
set sentinel(val) {
|
|
1754
|
+
if (val) {
|
|
1755
|
+
this.setAttribute('sentinel', '');
|
|
1756
|
+
} else {
|
|
1757
|
+
this.removeAttribute('sentinel');
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
cleanupSentinel() {
|
|
1762
|
+
if (this.observer) {
|
|
1763
|
+
this.observer.disconnect();
|
|
1764
|
+
this.observer = null;
|
|
1765
|
+
}
|
|
1766
|
+
if (this.sentinelEl) {
|
|
1767
|
+
this.sentinelEl.remove();
|
|
1768
|
+
this.sentinelEl = null;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
setupSentinel() {
|
|
1773
|
+
this.cleanupSentinel();
|
|
1774
|
+
|
|
1775
|
+
if (!this.sentinel || !('IntersectionObserver' in window)) {
|
|
1776
|
+
return;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// Create invisible sentinel element
|
|
1780
|
+
this.sentinelEl = document.createElement('div');
|
|
1781
|
+
this.sentinelEl.style.cssText = `
|
|
1782
|
+
position: absolute;
|
|
1783
|
+
height: 1px;
|
|
1784
|
+
width: 100%;
|
|
1785
|
+
pointer-events: none;
|
|
1786
|
+
visibility: hidden;
|
|
1787
|
+
`;
|
|
1788
|
+
|
|
1789
|
+
// Position sentinel based on sticky direction
|
|
1790
|
+
if (this.to === 'bottom') {
|
|
1791
|
+
this.sentinelEl.style.bottom = '0';
|
|
1792
|
+
this.insertAdjacentElement('afterend', this.sentinelEl);
|
|
1793
|
+
} else {
|
|
1794
|
+
this.sentinelEl.style.top = '0';
|
|
1795
|
+
this.insertAdjacentElement('beforebegin', this.sentinelEl);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// Observe the sentinel
|
|
1799
|
+
this.observer = new IntersectionObserver(
|
|
1800
|
+
(entries) => {
|
|
1801
|
+
entries.forEach((entry) => {
|
|
1802
|
+
// When sentinel is not visible, we're "stuck"
|
|
1803
|
+
const isStuck = !entry.isIntersecting;
|
|
1804
|
+
this.dataset.stuck = isStuck;
|
|
1805
|
+
this.dispatchEvent(new CustomEvent('stuck-change', {
|
|
1806
|
+
detail: { stuck: isStuck }
|
|
1807
|
+
}));
|
|
1808
|
+
});
|
|
1809
|
+
},
|
|
1810
|
+
{ threshold: [0] }
|
|
1811
|
+
);
|
|
1812
|
+
|
|
1813
|
+
this.observer.observe(this.sentinelEl);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
render() {
|
|
1817
|
+
this.style.setProperty('--him-offset', this.offset);
|
|
1818
|
+
|
|
1819
|
+
const id = `Him-${this.to}-${this.offset}`.replace(/[^\w-]/g, '');
|
|
1820
|
+
this.dataset.i = id;
|
|
1821
|
+
|
|
1822
|
+
if (!document.getElementById(id)) {
|
|
1823
|
+
const styleEl = document.createElement('style');
|
|
1824
|
+
styleEl.id = id;
|
|
1825
|
+
|
|
1826
|
+
const selector = `[data-i="${id}"]`;
|
|
1827
|
+
let css = '';
|
|
1828
|
+
|
|
1829
|
+
switch (this.to) {
|
|
1830
|
+
case 'bottom':
|
|
1831
|
+
css = `${selector} { inset-block-start: auto; inset-block-end: ${this.offset}; }`;
|
|
1832
|
+
break;
|
|
1833
|
+
case 'left':
|
|
1834
|
+
css = `${selector} { inset-block-start: auto; inset-inline-start: ${this.offset}; }`;
|
|
1835
|
+
break;
|
|
1836
|
+
case 'right':
|
|
1837
|
+
css = `${selector} { inset-block-start: auto; inset-inline-end: ${this.offset}; }`;
|
|
1838
|
+
break;
|
|
1839
|
+
default: // top
|
|
1840
|
+
css = `${selector} { inset-block-start: ${this.offset}; }`;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
styleEl.textContent = css;
|
|
1844
|
+
document.head.appendChild(styleEl);
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// Setup or cleanup sentinel observation
|
|
1848
|
+
if (this.sentinel) {
|
|
1849
|
+
// Defer to ensure element is in DOM
|
|
1850
|
+
requestAnimationFrame(() => this.setupSentinel());
|
|
1851
|
+
} else {
|
|
1852
|
+
this.cleanupSentinel();
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
if ('customElements' in window) {
|
|
1858
|
+
customElements.define('i-him', HimLayout);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
|
|
1864
|
+
// miriant
|
|
1865
|
+
/**
|
|
1866
|
+
* Grid-Placed Layout Custom Element (NEW)
|
|
1867
|
+
*
|
|
1868
|
+
* A CSS Grid with explicit control over columns, rows, and item placement.
|
|
1869
|
+
*
|
|
1870
|
+
* @property {number} columns - Number of grid columns (default: 12)
|
|
1871
|
+
* @property {string} space - Gap between grid cells (default: var(--s1))
|
|
1872
|
+
* @property {string} rowHeight - Height of auto-generated rows (default: minmax(0, auto))
|
|
1873
|
+
* @property {boolean} dense - Enable dense packing to fill holes
|
|
1874
|
+
*
|
|
1875
|
+
* Children can use data attributes for placement:
|
|
1876
|
+
* - data-col-span="N" - Span N columns (1-12 or "full")
|
|
1877
|
+
* - data-row-span="N" - Span N rows (1-6)
|
|
1878
|
+
* - data-col-start="N" - Start at column N
|
|
1879
|
+
* - data-row-start="N" - Start at row N
|
|
1880
|
+
*
|
|
1881
|
+
* @example
|
|
1882
|
+
* <i-miriant columns="4" space="var(--s2)">
|
|
1883
|
+
* <div data-col-span="2" data-row-span="2">Feature</div>
|
|
1884
|
+
* <div>Small</div>
|
|
1885
|
+
* <div>Small</div>
|
|
1886
|
+
* <div data-col-span="full">Full width</div>
|
|
1887
|
+
* </i-miriant>
|
|
1888
|
+
*/
|
|
1889
|
+
|
|
1890
|
+
class MiriantLayout extends HTMLElement {
|
|
1891
|
+
static get observedAttributes() {
|
|
1892
|
+
return ['columns', 'space', 'row-height', 'dense'];
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
constructor() {
|
|
1896
|
+
super();
|
|
1897
|
+
this.render = this.render.bind(this);
|
|
1898
|
+
this.mutationObserver = null;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
connectedCallback() {
|
|
1902
|
+
this.render();
|
|
1903
|
+
this.setupChildObserver();
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
disconnectedCallback() {
|
|
1907
|
+
if (this.mutationObserver) {
|
|
1908
|
+
this.mutationObserver.disconnect();
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
attributeChangedCallback() {
|
|
1913
|
+
this.render();
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
get columns() {
|
|
1917
|
+
return parseInt(this.getAttribute('columns'), 10) || 12;
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
set columns(val) {
|
|
1921
|
+
this.setAttribute('columns', val);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
get space() {
|
|
1925
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
set space(val) {
|
|
1929
|
+
this.setAttribute('space', val);
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
get rowHeight() {
|
|
1933
|
+
return this.getAttribute('row-height') || 'minmax(0, auto)';
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
set rowHeight(val) {
|
|
1937
|
+
this.setAttribute('row-height', val);
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
get dense() {
|
|
1941
|
+
return this.hasAttribute('dense');
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
set dense(val) {
|
|
1945
|
+
if (val) {
|
|
1946
|
+
this.setAttribute('dense', '');
|
|
1947
|
+
} else {
|
|
1948
|
+
this.removeAttribute('dense');
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
setupChildObserver() {
|
|
1953
|
+
if (!('MutationObserver' in window)) return;
|
|
1954
|
+
|
|
1955
|
+
this.mutationObserver = new MutationObserver((mutations) => {
|
|
1956
|
+
mutations.forEach((mutation) => {
|
|
1957
|
+
if (mutation.type === 'childList') {
|
|
1958
|
+
this.styleChildren();
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
});
|
|
1962
|
+
|
|
1963
|
+
this.mutationObserver.observe(this, { childList: true });
|
|
1964
|
+
this.styleChildren();
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
styleChildren() {
|
|
1968
|
+
// Apply inline styles for explicit placement (data-col-start, data-row-start)
|
|
1969
|
+
Array.from(this.children).forEach((child) => {
|
|
1970
|
+
const colStart = child.dataset.colStart;
|
|
1971
|
+
const rowStart = child.dataset.rowStart;
|
|
1972
|
+
|
|
1973
|
+
if (colStart) {
|
|
1974
|
+
child.style.gridColumnStart = colStart;
|
|
1975
|
+
}
|
|
1976
|
+
if (rowStart) {
|
|
1977
|
+
child.style.gridRowStart = rowStart;
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
render() {
|
|
1983
|
+
this.style.setProperty('--miriant-columns', this.columns);
|
|
1984
|
+
this.style.setProperty('--miriant-space', this.space);
|
|
1985
|
+
this.style.setProperty('--miriant-row-height', this.rowHeight);
|
|
1986
|
+
|
|
1987
|
+
const denseStr = this.dense ? '-dense' : '';
|
|
1988
|
+
const id = `Miriant-${this.columns}-${this.space}-${this.rowHeight}${denseStr}`.replace(/[^\w-]/g, '');
|
|
1989
|
+
this.dataset.i = id;
|
|
1990
|
+
|
|
1991
|
+
// Set container-type for container queries
|
|
1992
|
+
this.style.containerType = 'inline-size';
|
|
1993
|
+
|
|
1994
|
+
if (!document.getElementById(id)) {
|
|
1995
|
+
const styleEl = document.createElement('style');
|
|
1996
|
+
styleEl.id = id;
|
|
1997
|
+
|
|
1998
|
+
const selector = `[data-i="${id}"]`;
|
|
1999
|
+
|
|
2000
|
+
let css = `
|
|
2001
|
+
${selector} {
|
|
2002
|
+
grid-template-columns: repeat(${this.columns}, 1fr);
|
|
2003
|
+
gap: ${this.space};
|
|
2004
|
+
grid-auto-rows: ${this.rowHeight};
|
|
2005
|
+
}
|
|
2006
|
+
`;
|
|
2007
|
+
|
|
2008
|
+
if (this.dense) {
|
|
2009
|
+
css += `${selector} { grid-auto-flow: dense; }`;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
styleEl.textContent = css;
|
|
2013
|
+
document.head.appendChild(styleEl);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
if ('customElements' in window) {
|
|
2019
|
+
customElements.define('i-miriant', MiriantLayout);
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
|
|
2023
|
+
|
|
2024
|
+
|
|
2025
|
+
// gonath
|
|
2026
|
+
/**
|
|
2027
|
+
* Gonath Layout Custom Element (NEW)
|
|
2028
|
+
*
|
|
2029
|
+
* Pinterest-style layout with efficient column packing.
|
|
2030
|
+
*
|
|
2031
|
+
* Uses CSS columns (widely supported) with progressive enhancement
|
|
2032
|
+
* to CSS Grid masonry where available.
|
|
2033
|
+
*
|
|
2034
|
+
* @property {number} columns - Number of columns (default: 3)
|
|
2035
|
+
* @property {string} space - Gap between items (default: var(--s1))
|
|
2036
|
+
*
|
|
2037
|
+
* NOTE: CSS columns flow top-to-bottom, then left-to-right.
|
|
2038
|
+
* This means visual order differs from DOM order.
|
|
2039
|
+
* For true masonry ordering, JavaScript is required.
|
|
2040
|
+
*
|
|
2041
|
+
* @example
|
|
2042
|
+
* <i-gonath columns="4" space="var(--s2)">
|
|
2043
|
+
* <div>Item 1</div>
|
|
2044
|
+
* <div>Taller Item 2</div>
|
|
2045
|
+
* <div>Item 3</div>
|
|
2046
|
+
* ...
|
|
2047
|
+
* </i-gonath>
|
|
2048
|
+
*/
|
|
2049
|
+
|
|
2050
|
+
class GonathLayout extends HTMLElement {
|
|
2051
|
+
static get observedAttributes() {
|
|
2052
|
+
return ['columns', 'space'];
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
constructor() {
|
|
2056
|
+
super();
|
|
2057
|
+
this.render = this.render.bind(this);
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
connectedCallback() {
|
|
2061
|
+
this.render();
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
attributeChangedCallback() {
|
|
2065
|
+
this.render();
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
get columns() {
|
|
2069
|
+
return parseInt(this.getAttribute('columns'), 10) || 3;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
set columns(val) {
|
|
2073
|
+
this.setAttribute('columns', val);
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
get space() {
|
|
2077
|
+
return this.getAttribute('space') || 'var(--s1)';
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
set space(val) {
|
|
2081
|
+
this.setAttribute('space', val);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// Check if CSS Grid masonry is supported
|
|
2085
|
+
static get isGridGonathSupported() {
|
|
2086
|
+
return CSS.supports('grid-template-rows', 'masonry');
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
render() {
|
|
2090
|
+
this.style.setProperty('--gonath-columns', this.columns);
|
|
2091
|
+
this.style.setProperty('--gonath-space', this.space);
|
|
2092
|
+
|
|
2093
|
+
const id = `Gonath-${this.columns}-${this.space}`.replace(/[^\w-]/g, '');
|
|
2094
|
+
this.dataset.i = id;
|
|
2095
|
+
|
|
2096
|
+
if (!document.getElementById(id)) {
|
|
2097
|
+
const styleEl = document.createElement('style');
|
|
2098
|
+
styleEl.id = id;
|
|
2099
|
+
|
|
2100
|
+
const selector = `[data-i="${id}"]`;
|
|
2101
|
+
|
|
2102
|
+
let css = `
|
|
2103
|
+
${selector} {
|
|
2104
|
+
column-count: ${this.columns};
|
|
2105
|
+
column-gap: ${this.space};
|
|
2106
|
+
}
|
|
2107
|
+
${selector} > * {
|
|
2108
|
+
margin-block-end: ${this.space};
|
|
2109
|
+
}
|
|
2110
|
+
`;
|
|
2111
|
+
|
|
2112
|
+
// Progressive enhancement for CSS Grid masonry
|
|
2113
|
+
css += `
|
|
2114
|
+
@supports (grid-template-rows: masonry) {
|
|
2115
|
+
${selector} {
|
|
2116
|
+
display: grid;
|
|
2117
|
+
column-count: unset;
|
|
2118
|
+
grid-template-columns: repeat(${this.columns}, 1fr);
|
|
2119
|
+
grid-template-rows: masonry;
|
|
2120
|
+
gap: ${this.space};
|
|
2121
|
+
}
|
|
2122
|
+
${selector} > * {
|
|
2123
|
+
margin-block-end: 0;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
`;
|
|
2127
|
+
|
|
2128
|
+
styleEl.textContent = css;
|
|
2129
|
+
document.head.appendChild(styleEl);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
if ('customElements' in window) {
|
|
2135
|
+
customElements.define('i-gonath', GonathLayout);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
|
|
2141
|
+
|
|
2142
|
+
|
|
2143
|
+
// Expose to window
|
|
2144
|
+
window.Elvish = {
|
|
2145
|
+
HathLayout,
|
|
2146
|
+
BauLayout,
|
|
2147
|
+
EnedhLayout,
|
|
2148
|
+
TiniathLayout,
|
|
2149
|
+
GlanVelegLayout,
|
|
2150
|
+
GwistindorLayout,
|
|
2151
|
+
EsgalLayout,
|
|
2152
|
+
VircantieLayout,
|
|
2153
|
+
GantThalaLayout,
|
|
2154
|
+
GlanThollLayout,
|
|
2155
|
+
FanoLayout,
|
|
2156
|
+
ThannLayout,
|
|
2157
|
+
AdleithianLayout,
|
|
2158
|
+
HimLayout,
|
|
2159
|
+
MiriantLayout,
|
|
2160
|
+
GonathLayout,
|
|
2161
|
+
transition: transition,
|
|
2162
|
+
transitionTo: transitionTo,
|
|
2163
|
+
transitionTheme: transitionTheme,
|
|
2164
|
+
transitionRatio: transitionRatio,
|
|
2165
|
+
transitionLayout: transitionLayout,
|
|
2166
|
+
supportsViewTransitions: supportsViewTransitions,
|
|
2167
|
+
VERSION: '2.0.0'
|
|
2168
|
+
};
|
|
2169
|
+
})();
|