@jungherz-de/glasskit-elements 0.8.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 +263 -0
- package/dist/glasskit-elements.esm.js +1789 -0
- package/dist/glasskit-elements.js +1819 -0
- package/dist/glasskit-elements.min.js +1 -0
- package/package.json +43 -0
- package/src/base.js +181 -0
- package/src/components/buttons/glk-button.js +80 -0
- package/src/components/containers/glk-accordion-item.js +61 -0
- package/src/components/containers/glk-accordion.js +12 -0
- package/src/components/content/glk-avatar.js +58 -0
- package/src/components/content/glk-badge.js +37 -0
- package/src/components/content/glk-card.js +33 -0
- package/src/components/content/glk-divider.js +11 -0
- package/src/components/content/glk-status.js +35 -0
- package/src/components/content/glk-title.js +16 -0
- package/src/components/feedback/glk-modal.js +98 -0
- package/src/components/feedback/glk-progress.js +77 -0
- package/src/components/feedback/glk-toast.js +104 -0
- package/src/components/forms/glk-checkbox.js +103 -0
- package/src/components/forms/glk-input.js +138 -0
- package/src/components/forms/glk-radio.js +109 -0
- package/src/components/forms/glk-range.js +112 -0
- package/src/components/forms/glk-search.js +87 -0
- package/src/components/forms/glk-select.js +96 -0
- package/src/components/forms/glk-textarea.js +95 -0
- package/src/components/forms/glk-toggle.js +121 -0
- package/src/components/navigation/glk-nav.js +12 -0
- package/src/components/navigation/glk-pill.js +48 -0
- package/src/components/navigation/glk-tab-bar.js +31 -0
- package/src/components/navigation/glk-tab-item.js +74 -0
- package/src/index.js +38 -0
|
@@ -0,0 +1,1789 @@
|
|
|
1
|
+
// GlassKit v1.3.3 – Constructable Stylesheet
|
|
2
|
+
// Auto-generated by build-styles-js.mjs – do not edit manually.
|
|
3
|
+
|
|
4
|
+
const css = "[class*=gl-],[class*=glass-],[class*=glass-]::after,[class*=glass-]::before{box-sizing:border-box}:root,[data-theme=dark]{--gl-color-primary:#f5a623;--gl-color-primary-dark:#d4692a;--gl-color-primary-mid:#e07a24;--gl-color-bg-dark:#0e2530;--gl-color-bg-mid:#1a3d4e;--gl-color-bg-light:#1b3e50;--gl-color-text:#ffffff;--gl-color-text-muted:rgba(255, 255, 255, 0.60);--gl-color-text-on-light:#1a3a4a;--gl-color-text-heading:#ffffff;--gl-color-success:#34c759;--gl-color-error:#ff3b30;--gl-color-warning:#ffcc00;--gl-surface-1:rgba(255, 255, 255, 0.08);--gl-surface-2:rgba(255, 255, 255, 0.10);--gl-surface-3:rgba(255, 255, 255, 0.14);--gl-surface-4:rgba(255, 255, 255, 0.16);--gl-surface-5:rgba(255, 255, 255, 0.22);--gl-surface-milk:rgba(255, 255, 255, 0.55);--gl-surface-milk-strong:rgba(255, 255, 255, 0.75);--gl-surface-overlay:rgba(0, 0, 0, 0.50);--gl-card-glow-top:rgba(255, 255, 255, 0.32);--gl-card-glow-mid:rgba(255, 255, 255, 0.22);--gl-card-glow-low:rgba(255, 255, 255, 0.14);--gl-card-glow-bottom:rgba(255, 255, 255, 0.08);--gl-card-icon-color:rgba(255, 255, 255, 0.8);--gl-card-text-color:rgba(255, 255, 255, 0.7);--gl-border-subtle:rgba(255, 255, 255, 0.18);--gl-border-medium:rgba(255, 255, 255, 0.30);--gl-border-strong:rgba(255, 255, 255, 0.40);--gl-border-milk:rgba(255, 255, 255, 0.60);--gl-border-warm:rgba(255, 200, 100, 0.35);--gl-border-focus:rgba(245, 166, 35, 0.60);--gl-icon-default:rgba(255, 255, 255, 0.6);--gl-icon-muted:rgba(255, 255, 255, 0.45);--gl-icon-strong:rgba(255, 255, 255, 0.8);--gl-icon-on-primary:#ffffff;--gl-icon-on-secondary:#1a3a4a;--gl-icon-on-tertiary:rgba(255, 255, 255, 0.6);--gl-blur:24px;--gl-blur-light:16px;--gl-blur-soft:12px;--gl-blur-heavy:40px;--gl-radius-pill:50%;--gl-radius-full:9999px;--gl-radius-card:24px;--gl-radius-btn:16px;--gl-radius-input:14px;--gl-radius-sm:12px;--gl-radius-xs:8px;--gl-shadow-card:0 8px 32px rgba(0, 0, 0, 0.18);--gl-shadow-btn:0 4px 16px rgba(0, 0, 0, 0.12);--gl-shadow-btn-primary:0 6px 24px rgba(230, 130, 40, 0.35),0 2px 8px rgba(0, 0, 0, 0.15);--gl-shadow-glow:0 2px 12px rgba(255, 255, 255, 0.08),0 4px 16px rgba(0, 0, 0, 0.15);--gl-shadow-modal:0 24px 80px rgba(0, 0, 0, 0.4),0 8px 32px rgba(0, 0, 0, 0.2);--gl-shadow-toast:0 8px 32px rgba(0, 0, 0, 0.25);--gl-shadow-focus:0 0 0 3px rgba(245, 166, 35, 0.3);--gl-inset-subtle:inset 0 1px 1px rgba(255, 255, 255, 0.10);--gl-inset-medium:inset 0 1px 1px rgba(255, 255, 255, 0.15);--gl-inset-strong:inset 0 1px 2px rgba(255, 255, 255, 0.25);--gl-inset-milk:inset 0 1px 2px rgba(255, 255, 255, 0.40);--gl-bg-aurora-1:rgba(60, 160, 160, 0.25);--gl-bg-aurora-2:rgba(200, 140, 60, 0.12);--gl-bg-aurora-3:rgba(80, 180, 200, 0.18);--gl-bg-light-streak-1:rgba(90, 210, 200, 0.14);--gl-bg-light-streak-2:rgba(110, 200, 190, 0.10);--gl-bg-warm-glow:rgba(220, 160, 70, 0.10);--gl-space-2xs:4px;--gl-space-xs:8px;--gl-space-sm:12px;--gl-space-md:16px;--gl-space-lg:20px;--gl-space-xl:24px;--gl-space-2xl:32px;--gl-space-3xl:40px;--gl-space-4xl:56px;--gl-font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','Segoe UI',Roboto,Helvetica,Arial,sans-serif;--gl-font-size-xs:13px;--gl-font-size-sm:14px;--gl-font-size-base:15px;--gl-font-size-btn:16px;--gl-font-size-lg:18px;--gl-font-size-title:24px;--gl-font-size-modal:20px;--gl-font-weight-normal:400;--gl-font-weight-medium:500;--gl-font-weight-semibold:600;--gl-transition:all 0.25s ease;--gl-transition-fast:all 0.15s ease;--gl-transition-modal:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);--gl-tab-bar-height:82px;--gl-placeholder:rgba(255, 255, 255, 0.35);--gl-accordion-body-color:var(--gl-color-text-muted);--gl-toggle-thumb-bg:linear-gradient(to bottom,\n rgba(255,255,255,0.9) 0%,\n rgba(255,255,255,0.7) 100%);--gl-range-track-bg:var(--gl-surface-2);--gl-range-track-border:var(--gl-border-subtle)}[data-theme=light]{--gl-color-primary:#e8852d;--gl-color-primary-dark:#c96a1e;--gl-color-primary-mid:#d97826;--gl-color-bg-dark:#e8ecf1;--gl-color-bg-mid:#f0f4f8;--gl-color-bg-light:#f5f7fa;--gl-color-text:#1a2a36;--gl-color-text-muted:rgba(26, 42, 54, 0.55);--gl-color-text-on-light:#1a3a4a;--gl-color-text-heading:#0f1f2a;--gl-color-success:#28a745;--gl-color-error:#dc3545;--gl-color-warning:#e6a800;--gl-surface-1:rgba(255, 255, 255, 0.50);--gl-surface-2:rgba(255, 255, 255, 0.60);--gl-surface-3:rgba(255, 255, 255, 0.70);--gl-surface-4:rgba(255, 255, 255, 0.75);--gl-surface-5:rgba(255, 255, 255, 0.85);--gl-surface-milk:rgba(255, 255, 255, 0.85);--gl-surface-milk-strong:rgba(255, 255, 255, 0.92);--gl-surface-overlay:rgba(0, 0, 0, 0.25);--gl-card-glow-top:rgba(255, 255, 255, 0.90);--gl-card-glow-mid:rgba(255, 255, 255, 0.78);--gl-card-glow-low:rgba(255, 255, 255, 0.65);--gl-card-glow-bottom:rgba(255, 255, 255, 0.50);--gl-card-icon-color:rgba(26, 42, 54, 0.5);--gl-card-text-color:rgba(26, 42, 54, 0.6);--gl-border-subtle:rgba(0, 0, 0, 0.08);--gl-border-medium:rgba(0, 0, 0, 0.12);--gl-border-strong:rgba(0, 0, 0, 0.15);--gl-border-milk:rgba(0, 0, 0, 0.10);--gl-border-warm:rgba(232, 133, 45, 0.30);--gl-border-focus:rgba(232, 133, 45, 0.50);--gl-icon-default:rgba(26, 42, 54, 0.50);--gl-icon-muted:rgba(26, 42, 54, 0.35);--gl-icon-strong:rgba(26, 42, 54, 0.70);--gl-icon-on-primary:#ffffff;--gl-icon-on-secondary:#1a3a4a;--gl-icon-on-tertiary:rgba(26, 42, 54, 0.50);--gl-shadow-card:0 4px 20px rgba(0, 0, 0, 0.06);--gl-shadow-btn:0 2px 10px rgba(0, 0, 0, 0.06);--gl-shadow-btn-primary:0 4px 16px rgba(232, 133, 45, 0.25),0 2px 6px rgba(0, 0, 0, 0.08);--gl-shadow-glow:0 2px 8px rgba(0, 0, 0, 0.05),0 4px 12px rgba(0, 0, 0, 0.04);--gl-shadow-modal:0 16px 48px rgba(0, 0, 0, 0.12),0 4px 16px rgba(0, 0, 0, 0.08);--gl-shadow-toast:0 4px 20px rgba(0, 0, 0, 0.10);--gl-shadow-focus:0 0 0 3px rgba(232, 133, 45, 0.25);--gl-inset-subtle:inset 0 1px 1px rgba(255, 255, 255, 0.60);--gl-inset-medium:inset 0 1px 1px rgba(255, 255, 255, 0.70);--gl-inset-strong:inset 0 1px 2px rgba(255, 255, 255, 0.80);--gl-inset-milk:inset 0 1px 2px rgba(255, 255, 255, 0.90);--gl-bg-aurora-1:rgba(100, 200, 220, 0.15);--gl-bg-aurora-2:rgba(240, 180, 100, 0.10);--gl-bg-aurora-3:rgba(120, 200, 230, 0.12);--gl-bg-light-streak-1:rgba(140, 230, 220, 0.10);--gl-bg-light-streak-2:rgba(160, 220, 210, 0.08);--gl-bg-warm-glow:rgba(240, 180, 100, 0.08);--gl-placeholder:rgba(26, 42, 54, 0.30);--gl-accordion-body-color:var(--gl-color-text-muted);--gl-toggle-thumb-bg:linear-gradient(to bottom, #ffffff 0%, #f0f0f0 100%);--gl-range-track-bg:var(--gl-surface-2);--gl-range-track-border:var(--gl-border-subtle)}.glass-bg{background:radial-gradient(ellipse at 20% 40%,var(--gl-bg-aurora-1) 0,transparent 55%),radial-gradient(ellipse at 80% 65%,var(--gl-bg-aurora-2) 0,transparent 50%),radial-gradient(ellipse at 50% 20%,var(--gl-bg-aurora-3) 0,transparent 50%),linear-gradient(170deg,var(--gl-color-bg-light) 0,var(--gl-color-bg-mid) 50%,var(--gl-color-bg-dark) 100%);position:relative;min-height:100vh;overflow-x:hidden;font-family:var(--gl-font-family);color:var(--gl-color-text);-webkit-font-smoothing:antialiased;transition:background .4s,color .3s}.glass-bg::before{content:'';position:absolute;top:20%;left:-30%;width:160%;height:55%;background:radial-gradient(ellipse at 35% 45%,var(--gl-bg-light-streak-1) 0,transparent 50%),radial-gradient(ellipse at 65% 55%,var(--gl-bg-light-streak-2) 0,transparent 45%);transform:rotate(-8deg);pointer-events:none;z-index:0}.glass-bg::after{content:'';position:absolute;top:40%;right:-20%;width:70%;height:50%;background:radial-gradient(ellipse at center,var(--gl-bg-warm-glow) 0,transparent 65%);pointer-events:none;z-index:0}.glass-bg>*{position:relative;z-index:1}.glass-bg--has-tab-bar{padding-bottom:var(--gl-tab-bar-height)}.glass-nav{display:flex;justify-content:space-between;align-items:center;padding:0 var(--gl-space-xs)}.glass-pill{width:46px;height:46px;border-radius:var(--gl-radius-pill);background:var(--gl-surface-3);backdrop-filter:blur(var(--gl-blur));-webkit-backdrop-filter:blur(var(--gl-blur));border:1.5px solid var(--gl-border-strong);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:var(--gl-transition);color:var(--gl-color-text);box-shadow:var(--gl-shadow-glow),var(--gl-inset-medium);outline:0;-webkit-tap-highlight-color:transparent}.glass-pill:hover{background:var(--gl-surface-5);transform:scale(1.05)}.glass-pill:active{transform:scale(.95)}.glass-pill svg{width:20px;height:20px;fill:none;stroke:var(--gl-color-text);stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}.glass-title{text-align:center;color:var(--gl-color-text-heading);font-size:var(--gl-font-size-title);font-weight:var(--gl-font-weight-semibold);line-height:1.3;text-shadow:0 2px 12px rgba(0,0,0,.15);letter-spacing:-.3px}.glass-card{background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur));-webkit-backdrop-filter:blur(var(--gl-blur));border:1.5px solid var(--gl-border-medium);border-radius:var(--gl-radius-card);padding:var(--gl-space-3xl) var(--gl-space-xl) var(--gl-space-2xl);box-shadow:var(--gl-shadow-card),var(--gl-inset-strong)}.glass-card--glow{position:relative;overflow:hidden;background:linear-gradient(to bottom,var(--gl-card-glow-top) 0,var(--gl-card-glow-mid) 25%,var(--gl-card-glow-low) 55%,var(--gl-card-glow-bottom) 100%)}.glass-card--glow::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent 0,rgba(255,255,255,.5) 30%,rgba(255,255,255,.6) 50%,rgba(255,255,255,.5) 70%,transparent 100%);z-index:1}.glass-card__icon{display:flex;align-items:center;justify-content:center;margin:0 auto var(--gl-space-lg)}.glass-card__icon svg{width:64px;height:64px;stroke:var(--gl-card-icon-color);stroke-width:1.2;fill:none}.glass-card__text{color:var(--gl-card-text-color);font-size:var(--gl-font-size-base);font-weight:var(--gl-font-weight-normal);line-height:1.55;text-align:center}.glass-btn{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;height:56px;border:none;border-radius:var(--gl-radius-btn);font-family:var(--gl-font-family);font-size:var(--gl-font-size-btn);font-weight:var(--gl-font-weight-semibold);cursor:pointer;transition:var(--gl-transition);outline:0;-webkit-tap-highlight-color:transparent}.glass-btn svg{width:20px;height:20px;flex-shrink:0}.glass-btn--primary{background:linear-gradient(135deg,var(--gl-color-primary) 0,var(--gl-color-primary-mid) 50%,var(--gl-color-primary-dark) 100%);color:#fff;border:1px solid var(--gl-border-warm);box-shadow:var(--gl-shadow-btn-primary),var(--gl-inset-strong)}.glass-btn--primary svg{fill:var(--gl-icon-on-primary);stroke:var(--gl-icon-on-primary);stroke-width:0}.glass-btn--primary:hover{transform:translateY(-1px)}.glass-btn--primary:active{transform:translateY(1px)}.glass-btn--secondary{background:linear-gradient(to bottom,var(--gl-surface-milk-strong) 0,var(--gl-surface-milk) 100%);backdrop-filter:blur(var(--gl-blur));-webkit-backdrop-filter:blur(var(--gl-blur));border:1.5px solid var(--gl-border-milk);color:var(--gl-color-text-on-light);box-shadow:var(--gl-shadow-btn),var(--gl-inset-milk)}.glass-btn--secondary svg{fill:none;stroke:var(--gl-icon-on-secondary);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.glass-btn--secondary:hover{transform:translateY(-1px)}.glass-btn--secondary:active{transform:translateY(1px)}.glass-btn--tertiary{background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-light));-webkit-backdrop-filter:blur(var(--gl-blur-light));border:1px solid var(--gl-border-subtle);color:var(--gl-color-text-muted);box-shadow:0 4px 12px rgba(0,0,0,.1),var(--gl-inset-subtle)}.glass-btn--tertiary svg{fill:none;stroke:var(--gl-icon-on-tertiary);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.glass-btn--tertiary:hover{background:var(--gl-surface-4);color:var(--gl-color-text);transform:translateY(-1px)}.glass-btn--tertiary:active{transform:translateY(1px)}.glass-btn--sm{height:44px;font-size:var(--gl-font-size-sm);border-radius:var(--gl-radius-sm)}.glass-btn--lg{height:64px;font-size:var(--gl-font-size-lg)}.glass-btn--auto{width:auto;padding:0 var(--gl-space-xl)}.glass-status{background:var(--gl-surface-1);backdrop-filter:blur(var(--gl-blur-light));-webkit-backdrop-filter:blur(var(--gl-blur-light));border:1px solid var(--gl-border-subtle);border-radius:var(--gl-radius-btn);padding:var(--gl-space-md) var(--gl-space-lg);display:flex;align-items:center;gap:var(--gl-space-sm);box-shadow:0 4px 12px rgba(0,0,0,.08),var(--gl-inset-subtle)}.glass-status svg{width:20px;height:20px;flex-shrink:0;stroke:var(--gl-icon-muted);stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}.glass-status p{color:var(--gl-color-text-muted);font-size:var(--gl-font-size-sm);font-weight:var(--gl-font-weight-normal);line-height:1.45}.glass-input-group{display:flex;flex-direction:column;gap:var(--gl-space-xs)}.glass-label{font-size:var(--gl-font-size-sm);font-weight:var(--gl-font-weight-medium);color:var(--gl-icon-strong);padding-left:var(--gl-space-2xs)}.glass-input{width:100%;height:52px;padding:0 var(--gl-space-md);background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-light));-webkit-backdrop-filter:blur(var(--gl-blur-light));border:1.5px solid var(--gl-border-medium);border-radius:var(--gl-radius-input);color:var(--gl-color-text);font-family:var(--gl-font-family);font-size:var(--gl-font-size-base);transition:var(--gl-transition);outline:0;box-shadow:var(--gl-inset-subtle);-webkit-tap-highlight-color:transparent}.glass-input::placeholder{color:var(--gl-placeholder)}.glass-input:focus{border-color:var(--gl-border-focus);background:var(--gl-surface-3);box-shadow:var(--gl-shadow-focus),var(--gl-inset-medium)}.glass-input:disabled{opacity:.4;cursor:not-allowed}.glass-input--error{border-color:var(--gl-color-error)}.glass-input--error:focus{box-shadow:0 0 0 3px rgba(255,59,48,.25),var(--gl-inset-medium)}.glass-hint{font-size:var(--gl-font-size-xs);color:var(--gl-color-text-muted);padding-left:var(--gl-space-2xs)}.glass-hint--error{color:var(--gl-color-error)}.glass-textarea{width:100%;min-height:120px;padding:var(--gl-space-md);background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-light));-webkit-backdrop-filter:blur(var(--gl-blur-light));border:1.5px solid var(--gl-border-medium);border-radius:var(--gl-radius-input);color:var(--gl-color-text);font-family:var(--gl-font-family);font-size:var(--gl-font-size-base);line-height:1.5;resize:vertical;transition:var(--gl-transition);outline:0;box-shadow:var(--gl-inset-subtle)}.glass-textarea::placeholder{color:var(--gl-placeholder)}.glass-textarea:focus{border-color:var(--gl-border-focus);background:var(--gl-surface-3);box-shadow:var(--gl-shadow-focus),var(--gl-inset-medium)}.glass-select{width:100%;height:52px;padding:0 var(--gl-space-3xl) 0 var(--gl-space-md);background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-light));-webkit-backdrop-filter:blur(var(--gl-blur-light));border:1.5px solid var(--gl-border-medium);border-radius:var(--gl-radius-input);color:var(--gl-color-text);font-family:var(--gl-font-family);font-size:var(--gl-font-size-base);transition:var(--gl-transition);outline:0;box-shadow:var(--gl-inset-subtle);cursor:pointer;appearance:none;-webkit-appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.6)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 16px center}.glass-select:focus{border-color:var(--gl-border-focus);box-shadow:var(--gl-shadow-focus),var(--gl-inset-medium)}.glass-select option{background:var(--gl-color-bg-mid);color:var(--gl-color-text)}.glass-search{position:relative;display:flex;align-items:center}.glass-search__icon{position:absolute;left:var(--gl-space-md);width:18px;height:18px;stroke:var(--gl-icon-muted)!important;stroke-width:2!important;stroke-linecap:round!important;stroke-linejoin:round!important;fill:none!important;pointer-events:none;z-index:2}.glass-search .glass-input{padding-left:44px;border-radius:var(--gl-radius-full)}.glass-toggle{display:inline-flex;align-items:center;gap:var(--gl-space-sm);cursor:pointer;-webkit-tap-highlight-color:transparent}.glass-toggle__input{position:absolute;opacity:0;width:0;height:0}.glass-toggle__track{position:relative;width:52px;height:30px;background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-soft));-webkit-backdrop-filter:blur(var(--gl-blur-soft));border:1.5px solid var(--gl-border-medium);border-radius:var(--gl-radius-full);transition:var(--gl-transition);box-shadow:var(--gl-inset-subtle);flex-shrink:0}.glass-toggle__thumb{position:absolute;top:3px;left:3px;width:22px;height:22px;border-radius:var(--gl-radius-pill);background:var(--gl-toggle-thumb-bg);box-shadow:0 2px 6px rgba(0,0,0,.2),var(--gl-inset-milk);transition:var(--gl-transition)}.glass-toggle__input:checked+.glass-toggle__track{background:linear-gradient(135deg,var(--gl-color-primary) 0,var(--gl-color-primary-dark) 100%);border-color:var(--gl-border-warm)}.glass-toggle__input:checked+.glass-toggle__track .glass-toggle__thumb{transform:translateX(22px)}.glass-toggle__input:focus-visible+.glass-toggle__track{box-shadow:var(--gl-shadow-focus)}.glass-toggle__label{font-size:var(--gl-font-size-base);color:var(--gl-color-text);user-select:none}.glass-checkbox{display:inline-flex;align-items:center;gap:var(--gl-space-sm);cursor:pointer;-webkit-tap-highlight-color:transparent}.glass-checkbox__input{position:absolute;opacity:0;width:0;height:0}.glass-checkbox__box{width:24px;height:24px;border-radius:var(--gl-radius-xs);background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-soft));-webkit-backdrop-filter:blur(var(--gl-blur-soft));border:1.5px solid var(--gl-border-medium);display:flex;align-items:center;justify-content:center;transition:var(--gl-transition);box-shadow:var(--gl-inset-subtle);flex-shrink:0}.glass-checkbox__box svg{width:14px;height:14px;stroke:white;stroke-width:3;fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:0;transform:scale(.5);transition:var(--gl-transition-fast)}.glass-checkbox__input:checked+.glass-checkbox__box{background:linear-gradient(135deg,var(--gl-color-primary) 0,var(--gl-color-primary-dark) 100%);border-color:var(--gl-border-warm)}.glass-checkbox__input:checked+.glass-checkbox__box svg{opacity:1;transform:scale(1)}.glass-checkbox__input:focus-visible+.glass-checkbox__box{box-shadow:var(--gl-shadow-focus)}.glass-checkbox__label{font-size:var(--gl-font-size-base);color:var(--gl-color-text);user-select:none}.glass-radio{display:inline-flex;align-items:center;gap:var(--gl-space-sm);cursor:pointer;-webkit-tap-highlight-color:transparent}.glass-radio__input{position:absolute;opacity:0;width:0;height:0}.glass-radio__circle{width:24px;height:24px;border-radius:var(--gl-radius-pill);background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-soft));border:1.5px solid var(--gl-border-medium);display:flex;align-items:center;justify-content:center;transition:var(--gl-transition);box-shadow:var(--gl-inset-subtle);flex-shrink:0}.glass-radio__dot{width:10px;height:10px;border-radius:var(--gl-radius-pill);background:#fff;opacity:0;transform:scale(0);transition:var(--gl-transition-fast)}.glass-radio__input:checked+.glass-radio__circle{background:linear-gradient(135deg,var(--gl-color-primary) 0,var(--gl-color-primary-dark) 100%);border-color:var(--gl-border-warm)}.glass-radio__input:checked+.glass-radio__circle .glass-radio__dot{opacity:1;transform:scale(1)}.glass-radio__input:focus-visible+.glass-radio__circle{box-shadow:var(--gl-shadow-focus)}.glass-radio__label{font-size:var(--gl-font-size-base);color:var(--gl-color-text);user-select:none}.glass-range-group{display:flex;flex-direction:column;gap:var(--gl-space-xs)}.glass-range-header{display:flex;justify-content:space-between;align-items:baseline}.glass-range-value{font-size:var(--gl-font-size-sm);font-weight:var(--gl-font-weight-semibold);color:var(--gl-color-primary);font-variant-numeric:tabular-nums}.glass-range{-webkit-appearance:none;appearance:none;width:100%;height:6px;border-radius:var(--gl-radius-full);background:var(--gl-range-track-bg);border:1px solid var(--gl-range-track-border);outline:0;cursor:pointer}.glass-range::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:26px;height:26px;border-radius:var(--gl-radius-pill);background:linear-gradient(135deg,var(--gl-color-primary) 0,var(--gl-color-primary-dark) 100%);border:2px solid rgba(255,255,255,.5);box-shadow:0 2px 8px rgba(230,130,40,.35),var(--gl-inset-strong);margin-top:-10px;cursor:pointer;transition:var(--gl-transition)}.glass-range::-webkit-slider-thumb:hover{transform:scale(1.1)}.glass-range::-moz-range-thumb{width:26px;height:26px;border-radius:var(--gl-radius-pill);background:linear-gradient(135deg,var(--gl-color-primary) 0,var(--gl-color-primary-dark) 100%);border:2px solid rgba(255,255,255,.5);box-shadow:0 2px 8px rgba(230,130,40,.35);cursor:pointer}.glass-range::-moz-range-track{height:6px;border-radius:var(--gl-radius-full);background:var(--gl-range-track-bg);border:1px solid var(--gl-range-track-border)}.glass-range:focus-visible{box-shadow:var(--gl-shadow-focus)}.glass-progress{width:100%;display:flex;flex-direction:column;gap:var(--gl-space-xs)}.glass-progress__header{display:flex;justify-content:space-between;align-items:baseline}.glass-progress__label{font-size:var(--gl-font-size-sm);font-weight:var(--gl-font-weight-medium);color:var(--gl-icon-strong)}.glass-progress__value{font-size:var(--gl-font-size-xs);font-weight:var(--gl-font-weight-semibold);color:var(--gl-color-text-muted);font-variant-numeric:tabular-nums}.glass-progress__track{width:100%;height:8px;border-radius:var(--gl-radius-full);background:var(--gl-surface-2);border:1px solid var(--gl-border-subtle);overflow:hidden;box-shadow:var(--gl-inset-subtle)}.glass-progress__fill{height:100%;border-radius:var(--gl-radius-full);background:linear-gradient(90deg,var(--gl-color-primary) 0,var(--gl-color-primary-dark) 100%);box-shadow:0 0 8px rgba(245,166,35,.3),var(--gl-inset-strong);transition:width .5s cubic-bezier(.4, 0, .2, 1);position:relative}.glass-progress__fill::after{content:'';position:absolute;top:0;left:0;right:0;height:50%;border-radius:var(--gl-radius-full);background:linear-gradient(to bottom,rgba(255,255,255,.3) 0,transparent 100%)}.glass-progress--sm .glass-progress__track{height:4px}.glass-progress--lg .glass-progress__track{height:12px}.glass-progress--success .glass-progress__fill{background:linear-gradient(90deg,#34c759 0,#2da44e 100%);box-shadow:0 0 8px rgba(52,199,89,.3),var(--gl-inset-strong)}.glass-progress--error .glass-progress__fill{background:linear-gradient(90deg,#ff3b30 0,#d63027 100%);box-shadow:0 0 8px rgba(255,59,48,.3),var(--gl-inset-strong)}.glass-modal-overlay{position:fixed;inset:0;background:var(--gl-surface-overlay);backdrop-filter:blur(var(--gl-blur-soft));-webkit-backdrop-filter:blur(var(--gl-blur-soft));display:flex;align-items:center;justify-content:center;padding:var(--gl-space-lg);z-index:1000;opacity:0;pointer-events:none;transition:var(--gl-transition-modal);font-family:var(--gl-font-family);-webkit-font-smoothing:antialiased}.glass-modal-overlay.is-active{opacity:1;pointer-events:auto}.glass-modal{width:100%;max-width:340px;background:linear-gradient(to bottom,var(--gl-card-glow-top) 0,var(--gl-card-glow-bottom) 100%);backdrop-filter:blur(var(--gl-blur-heavy));-webkit-backdrop-filter:blur(var(--gl-blur-heavy));border:1.5px solid var(--gl-border-medium);border-radius:var(--gl-radius-card);overflow:hidden;box-shadow:var(--gl-shadow-modal);transform:scale(.9) translateY(20px);transition:var(--gl-transition-modal);position:relative}.glass-modal-overlay.is-active .glass-modal{transform:scale(1) translateY(0)}.glass-modal::before{content:'';position:absolute;top:0;left:10%;right:10%;height:1px;background:linear-gradient(90deg,transparent 0,rgba(255,255,255,.4) 30%,rgba(255,255,255,.5) 50%,rgba(255,255,255,.4) 70%,transparent 100%)}.glass-modal__header{padding:var(--gl-space-xl) var(--gl-space-xl) var(--gl-space-sm);text-align:center}.glass-modal__title{font-size:var(--gl-font-size-modal);font-weight:var(--gl-font-weight-semibold);color:var(--gl-color-text)}.glass-modal__body{padding:var(--gl-space-sm) var(--gl-space-xl) var(--gl-space-xl);text-align:center}.glass-modal__body p{color:var(--gl-color-text-muted);font-size:var(--gl-font-size-base);line-height:1.55}.glass-modal__footer{display:flex;border-top:1px solid var(--gl-border-subtle)}.glass-modal__action{flex:1;height:52px;background:0 0;border:none;font-family:var(--gl-font-family);font-size:var(--gl-font-size-btn);font-weight:var(--gl-font-weight-medium);color:var(--gl-color-text-muted);cursor:pointer;transition:var(--gl-transition-fast);outline:0}.glass-modal__action:hover{background:var(--gl-surface-1)}.glass-modal__action+.glass-modal__action{border-left:1px solid var(--gl-border-subtle)}.glass-modal__action--primary{color:var(--gl-color-primary);font-weight:var(--gl-font-weight-semibold)}.glass-modal__action--danger{color:var(--gl-color-error);font-weight:var(--gl-font-weight-semibold)}.glass-toast{position:fixed;top:var(--gl-space-4xl);left:50%;transform:translateX(-50%) translateY(-20px);min-width:280px;max-width:340px;padding:var(--gl-space-md) var(--gl-space-lg);background:linear-gradient(to bottom,var(--gl-surface-4) 0,var(--gl-surface-2) 100%);backdrop-filter:blur(var(--gl-blur));-webkit-backdrop-filter:blur(var(--gl-blur));border:1px solid var(--gl-border-medium);border-radius:var(--gl-radius-btn);display:flex;align-items:center;gap:var(--gl-space-sm);box-shadow:var(--gl-shadow-toast),var(--gl-inset-medium);z-index:1100;opacity:0;pointer-events:none;transition:var(--gl-transition-modal);font-family:var(--gl-font-family);-webkit-font-smoothing:antialiased}.glass-toast.is-visible{opacity:1;transform:translateX(-50%) translateY(0);pointer-events:auto}.glass-toast__icon{width:22px;height:22px;flex-shrink:0;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}.glass-toast--success .glass-toast__icon{stroke:var(--gl-color-success)}.glass-toast--error .glass-toast__icon{stroke:var(--gl-color-error)}.glass-toast--warning .glass-toast__icon{stroke:var(--gl-color-warning)}.glass-toast__text{font-size:var(--gl-font-size-sm);font-weight:var(--gl-font-weight-medium);color:var(--gl-color-text);line-height:1.4}.glass-tab-bar{position:fixed;bottom:0;left:0;right:0;height:var(--gl-tab-bar-height);background:linear-gradient(to bottom,var(--gl-surface-3) 0,var(--gl-surface-1) 100%);backdrop-filter:blur(var(--gl-blur));-webkit-backdrop-filter:blur(var(--gl-blur));border-top:1px solid var(--gl-border-subtle);display:flex;justify-content:space-around;align-items:flex-start;padding-top:var(--gl-space-xs);padding-bottom:env(safe-area-inset-bottom,8px);z-index:900;box-shadow:0 -4px 20px rgba(0,0,0,.08),var(--gl-inset-subtle)}.glass-tab-bar__item{display:flex;flex-direction:column;align-items:center;gap:var(--gl-space-2xs);padding:var(--gl-space-2xs) var(--gl-space-sm);background:0 0;border:none;cursor:pointer;transition:var(--gl-transition);outline:0;-webkit-tap-highlight-color:transparent;min-width:60px}.glass-tab-bar__icon{width:24px;height:24px;display:flex;align-items:center;justify-content:center;position:relative}.glass-tab-bar__icon svg{width:24px;height:24px;fill:none;stroke:var(--gl-icon-muted);stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;transition:var(--gl-transition)}.glass-tab-bar__label{font-size:10px;font-weight:var(--gl-font-weight-medium);color:var(--gl-icon-muted);transition:var(--gl-transition);letter-spacing:.2px}.glass-tab-bar__item.is-active .glass-tab-bar__icon svg{stroke:var(--gl-color-primary);filter:drop-shadow(0 0 6px rgba(245,166,35,.35))}.glass-tab-bar__item.is-active .glass-tab-bar__label{color:var(--gl-color-primary)}.glass-tab-bar__item.is-active .glass-tab-bar__icon::after{content:'';position:absolute;bottom:-6px;left:50%;transform:translateX(-50%);width:4px;height:4px;border-radius:var(--gl-radius-pill);background:var(--gl-color-primary);box-shadow:0 0 6px rgba(245,166,35,.5)}.glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__icon svg{stroke:var(--gl-icon-default)}.glass-tab-bar__item:not(.is-active):hover .glass-tab-bar__label{color:var(--gl-icon-default)}.glass-tab-bar__badge{position:absolute;top:-4px;right:-8px;min-width:16px;height:16px;padding:0 4px;border-radius:var(--gl-radius-full);background:var(--gl-color-error);color:#fff;font-size:10px;font-weight:var(--gl-font-weight-semibold);display:flex;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(255,59,48,.4)}.glass-accordion{display:flex;flex-direction:column;gap:var(--gl-space-xs)}.glass-accordion__item{background:var(--gl-surface-2);backdrop-filter:blur(var(--gl-blur-light));-webkit-backdrop-filter:blur(var(--gl-blur-light));border:1px solid var(--gl-border-subtle);border-radius:var(--gl-radius-btn);overflow:hidden;transition:var(--gl-transition);box-shadow:var(--gl-inset-subtle)}.glass-accordion__item.is-open{background:var(--gl-surface-3);border-color:var(--gl-border-medium);box-shadow:0 4px 16px rgba(0,0,0,.1),var(--gl-inset-medium)}.glass-accordion__trigger{width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--gl-space-sm);padding:var(--gl-space-md) var(--gl-space-lg);background:0 0;border:none;cursor:pointer;font-family:var(--gl-font-family);font-size:var(--gl-font-size-base);font-weight:var(--gl-font-weight-semibold);color:var(--gl-color-text);text-align:left;outline:0;transition:var(--gl-transition);-webkit-tap-highlight-color:transparent}.glass-accordion__trigger:hover{color:var(--gl-color-primary)}.glass-accordion__trigger-icon{display:flex;align-items:center;justify-content:center;flex-shrink:0}.glass-accordion__trigger-icon svg{width:18px;height:18px;fill:none;stroke:var(--gl-icon-default);stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .3s cubic-bezier(.4, 0, .2, 1)}.glass-accordion__item.is-open .glass-accordion__trigger-icon svg{transform:rotate(180deg);stroke:var(--gl-color-primary)}.glass-accordion__content{max-height:0;overflow:hidden;transition:max-height .35s cubic-bezier(.4, 0, .2, 1)}.glass-accordion__item.is-open .glass-accordion__content{max-height:500px}.glass-accordion__body{padding:0 var(--gl-space-lg) var(--gl-space-lg);color:var(--gl-accordion-body-color);font-size:var(--gl-font-size-sm);line-height:1.6}.glass-badge{display:inline-flex;align-items:center;gap:var(--gl-space-2xs);padding:var(--gl-space-2xs) var(--gl-space-sm);background:var(--gl-surface-3);backdrop-filter:blur(var(--gl-blur-soft));-webkit-backdrop-filter:blur(var(--gl-blur-soft));border:1px solid var(--gl-border-subtle);border-radius:var(--gl-radius-full);font-size:var(--gl-font-size-xs);font-weight:var(--gl-font-weight-medium);color:var(--gl-color-text);box-shadow:var(--gl-inset-subtle)}.glass-badge--primary{background:linear-gradient(135deg,rgba(245,166,35,.25) 0,rgba(212,105,42,.2) 100%);border-color:var(--gl-border-warm);color:var(--gl-color-primary)}.glass-badge--success{background:rgba(52,199,89,.15);border-color:rgba(52,199,89,.3);color:var(--gl-color-success)}.glass-badge--error{background:rgba(255,59,48,.15);border-color:rgba(255,59,48,.3);color:var(--gl-color-error)}.glass-divider{height:1px;border:none;background:linear-gradient(90deg,transparent 0,var(--gl-border-subtle) 20%,var(--gl-border-medium) 50%,var(--gl-border-subtle) 80%,transparent 100%)}.glass-avatar{width:44px;height:44px;border-radius:var(--gl-radius-pill);background:var(--gl-surface-3);backdrop-filter:blur(var(--gl-blur-soft));-webkit-backdrop-filter:blur(var(--gl-blur-soft));border:1.5px solid var(--gl-border-strong);display:flex;align-items:center;justify-content:center;overflow:hidden;box-shadow:var(--gl-shadow-glow),var(--gl-inset-medium);flex-shrink:0;font-size:var(--gl-font-size-btn);font-weight:var(--gl-font-weight-semibold);color:var(--gl-color-text)}.glass-avatar img{width:100%;height:100%;object-fit:cover}.glass-avatar--sm{width:32px;height:32px;font-size:var(--gl-font-size-xs)}.glass-avatar--lg{width:56px;height:56px;font-size:var(--gl-font-size-lg)}.glass-theme-toggle{width:46px;height:46px;border-radius:var(--gl-radius-pill);background:var(--gl-surface-3);backdrop-filter:blur(var(--gl-blur));-webkit-backdrop-filter:blur(var(--gl-blur));border:1.5px solid var(--gl-border-strong);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:var(--gl-transition);box-shadow:var(--gl-shadow-glow),var(--gl-inset-medium);outline:0;-webkit-tap-highlight-color:transparent}.glass-theme-toggle:hover{background:var(--gl-surface-5);transform:scale(1.05)}.glass-theme-toggle:active{transform:scale(.95)}.glass-theme-toggle svg{width:20px;height:20px;fill:none;stroke:var(--gl-color-text);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.glass-theme-toggle .icon-sun{display:none}.glass-theme-toggle .icon-moon,[data-theme=light] .glass-theme-toggle .icon-sun{display:block}[data-theme=light] .glass-theme-toggle .icon-moon{display:none}.gl-stack{display:flex;flex-direction:column}.gl-row{display:flex;flex-direction:row;align-items:center}.gl-stack--2xs{gap:var(--gl-space-2xs)}.gl-stack--xs{gap:var(--gl-space-xs)}.gl-stack--sm{gap:var(--gl-space-sm)}.gl-stack--md{gap:var(--gl-space-md)}.gl-stack--lg{gap:var(--gl-space-lg)}.gl-stack--xl{gap:var(--gl-space-xl)}.gl-row--xs{gap:var(--gl-space-xs)}.gl-row--sm{gap:var(--gl-space-sm)}.gl-row--md{gap:var(--gl-space-md)}.gl-text-center{text-align:center}.gl-text-muted{color:var(--gl-color-text-muted)}.gl-text-sm{font-size:var(--gl-font-size-sm)}.gl-mt-sm{margin-top:var(--gl-space-sm)}.gl-mt-md{margin-top:var(--gl-space-md)}.gl-mt-lg{margin-top:var(--gl-space-lg)}.gl-mt-xl{margin-top:var(--gl-space-xl)}.gl-mb-sm{margin-bottom:var(--gl-space-sm)}.gl-mb-md{margin-bottom:var(--gl-space-md)}.gl-mb-lg{margin-bottom:var(--gl-space-lg)}.gl-mb-xl{margin-bottom:var(--gl-space-xl)}.gl-px{padding-left:var(--gl-space-lg);padding-right:var(--gl-space-lg)}.gl-w-full{width:100%}.gl-flex-1{flex:1}\n/*# sourceMappingURL=glasskit.min.css.map */";
|
|
5
|
+
|
|
6
|
+
const glassSheet = new CSSStyleSheet();
|
|
7
|
+
glassSheet.replaceSync(css);
|
|
8
|
+
|
|
9
|
+
// ── Global Theme Sync ──
|
|
10
|
+
// Single MutationObserver that watches data-theme on <html>
|
|
11
|
+
// and notifies all GlkElement instances.
|
|
12
|
+
|
|
13
|
+
const instances = new Set();
|
|
14
|
+
|
|
15
|
+
function getCurrentTheme() {
|
|
16
|
+
return document.documentElement.getAttribute('data-theme') || 'dark';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function syncAllThemes() {
|
|
20
|
+
const theme = getCurrentTheme();
|
|
21
|
+
for (const instance of instances) {
|
|
22
|
+
instance._syncTheme(theme);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof window !== 'undefined' && typeof MutationObserver !== 'undefined') {
|
|
27
|
+
const observer = new MutationObserver(syncAllThemes);
|
|
28
|
+
observer.observe(document.documentElement, {
|
|
29
|
+
attributes: true,
|
|
30
|
+
attributeFilter: ['data-theme']
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ── Host Stylesheet ──
|
|
35
|
+
// Sets display:block on all custom elements by default.
|
|
36
|
+
// Inline components (badge, avatar) override this.
|
|
37
|
+
|
|
38
|
+
const hostSheet = new CSSStyleSheet();
|
|
39
|
+
hostSheet.replaceSync(`
|
|
40
|
+
:host { display: block; }
|
|
41
|
+
:host([hidden]) { display: none; }
|
|
42
|
+
.glk-wrapper { display: contents; }
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
const inlineHostSheet = new CSSStyleSheet();
|
|
46
|
+
inlineHostSheet.replaceSync(`
|
|
47
|
+
:host { display: inline-block; }
|
|
48
|
+
:host([hidden]) { display: none; }
|
|
49
|
+
.glk-wrapper { display: contents; }
|
|
50
|
+
`);
|
|
51
|
+
|
|
52
|
+
// ── Base Class ──
|
|
53
|
+
|
|
54
|
+
class GlkElement extends HTMLElement {
|
|
55
|
+
|
|
56
|
+
/** Override in subclass to use inline-block display */
|
|
57
|
+
static get displayInline() { return false; }
|
|
58
|
+
|
|
59
|
+
static get observedAttributes() {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
constructor() {
|
|
64
|
+
super();
|
|
65
|
+
this._initialized = false;
|
|
66
|
+
this._shadow = this.attachShadow({ mode: 'open' });
|
|
67
|
+
const displaySheet = this.constructor.displayInline ? inlineHostSheet : hostSheet;
|
|
68
|
+
this._shadow.adoptedStyleSheets = [glassSheet, displaySheet];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
connectedCallback() {
|
|
72
|
+
if (!this._initialized) {
|
|
73
|
+
this._initialized = true;
|
|
74
|
+
|
|
75
|
+
// Create theme wrapper (display:contents makes it layout-transparent)
|
|
76
|
+
this._wrapper = document.createElement('div');
|
|
77
|
+
this._wrapper.className = 'glk-wrapper';
|
|
78
|
+
this._wrapper.setAttribute('data-theme', getCurrentTheme());
|
|
79
|
+
this._shadow.appendChild(this._wrapper);
|
|
80
|
+
|
|
81
|
+
this.render();
|
|
82
|
+
this.setupEvents();
|
|
83
|
+
|
|
84
|
+
// Register for theme sync
|
|
85
|
+
instances.add(this);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
disconnectedCallback() {
|
|
90
|
+
instances.delete(this);
|
|
91
|
+
this.teardownEvents();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
95
|
+
if (!this._initialized) return;
|
|
96
|
+
if (oldValue === newValue) return;
|
|
97
|
+
this.onAttributeChanged(name, oldValue, newValue);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_syncTheme(theme) {
|
|
101
|
+
if (this._wrapper) {
|
|
102
|
+
this._wrapper.setAttribute('data-theme', theme);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Subclasses override to build inner DOM inside this._wrapper. */
|
|
107
|
+
render() {}
|
|
108
|
+
|
|
109
|
+
/** Subclasses override to attach event listeners. */
|
|
110
|
+
setupEvents() {}
|
|
111
|
+
|
|
112
|
+
/** Subclasses override to remove event listeners. */
|
|
113
|
+
teardownEvents() {}
|
|
114
|
+
|
|
115
|
+
/** Subclasses override to react to attribute changes. */
|
|
116
|
+
onAttributeChanged(name, oldValue, newValue) {}
|
|
117
|
+
|
|
118
|
+
// ── Utility Methods ──
|
|
119
|
+
|
|
120
|
+
getBoolAttr(name) {
|
|
121
|
+
return this.hasAttribute(name);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
setBoolAttr(name, value) {
|
|
125
|
+
if (value) {
|
|
126
|
+
this.setAttribute(name, '');
|
|
127
|
+
} else {
|
|
128
|
+
this.removeAttribute(name);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
createElement(tag, classes = [], attrs = {}) {
|
|
133
|
+
const el = document.createElement(tag);
|
|
134
|
+
if (classes.length) el.classList.add(...classes);
|
|
135
|
+
for (const [key, val] of Object.entries(attrs)) {
|
|
136
|
+
el.setAttribute(key, val);
|
|
137
|
+
}
|
|
138
|
+
return el;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
emit(eventName, detail = null) {
|
|
142
|
+
this.dispatchEvent(new CustomEvent(eventName, {
|
|
143
|
+
bubbles: true,
|
|
144
|
+
composed: true,
|
|
145
|
+
detail
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── Form-Associated Base Class ──
|
|
151
|
+
|
|
152
|
+
class GlkFormElement extends GlkElement {
|
|
153
|
+
|
|
154
|
+
static formAssociated = true;
|
|
155
|
+
|
|
156
|
+
constructor() {
|
|
157
|
+
super();
|
|
158
|
+
this._internals = this.attachInternals();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
get form() { return this._internals.form; }
|
|
162
|
+
get validationMessage() { return this._internals.validationMessage; }
|
|
163
|
+
get validity() { return this._internals.validity; }
|
|
164
|
+
|
|
165
|
+
checkValidity() { return this._internals.checkValidity(); }
|
|
166
|
+
reportValidity() { return this._internals.reportValidity(); }
|
|
167
|
+
|
|
168
|
+
formResetCallback() {
|
|
169
|
+
this.resetValue();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
formStateRestoreCallback(state, mode) {
|
|
173
|
+
this.restoreValue(state);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Subclasses override. */
|
|
177
|
+
resetValue() {}
|
|
178
|
+
restoreValue(state) {}
|
|
179
|
+
|
|
180
|
+
setFormValue(value) {
|
|
181
|
+
this._internals.setFormValue(value);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
setValidity(flags, message, anchor) {
|
|
185
|
+
this._internals.setValidity(flags, message, anchor);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
class GlkNav extends GlkElement {
|
|
190
|
+
render() {
|
|
191
|
+
const nav = this.createElement('nav', ['glass-nav']);
|
|
192
|
+
nav.appendChild(document.createElement('slot'));
|
|
193
|
+
this._wrapper.appendChild(nav);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
customElements.define('glk-nav', GlkNav);
|
|
198
|
+
|
|
199
|
+
class GlkPill extends GlkElement {
|
|
200
|
+
static get observedAttributes() {
|
|
201
|
+
return ['label', 'disabled'];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
render() {
|
|
205
|
+
this._btn = this.createElement('button', ['glass-pill']);
|
|
206
|
+
|
|
207
|
+
if (this.getBoolAttr('disabled')) this._btn.disabled = true;
|
|
208
|
+
|
|
209
|
+
const ariaLabel = this.getAttribute('label');
|
|
210
|
+
if (ariaLabel) this._btn.setAttribute('aria-label', ariaLabel);
|
|
211
|
+
|
|
212
|
+
// Slotted content (SVG icons, text)
|
|
213
|
+
this._btn.appendChild(document.createElement('slot'));
|
|
214
|
+
this._wrapper.appendChild(this._btn);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
setupEvents() {
|
|
218
|
+
this._onClick = () => {
|
|
219
|
+
if (!this.getBoolAttr('disabled')) {
|
|
220
|
+
this.emit('glk-click');
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
this._btn.addEventListener('click', this._onClick);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
teardownEvents() {
|
|
227
|
+
this._btn?.removeEventListener('click', this._onClick);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
onAttributeChanged(name) {
|
|
231
|
+
if (!this._btn) return;
|
|
232
|
+
switch (name) {
|
|
233
|
+
case 'label':
|
|
234
|
+
this._btn.setAttribute('aria-label', this.getAttribute('label') || '');
|
|
235
|
+
break;
|
|
236
|
+
case 'disabled':
|
|
237
|
+
this._btn.disabled = this.getBoolAttr('disabled');
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
customElements.define('glk-pill', GlkPill);
|
|
244
|
+
|
|
245
|
+
class GlkTabBar extends GlkElement {
|
|
246
|
+
render() {
|
|
247
|
+
const nav = this.createElement('nav', ['glass-tab-bar']);
|
|
248
|
+
nav.appendChild(document.createElement('slot'));
|
|
249
|
+
this._wrapper.appendChild(nav);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setupEvents() {
|
|
253
|
+
// Listen for tab selection events from child glk-tab-item elements
|
|
254
|
+
this._onTabSelect = (e) => {
|
|
255
|
+
// Deactivate all other tabs
|
|
256
|
+
const items = this.querySelectorAll('glk-tab-item');
|
|
257
|
+
items.forEach(item => {
|
|
258
|
+
if (item !== e.target) {
|
|
259
|
+
item.removeAttribute('active');
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
this.emit('glk-tab-change', { tab: e.target });
|
|
263
|
+
};
|
|
264
|
+
this.addEventListener('glk-tab-select', this._onTabSelect);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
teardownEvents() {
|
|
268
|
+
this.removeEventListener('glk-tab-select', this._onTabSelect);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
customElements.define('glk-tab-bar', GlkTabBar);
|
|
273
|
+
|
|
274
|
+
class GlkTabItem extends GlkElement {
|
|
275
|
+
static get observedAttributes() {
|
|
276
|
+
return ['label', 'active', 'badge'];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
render() {
|
|
280
|
+
this._btn = this.createElement('button', ['glass-tab-bar__item']);
|
|
281
|
+
if (this.getBoolAttr('active')) this._btn.classList.add('is-active');
|
|
282
|
+
|
|
283
|
+
// Icon slot
|
|
284
|
+
this._iconEl = this.createElement('span', ['glass-tab-bar__icon']);
|
|
285
|
+
this._iconEl.appendChild(document.createElement('slot'));
|
|
286
|
+
|
|
287
|
+
// Label
|
|
288
|
+
this._labelEl = this.createElement('span', ['glass-tab-bar__label']);
|
|
289
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
290
|
+
|
|
291
|
+
this._btn.appendChild(this._iconEl);
|
|
292
|
+
this._btn.appendChild(this._labelEl);
|
|
293
|
+
|
|
294
|
+
// Badge (optional)
|
|
295
|
+
const badge = this.getAttribute('badge');
|
|
296
|
+
if (badge) {
|
|
297
|
+
this._badgeEl = this.createElement('span', ['glass-tab-bar__badge']);
|
|
298
|
+
this._badgeEl.textContent = badge;
|
|
299
|
+
this._btn.appendChild(this._badgeEl);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this._wrapper.appendChild(this._btn);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
setupEvents() {
|
|
306
|
+
this._onClick = () => {
|
|
307
|
+
this.setAttribute('active', '');
|
|
308
|
+
this.emit('glk-tab-select');
|
|
309
|
+
};
|
|
310
|
+
this._btn.addEventListener('click', this._onClick);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
teardownEvents() {
|
|
314
|
+
this._btn?.removeEventListener('click', this._onClick);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
onAttributeChanged(name) {
|
|
318
|
+
if (!this._btn) return;
|
|
319
|
+
switch (name) {
|
|
320
|
+
case 'active':
|
|
321
|
+
this._btn.classList.toggle('is-active', this.getBoolAttr('active'));
|
|
322
|
+
break;
|
|
323
|
+
case 'label':
|
|
324
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
325
|
+
break;
|
|
326
|
+
case 'badge': {
|
|
327
|
+
const badge = this.getAttribute('badge');
|
|
328
|
+
if (badge) {
|
|
329
|
+
if (!this._badgeEl) {
|
|
330
|
+
this._badgeEl = this.createElement('span', ['glass-tab-bar__badge']);
|
|
331
|
+
this._btn.appendChild(this._badgeEl);
|
|
332
|
+
}
|
|
333
|
+
this._badgeEl.textContent = badge;
|
|
334
|
+
} else if (this._badgeEl) {
|
|
335
|
+
this._badgeEl.remove();
|
|
336
|
+
this._badgeEl = null;
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
customElements.define('glk-tab-item', GlkTabItem);
|
|
345
|
+
|
|
346
|
+
class GlkCard extends GlkElement {
|
|
347
|
+
static get observedAttributes() {
|
|
348
|
+
return ['glow'];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
render() {
|
|
352
|
+
this._card = this.createElement('div', this._computeClasses());
|
|
353
|
+
this._card.appendChild(document.createElement('slot'));
|
|
354
|
+
this._wrapper.appendChild(this._card);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
onAttributeChanged(name) {
|
|
358
|
+
if (name === 'glow' && this._card) {
|
|
359
|
+
this._card.className = this._computeClasses().join(' ');
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
_computeClasses() {
|
|
364
|
+
const classes = ['glass-card'];
|
|
365
|
+
if (this.getBoolAttr('glow')) {
|
|
366
|
+
classes.push('glass-card--glow');
|
|
367
|
+
}
|
|
368
|
+
return classes;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
get glow() { return this.getBoolAttr('glow'); }
|
|
372
|
+
set glow(v) { this.setBoolAttr('glow', v); }
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
customElements.define('glk-card', GlkCard);
|
|
376
|
+
|
|
377
|
+
const VARIANTS$3 = ['primary', 'success', 'error'];
|
|
378
|
+
|
|
379
|
+
class GlkBadge extends GlkElement {
|
|
380
|
+
static get displayInline() { return true; }
|
|
381
|
+
static get observedAttributes() {
|
|
382
|
+
return ['variant'];
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
render() {
|
|
386
|
+
this._badge = this.createElement('span', this._computeClasses());
|
|
387
|
+
this._badge.appendChild(document.createElement('slot'));
|
|
388
|
+
this._wrapper.appendChild(this._badge);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
onAttributeChanged(name) {
|
|
392
|
+
if (name === 'variant' && this._badge) {
|
|
393
|
+
this._badge.className = this._computeClasses().join(' ');
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
_computeClasses() {
|
|
398
|
+
const classes = ['glass-badge'];
|
|
399
|
+
const variant = this.getAttribute('variant');
|
|
400
|
+
if (variant && VARIANTS$3.includes(variant)) {
|
|
401
|
+
classes.push(`glass-badge--${variant}`);
|
|
402
|
+
}
|
|
403
|
+
return classes;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
get variant() { return this.getAttribute('variant'); }
|
|
407
|
+
set variant(v) { this.setAttribute('variant', v); }
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
customElements.define('glk-badge', GlkBadge);
|
|
411
|
+
|
|
412
|
+
const SIZES$2 = ['sm', 'lg'];
|
|
413
|
+
|
|
414
|
+
class GlkAvatar extends GlkElement {
|
|
415
|
+
static get displayInline() { return true; }
|
|
416
|
+
static get observedAttributes() {
|
|
417
|
+
return ['size', 'src', 'alt'];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
render() {
|
|
421
|
+
this._avatar = this.createElement('div', this._computeClasses());
|
|
422
|
+
this._updateContent();
|
|
423
|
+
this._wrapper.appendChild(this._avatar);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
onAttributeChanged(name) {
|
|
427
|
+
if (!this._avatar) return;
|
|
428
|
+
if (name === 'size') {
|
|
429
|
+
this._avatar.className = this._computeClasses().join(' ');
|
|
430
|
+
} else if (name === 'src' || name === 'alt') {
|
|
431
|
+
this._updateContent();
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
_updateContent() {
|
|
436
|
+
const src = this.getAttribute('src');
|
|
437
|
+
this._avatar.innerHTML = '';
|
|
438
|
+
if (src) {
|
|
439
|
+
const img = this.createElement('img', [], {
|
|
440
|
+
src,
|
|
441
|
+
alt: this.getAttribute('alt') || ''
|
|
442
|
+
});
|
|
443
|
+
this._avatar.appendChild(img);
|
|
444
|
+
} else {
|
|
445
|
+
// Use slotted text content (initials)
|
|
446
|
+
this._avatar.appendChild(document.createElement('slot'));
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
_computeClasses() {
|
|
451
|
+
const classes = ['glass-avatar'];
|
|
452
|
+
const size = this.getAttribute('size');
|
|
453
|
+
if (size && SIZES$2.includes(size)) {
|
|
454
|
+
classes.push(`glass-avatar--${size}`);
|
|
455
|
+
}
|
|
456
|
+
return classes;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
get size() { return this.getAttribute('size'); }
|
|
460
|
+
set size(v) { this.setAttribute('size', v); }
|
|
461
|
+
|
|
462
|
+
get src() { return this.getAttribute('src'); }
|
|
463
|
+
set src(v) { this.setAttribute('src', v); }
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
customElements.define('glk-avatar', GlkAvatar);
|
|
467
|
+
|
|
468
|
+
class GlkTitle extends GlkElement {
|
|
469
|
+
static get observedAttributes() {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
render() {
|
|
474
|
+
const el = this.createElement('div', ['glass-title']);
|
|
475
|
+
el.appendChild(document.createElement('slot'));
|
|
476
|
+
this._wrapper.appendChild(el);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
customElements.define('glk-title', GlkTitle);
|
|
481
|
+
|
|
482
|
+
class GlkDivider extends GlkElement {
|
|
483
|
+
render() {
|
|
484
|
+
const hr = this.createElement('div', ['glass-divider']);
|
|
485
|
+
this._wrapper.appendChild(hr);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
customElements.define('glk-divider', GlkDivider);
|
|
490
|
+
|
|
491
|
+
class GlkStatus extends GlkElement {
|
|
492
|
+
static get observedAttributes() {
|
|
493
|
+
return ['message'];
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
render() {
|
|
497
|
+
this._container = this.createElement('div', ['glass-status']);
|
|
498
|
+
|
|
499
|
+
// Icon via slot
|
|
500
|
+
this._iconSlot = this.createElement('span', []);
|
|
501
|
+
this._iconSlot.appendChild(this.createElement('slot', [], { name: 'icon' }));
|
|
502
|
+
|
|
503
|
+
// Message
|
|
504
|
+
this._messageEl = this.createElement('p', []);
|
|
505
|
+
this._messageEl.textContent = this.getAttribute('message') || '';
|
|
506
|
+
|
|
507
|
+
this._container.appendChild(this._iconSlot);
|
|
508
|
+
this._container.appendChild(this._messageEl);
|
|
509
|
+
this._wrapper.appendChild(this._container);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
onAttributeChanged(name) {
|
|
513
|
+
if (name === 'message' && this._messageEl) {
|
|
514
|
+
this._messageEl.textContent = this.getAttribute('message') || '';
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
get message() { return this.getAttribute('message'); }
|
|
519
|
+
set message(v) { this.setAttribute('message', v); }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
customElements.define('glk-status', GlkStatus);
|
|
523
|
+
|
|
524
|
+
const VARIANTS$2 = ['primary', 'secondary', 'tertiary'];
|
|
525
|
+
const SIZES$1 = ['sm', 'md', 'lg', 'auto'];
|
|
526
|
+
|
|
527
|
+
class GlkButton extends GlkElement {
|
|
528
|
+
static get observedAttributes() {
|
|
529
|
+
return ['variant', 'size', 'disabled', 'type'];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
render() {
|
|
533
|
+
this._btn = this.createElement('button', this._computeClasses(), {
|
|
534
|
+
type: this.getAttribute('type') || 'button'
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
if (this.getBoolAttr('disabled')) {
|
|
538
|
+
this._btn.disabled = true;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
this._btn.appendChild(document.createElement('slot'));
|
|
542
|
+
this._wrapper.appendChild(this._btn);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
setupEvents() {
|
|
546
|
+
this._onClick = (e) => {
|
|
547
|
+
if (this.getBoolAttr('disabled')) {
|
|
548
|
+
e.preventDefault();
|
|
549
|
+
e.stopPropagation();
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
this.emit('glk-click');
|
|
553
|
+
};
|
|
554
|
+
this._btn.addEventListener('click', this._onClick);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
teardownEvents() {
|
|
558
|
+
this._btn?.removeEventListener('click', this._onClick);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
onAttributeChanged(name) {
|
|
562
|
+
if (!this._btn) return;
|
|
563
|
+
switch (name) {
|
|
564
|
+
case 'variant':
|
|
565
|
+
case 'size':
|
|
566
|
+
this._btn.className = this._computeClasses().join(' ');
|
|
567
|
+
break;
|
|
568
|
+
case 'disabled':
|
|
569
|
+
this._btn.disabled = this.getBoolAttr('disabled');
|
|
570
|
+
break;
|
|
571
|
+
case 'type':
|
|
572
|
+
this._btn.setAttribute('type', this.getAttribute('type') || 'button');
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
_computeClasses() {
|
|
578
|
+
const classes = ['glass-btn'];
|
|
579
|
+
const variant = this.getAttribute('variant');
|
|
580
|
+
if (variant && VARIANTS$2.includes(variant)) {
|
|
581
|
+
classes.push(`glass-btn--${variant}`);
|
|
582
|
+
}
|
|
583
|
+
const size = this.getAttribute('size');
|
|
584
|
+
if (size && SIZES$1.includes(size)) {
|
|
585
|
+
classes.push(`glass-btn--${size}`);
|
|
586
|
+
}
|
|
587
|
+
return classes;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
get variant() { return this.getAttribute('variant'); }
|
|
591
|
+
set variant(v) { this.setAttribute('variant', v); }
|
|
592
|
+
|
|
593
|
+
get size() { return this.getAttribute('size'); }
|
|
594
|
+
set size(v) { this.setAttribute('size', v); }
|
|
595
|
+
|
|
596
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
597
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
customElements.define('glk-button', GlkButton);
|
|
601
|
+
|
|
602
|
+
class GlkInput extends GlkFormElement {
|
|
603
|
+
static get observedAttributes() {
|
|
604
|
+
return ['label', 'type', 'placeholder', 'error', 'hint', 'disabled', 'name', 'value', 'required'];
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
render() {
|
|
608
|
+
const group = this.createElement('div', ['glass-input-group']);
|
|
609
|
+
|
|
610
|
+
// Label
|
|
611
|
+
this._labelEl = this.createElement('label', ['glass-label']);
|
|
612
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
613
|
+
|
|
614
|
+
// Input
|
|
615
|
+
this._input = this.createElement('input', this._computeInputClasses(), {
|
|
616
|
+
type: this.getAttribute('type') || 'text'
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
const placeholder = this.getAttribute('placeholder');
|
|
620
|
+
if (placeholder) this._input.setAttribute('placeholder', placeholder);
|
|
621
|
+
|
|
622
|
+
const name = this.getAttribute('name');
|
|
623
|
+
if (name) this._input.setAttribute('name', name);
|
|
624
|
+
|
|
625
|
+
const value = this.getAttribute('value');
|
|
626
|
+
if (value) this._input.value = value;
|
|
627
|
+
|
|
628
|
+
if (this.getBoolAttr('disabled')) this._input.disabled = true;
|
|
629
|
+
if (this.getBoolAttr('required')) this._input.required = true;
|
|
630
|
+
|
|
631
|
+
// Hint
|
|
632
|
+
this._hintEl = this.createElement('span', this._computeHintClasses());
|
|
633
|
+
this._hintEl.textContent = this.getAttribute('hint') || '';
|
|
634
|
+
|
|
635
|
+
group.appendChild(this._labelEl);
|
|
636
|
+
group.appendChild(this._input);
|
|
637
|
+
if (this.getAttribute('hint')) group.appendChild(this._hintEl);
|
|
638
|
+
|
|
639
|
+
this._group = group;
|
|
640
|
+
this._wrapper.appendChild(group);
|
|
641
|
+
|
|
642
|
+
this._syncFormValue();
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
setupEvents() {
|
|
646
|
+
this._onInput = () => {
|
|
647
|
+
this._syncFormValue();
|
|
648
|
+
this.emit('glk-input', { value: this._input.value });
|
|
649
|
+
this.dispatchEvent(new Event('input', { bubbles: true }));
|
|
650
|
+
};
|
|
651
|
+
this._onChangeNative = () => {
|
|
652
|
+
this.emit('glk-change', { value: this._input.value });
|
|
653
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
654
|
+
};
|
|
655
|
+
this._input.addEventListener('input', this._onInput);
|
|
656
|
+
this._input.addEventListener('change', this._onChangeNative);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
teardownEvents() {
|
|
660
|
+
this._input?.removeEventListener('input', this._onInput);
|
|
661
|
+
this._input?.removeEventListener('change', this._onChangeNative);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
onAttributeChanged(name) {
|
|
665
|
+
if (!this._input) return;
|
|
666
|
+
switch (name) {
|
|
667
|
+
case 'label':
|
|
668
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
669
|
+
break;
|
|
670
|
+
case 'type':
|
|
671
|
+
this._input.setAttribute('type', this.getAttribute('type') || 'text');
|
|
672
|
+
break;
|
|
673
|
+
case 'placeholder':
|
|
674
|
+
this._input.setAttribute('placeholder', this.getAttribute('placeholder') || '');
|
|
675
|
+
break;
|
|
676
|
+
case 'error':
|
|
677
|
+
this._input.className = this._computeInputClasses().join(' ');
|
|
678
|
+
this._hintEl.className = this._computeHintClasses().join(' ');
|
|
679
|
+
break;
|
|
680
|
+
case 'hint':
|
|
681
|
+
this._hintEl.textContent = this.getAttribute('hint') || '';
|
|
682
|
+
if (this.getAttribute('hint') && !this._hintEl.parentNode) {
|
|
683
|
+
this._group.appendChild(this._hintEl);
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
case 'disabled':
|
|
687
|
+
this._input.disabled = this.getBoolAttr('disabled');
|
|
688
|
+
break;
|
|
689
|
+
case 'name':
|
|
690
|
+
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
691
|
+
break;
|
|
692
|
+
case 'value':
|
|
693
|
+
this._input.value = this.getAttribute('value') || '';
|
|
694
|
+
this._syncFormValue();
|
|
695
|
+
break;
|
|
696
|
+
case 'required':
|
|
697
|
+
this._input.required = this.getBoolAttr('required');
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
_computeInputClasses() {
|
|
703
|
+
const classes = ['glass-input'];
|
|
704
|
+
if (this.getBoolAttr('error')) classes.push('glass-input--error');
|
|
705
|
+
return classes;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
_computeHintClasses() {
|
|
709
|
+
const classes = ['glass-hint'];
|
|
710
|
+
if (this.getBoolAttr('error')) classes.push('glass-hint--error');
|
|
711
|
+
return classes;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
_syncFormValue() {
|
|
715
|
+
this.setFormValue(this._input.value);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
resetValue() {
|
|
719
|
+
this._input.value = this.getAttribute('value') || '';
|
|
720
|
+
this._syncFormValue();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
get value() { return this._input?.value ?? ''; }
|
|
724
|
+
set value(v) {
|
|
725
|
+
if (this._input) this._input.value = v;
|
|
726
|
+
this._syncFormValue();
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
730
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
731
|
+
|
|
732
|
+
get name() { return this.getAttribute('name'); }
|
|
733
|
+
set name(v) { this.setAttribute('name', v); }
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
customElements.define('glk-input', GlkInput);
|
|
737
|
+
|
|
738
|
+
class GlkTextarea extends GlkFormElement {
|
|
739
|
+
static get observedAttributes() {
|
|
740
|
+
return ['label', 'placeholder', 'rows', 'disabled', 'name', 'value', 'required'];
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
render() {
|
|
744
|
+
const group = this.createElement('div', ['glass-input-group']);
|
|
745
|
+
|
|
746
|
+
this._labelEl = this.createElement('label', ['glass-label']);
|
|
747
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
748
|
+
|
|
749
|
+
this._textarea = this.createElement('textarea', ['glass-textarea']);
|
|
750
|
+
const placeholder = this.getAttribute('placeholder');
|
|
751
|
+
if (placeholder) this._textarea.setAttribute('placeholder', placeholder);
|
|
752
|
+
|
|
753
|
+
const rows = this.getAttribute('rows');
|
|
754
|
+
if (rows) this._textarea.setAttribute('rows', rows);
|
|
755
|
+
|
|
756
|
+
const name = this.getAttribute('name');
|
|
757
|
+
if (name) this._textarea.setAttribute('name', name);
|
|
758
|
+
|
|
759
|
+
const value = this.getAttribute('value');
|
|
760
|
+
if (value) this._textarea.value = value;
|
|
761
|
+
|
|
762
|
+
if (this.getBoolAttr('disabled')) this._textarea.disabled = true;
|
|
763
|
+
if (this.getBoolAttr('required')) this._textarea.required = true;
|
|
764
|
+
|
|
765
|
+
group.appendChild(this._labelEl);
|
|
766
|
+
group.appendChild(this._textarea);
|
|
767
|
+
|
|
768
|
+
this._wrapper.appendChild(group);
|
|
769
|
+
this._syncFormValue();
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
setupEvents() {
|
|
773
|
+
this._onInput = () => {
|
|
774
|
+
this._syncFormValue();
|
|
775
|
+
this.emit('glk-input', { value: this._textarea.value });
|
|
776
|
+
this.dispatchEvent(new Event('input', { bubbles: true }));
|
|
777
|
+
};
|
|
778
|
+
this._textarea.addEventListener('input', this._onInput);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
teardownEvents() {
|
|
782
|
+
this._textarea?.removeEventListener('input', this._onInput);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
onAttributeChanged(name) {
|
|
786
|
+
if (!this._textarea) return;
|
|
787
|
+
switch (name) {
|
|
788
|
+
case 'label':
|
|
789
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
790
|
+
break;
|
|
791
|
+
case 'placeholder':
|
|
792
|
+
this._textarea.setAttribute('placeholder', this.getAttribute('placeholder') || '');
|
|
793
|
+
break;
|
|
794
|
+
case 'rows':
|
|
795
|
+
this._textarea.setAttribute('rows', this.getAttribute('rows') || '');
|
|
796
|
+
break;
|
|
797
|
+
case 'disabled':
|
|
798
|
+
this._textarea.disabled = this.getBoolAttr('disabled');
|
|
799
|
+
break;
|
|
800
|
+
case 'name':
|
|
801
|
+
this._textarea.setAttribute('name', this.getAttribute('name') || '');
|
|
802
|
+
break;
|
|
803
|
+
case 'value':
|
|
804
|
+
this._textarea.value = this.getAttribute('value') || '';
|
|
805
|
+
this._syncFormValue();
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
_syncFormValue() {
|
|
811
|
+
this.setFormValue(this._textarea.value);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
resetValue() {
|
|
815
|
+
this._textarea.value = this.getAttribute('value') || '';
|
|
816
|
+
this._syncFormValue();
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
get value() { return this._textarea?.value ?? ''; }
|
|
820
|
+
set value(v) {
|
|
821
|
+
if (this._textarea) this._textarea.value = v;
|
|
822
|
+
this._syncFormValue();
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
826
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
customElements.define('glk-textarea', GlkTextarea);
|
|
830
|
+
|
|
831
|
+
class GlkSelect extends GlkFormElement {
|
|
832
|
+
static get observedAttributes() {
|
|
833
|
+
return ['label', 'disabled', 'name', 'value', 'required'];
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
render() {
|
|
837
|
+
const group = this.createElement('div', ['glass-input-group']);
|
|
838
|
+
|
|
839
|
+
this._labelEl = this.createElement('label', ['glass-label']);
|
|
840
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
841
|
+
|
|
842
|
+
this._select = this.createElement('select', ['glass-select']);
|
|
843
|
+
|
|
844
|
+
const name = this.getAttribute('name');
|
|
845
|
+
if (name) this._select.setAttribute('name', name);
|
|
846
|
+
|
|
847
|
+
if (this.getBoolAttr('disabled')) this._select.disabled = true;
|
|
848
|
+
if (this.getBoolAttr('required')) this._select.required = true;
|
|
849
|
+
|
|
850
|
+
// Move slotted <option> elements into the shadow select
|
|
851
|
+
this._moveOptions();
|
|
852
|
+
|
|
853
|
+
group.appendChild(this._labelEl);
|
|
854
|
+
group.appendChild(this._select);
|
|
855
|
+
|
|
856
|
+
this._wrapper.appendChild(group);
|
|
857
|
+
|
|
858
|
+
const value = this.getAttribute('value');
|
|
859
|
+
if (value) this._select.value = value;
|
|
860
|
+
|
|
861
|
+
this._syncFormValue();
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
_moveOptions() {
|
|
865
|
+
// Copy <option> children from the light DOM into the shadow <select>
|
|
866
|
+
const options = this.querySelectorAll('option');
|
|
867
|
+
options.forEach(opt => {
|
|
868
|
+
this._select.appendChild(opt.cloneNode(true));
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
setupEvents() {
|
|
873
|
+
this._onChange = () => {
|
|
874
|
+
this._syncFormValue();
|
|
875
|
+
this.emit('glk-change', { value: this._select.value });
|
|
876
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
877
|
+
};
|
|
878
|
+
this._select.addEventListener('change', this._onChange);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
teardownEvents() {
|
|
882
|
+
this._select?.removeEventListener('change', this._onChange);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
onAttributeChanged(name) {
|
|
886
|
+
if (!this._select) return;
|
|
887
|
+
switch (name) {
|
|
888
|
+
case 'label':
|
|
889
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
890
|
+
break;
|
|
891
|
+
case 'disabled':
|
|
892
|
+
this._select.disabled = this.getBoolAttr('disabled');
|
|
893
|
+
break;
|
|
894
|
+
case 'name':
|
|
895
|
+
this._select.setAttribute('name', this.getAttribute('name') || '');
|
|
896
|
+
break;
|
|
897
|
+
case 'value':
|
|
898
|
+
this._select.value = this.getAttribute('value') || '';
|
|
899
|
+
this._syncFormValue();
|
|
900
|
+
break;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
_syncFormValue() {
|
|
905
|
+
this.setFormValue(this._select.value);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
resetValue() {
|
|
909
|
+
this._select.selectedIndex = 0;
|
|
910
|
+
this._syncFormValue();
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
get value() { return this._select?.value ?? ''; }
|
|
914
|
+
set value(v) {
|
|
915
|
+
if (this._select) this._select.value = v;
|
|
916
|
+
this._syncFormValue();
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
920
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
customElements.define('glk-select', GlkSelect);
|
|
924
|
+
|
|
925
|
+
const SEARCH_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>`;
|
|
926
|
+
|
|
927
|
+
class GlkSearch extends GlkFormElement {
|
|
928
|
+
static get observedAttributes() {
|
|
929
|
+
return ['placeholder', 'name', 'value', 'disabled'];
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
render() {
|
|
933
|
+
const container = this.createElement('div', ['glass-search']);
|
|
934
|
+
|
|
935
|
+
const icon = this.createElement('span', ['glass-search__icon']);
|
|
936
|
+
icon.innerHTML = SEARCH_SVG;
|
|
937
|
+
|
|
938
|
+
this._input = this.createElement('input', ['glass-input'], {
|
|
939
|
+
type: 'search'
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
const placeholder = this.getAttribute('placeholder');
|
|
943
|
+
if (placeholder) this._input.setAttribute('placeholder', placeholder);
|
|
944
|
+
|
|
945
|
+
const name = this.getAttribute('name');
|
|
946
|
+
if (name) this._input.setAttribute('name', name);
|
|
947
|
+
|
|
948
|
+
const value = this.getAttribute('value');
|
|
949
|
+
if (value) this._input.value = value;
|
|
950
|
+
|
|
951
|
+
if (this.getBoolAttr('disabled')) this._input.disabled = true;
|
|
952
|
+
|
|
953
|
+
container.appendChild(icon);
|
|
954
|
+
container.appendChild(this._input);
|
|
955
|
+
|
|
956
|
+
this._wrapper.appendChild(container);
|
|
957
|
+
this._syncFormValue();
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
setupEvents() {
|
|
961
|
+
this._onInput = () => {
|
|
962
|
+
this._syncFormValue();
|
|
963
|
+
this.emit('glk-input', { value: this._input.value });
|
|
964
|
+
this.dispatchEvent(new Event('input', { bubbles: true }));
|
|
965
|
+
};
|
|
966
|
+
this._input.addEventListener('input', this._onInput);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
teardownEvents() {
|
|
970
|
+
this._input?.removeEventListener('input', this._onInput);
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
onAttributeChanged(name) {
|
|
974
|
+
if (!this._input) return;
|
|
975
|
+
switch (name) {
|
|
976
|
+
case 'placeholder':
|
|
977
|
+
this._input.setAttribute('placeholder', this.getAttribute('placeholder') || '');
|
|
978
|
+
break;
|
|
979
|
+
case 'name':
|
|
980
|
+
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
981
|
+
break;
|
|
982
|
+
case 'value':
|
|
983
|
+
this._input.value = this.getAttribute('value') || '';
|
|
984
|
+
this._syncFormValue();
|
|
985
|
+
break;
|
|
986
|
+
case 'disabled':
|
|
987
|
+
this._input.disabled = this.getBoolAttr('disabled');
|
|
988
|
+
break;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
_syncFormValue() {
|
|
993
|
+
this.setFormValue(this._input.value);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
resetValue() {
|
|
997
|
+
this._input.value = this.getAttribute('value') || '';
|
|
998
|
+
this._syncFormValue();
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
get value() { return this._input?.value ?? ''; }
|
|
1002
|
+
set value(v) {
|
|
1003
|
+
if (this._input) this._input.value = v;
|
|
1004
|
+
this._syncFormValue();
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
customElements.define('glk-search', GlkSearch);
|
|
1009
|
+
|
|
1010
|
+
class GlkToggle extends GlkFormElement {
|
|
1011
|
+
static get observedAttributes() {
|
|
1012
|
+
return ['checked', 'disabled', 'label', 'name', 'value'];
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
render() {
|
|
1016
|
+
const label = this.createElement('label', ['glass-toggle']);
|
|
1017
|
+
|
|
1018
|
+
this._input = this.createElement('input', ['glass-toggle__input'], {
|
|
1019
|
+
type: 'checkbox'
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
const name = this.getAttribute('name');
|
|
1023
|
+
if (name) this._input.setAttribute('name', name);
|
|
1024
|
+
|
|
1025
|
+
const track = this.createElement('span', ['glass-toggle__track']);
|
|
1026
|
+
const thumb = this.createElement('span', ['glass-toggle__thumb']);
|
|
1027
|
+
track.appendChild(thumb);
|
|
1028
|
+
|
|
1029
|
+
this._labelEl = this.createElement('span', ['glass-toggle__label']);
|
|
1030
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1031
|
+
|
|
1032
|
+
label.appendChild(this._input);
|
|
1033
|
+
label.appendChild(track);
|
|
1034
|
+
label.appendChild(this._labelEl);
|
|
1035
|
+
|
|
1036
|
+
if (this.getBoolAttr('checked')) this._input.checked = true;
|
|
1037
|
+
if (this.getBoolAttr('disabled')) this._input.disabled = true;
|
|
1038
|
+
|
|
1039
|
+
this._defaultChecked = this.getBoolAttr('checked');
|
|
1040
|
+
this._wrapper.appendChild(label);
|
|
1041
|
+
|
|
1042
|
+
this._syncFormValue();
|
|
1043
|
+
this.setAttribute('role', 'switch');
|
|
1044
|
+
this.setAttribute('aria-checked', String(this._input.checked));
|
|
1045
|
+
if (this.getBoolAttr('disabled')) this.setAttribute('aria-disabled', 'true');
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
setupEvents() {
|
|
1049
|
+
this._onChange = () => {
|
|
1050
|
+
this._syncing = true;
|
|
1051
|
+
this.setBoolAttr('checked', this._input.checked);
|
|
1052
|
+
this._syncing = false;
|
|
1053
|
+
this.setAttribute('aria-checked', String(this._input.checked));
|
|
1054
|
+
this._syncFormValue();
|
|
1055
|
+
this.emit('glk-change', { checked: this._input.checked });
|
|
1056
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1057
|
+
};
|
|
1058
|
+
this._input.addEventListener('change', this._onChange);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
teardownEvents() {
|
|
1062
|
+
this._input?.removeEventListener('change', this._onChange);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
onAttributeChanged(name) {
|
|
1066
|
+
if (this._syncing) return;
|
|
1067
|
+
if (!this._input) return;
|
|
1068
|
+
switch (name) {
|
|
1069
|
+
case 'checked':
|
|
1070
|
+
this._input.checked = this.getBoolAttr('checked');
|
|
1071
|
+
this.setAttribute('aria-checked', String(this._input.checked));
|
|
1072
|
+
this._syncFormValue();
|
|
1073
|
+
break;
|
|
1074
|
+
case 'disabled':
|
|
1075
|
+
this._input.disabled = this.getBoolAttr('disabled');
|
|
1076
|
+
this.setAttribute('aria-disabled', String(this.getBoolAttr('disabled')));
|
|
1077
|
+
break;
|
|
1078
|
+
case 'label':
|
|
1079
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1080
|
+
break;
|
|
1081
|
+
case 'name':
|
|
1082
|
+
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
_syncFormValue() {
|
|
1088
|
+
const val = this.getAttribute('value') || 'on';
|
|
1089
|
+
this.setFormValue(this._input.checked ? val : null);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
resetValue() {
|
|
1093
|
+
this._input.checked = this._defaultChecked;
|
|
1094
|
+
this.setBoolAttr('checked', this._defaultChecked);
|
|
1095
|
+
this.setAttribute('aria-checked', String(this._defaultChecked));
|
|
1096
|
+
this._syncFormValue();
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
restoreValue(state) {
|
|
1100
|
+
if (state) {
|
|
1101
|
+
this._input.checked = true;
|
|
1102
|
+
this.setBoolAttr('checked', true);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
get checked() { return this._input?.checked ?? false; }
|
|
1107
|
+
set checked(v) {
|
|
1108
|
+
if (this._input) this._input.checked = v;
|
|
1109
|
+
this.setBoolAttr('checked', v);
|
|
1110
|
+
this.setAttribute('aria-checked', String(v));
|
|
1111
|
+
this._syncFormValue();
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
1115
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
1116
|
+
|
|
1117
|
+
get name() { return this.getAttribute('name'); }
|
|
1118
|
+
set name(v) { this.setAttribute('name', v); }
|
|
1119
|
+
|
|
1120
|
+
get value() { return this.getAttribute('value') || 'on'; }
|
|
1121
|
+
set value(v) { this.setAttribute('value', v); }
|
|
1122
|
+
|
|
1123
|
+
get label() { return this.getAttribute('label'); }
|
|
1124
|
+
set label(v) { this.setAttribute('label', v); }
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
customElements.define('glk-toggle', GlkToggle);
|
|
1128
|
+
|
|
1129
|
+
const CHECKMARK_SVG = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>`;
|
|
1130
|
+
|
|
1131
|
+
class GlkCheckbox extends GlkFormElement {
|
|
1132
|
+
static get observedAttributes() {
|
|
1133
|
+
return ['checked', 'disabled', 'label', 'name', 'value'];
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
render() {
|
|
1137
|
+
const label = this.createElement('label', ['glass-checkbox']);
|
|
1138
|
+
|
|
1139
|
+
this._input = this.createElement('input', ['glass-checkbox__input'], {
|
|
1140
|
+
type: 'checkbox'
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
const name = this.getAttribute('name');
|
|
1144
|
+
if (name) this._input.setAttribute('name', name);
|
|
1145
|
+
|
|
1146
|
+
const box = this.createElement('span', ['glass-checkbox__box']);
|
|
1147
|
+
box.innerHTML = CHECKMARK_SVG;
|
|
1148
|
+
|
|
1149
|
+
this._labelEl = this.createElement('span', ['glass-checkbox__label']);
|
|
1150
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1151
|
+
|
|
1152
|
+
label.appendChild(this._input);
|
|
1153
|
+
label.appendChild(box);
|
|
1154
|
+
label.appendChild(this._labelEl);
|
|
1155
|
+
|
|
1156
|
+
if (this.getBoolAttr('checked')) this._input.checked = true;
|
|
1157
|
+
if (this.getBoolAttr('disabled')) this._input.disabled = true;
|
|
1158
|
+
|
|
1159
|
+
this._defaultChecked = this.getBoolAttr('checked');
|
|
1160
|
+
this._wrapper.appendChild(label);
|
|
1161
|
+
this._syncFormValue();
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
setupEvents() {
|
|
1165
|
+
this._onChange = () => {
|
|
1166
|
+
this._syncing = true;
|
|
1167
|
+
this.setBoolAttr('checked', this._input.checked);
|
|
1168
|
+
this._syncing = false;
|
|
1169
|
+
this._syncFormValue();
|
|
1170
|
+
this.emit('glk-change', { checked: this._input.checked });
|
|
1171
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1172
|
+
};
|
|
1173
|
+
this._input.addEventListener('change', this._onChange);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
teardownEvents() {
|
|
1177
|
+
this._input?.removeEventListener('change', this._onChange);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
onAttributeChanged(name) {
|
|
1181
|
+
if (this._syncing) return;
|
|
1182
|
+
if (!this._input) return;
|
|
1183
|
+
switch (name) {
|
|
1184
|
+
case 'checked':
|
|
1185
|
+
this._input.checked = this.getBoolAttr('checked');
|
|
1186
|
+
this._syncFormValue();
|
|
1187
|
+
break;
|
|
1188
|
+
case 'disabled':
|
|
1189
|
+
this._input.disabled = this.getBoolAttr('disabled');
|
|
1190
|
+
break;
|
|
1191
|
+
case 'label':
|
|
1192
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1193
|
+
break;
|
|
1194
|
+
case 'name':
|
|
1195
|
+
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
1196
|
+
break;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
_syncFormValue() {
|
|
1201
|
+
const val = this.getAttribute('value') || 'on';
|
|
1202
|
+
this.setFormValue(this._input.checked ? val : null);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
resetValue() {
|
|
1206
|
+
this._input.checked = this._defaultChecked;
|
|
1207
|
+
this.setBoolAttr('checked', this._defaultChecked);
|
|
1208
|
+
this._syncFormValue();
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
get checked() { return this._input?.checked ?? false; }
|
|
1212
|
+
set checked(v) {
|
|
1213
|
+
if (this._input) this._input.checked = v;
|
|
1214
|
+
this.setBoolAttr('checked', v);
|
|
1215
|
+
this._syncFormValue();
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
1219
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
1220
|
+
|
|
1221
|
+
get name() { return this.getAttribute('name'); }
|
|
1222
|
+
set name(v) { this.setAttribute('name', v); }
|
|
1223
|
+
|
|
1224
|
+
get value() { return this.getAttribute('value') || 'on'; }
|
|
1225
|
+
set value(v) { this.setAttribute('value', v); }
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
customElements.define('glk-checkbox', GlkCheckbox);
|
|
1229
|
+
|
|
1230
|
+
class GlkRadio extends GlkFormElement {
|
|
1231
|
+
static get observedAttributes() {
|
|
1232
|
+
return ['checked', 'disabled', 'label', 'name', 'value'];
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
render() {
|
|
1236
|
+
const label = this.createElement('label', ['glass-radio']);
|
|
1237
|
+
|
|
1238
|
+
this._input = this.createElement('input', ['glass-radio__input'], {
|
|
1239
|
+
type: 'radio'
|
|
1240
|
+
});
|
|
1241
|
+
|
|
1242
|
+
const name = this.getAttribute('name');
|
|
1243
|
+
if (name) this._input.setAttribute('name', name);
|
|
1244
|
+
|
|
1245
|
+
const val = this.getAttribute('value');
|
|
1246
|
+
if (val) this._input.setAttribute('value', val);
|
|
1247
|
+
|
|
1248
|
+
const circle = this.createElement('span', ['glass-radio__circle']);
|
|
1249
|
+
const dot = this.createElement('span', ['glass-radio__dot']);
|
|
1250
|
+
circle.appendChild(dot);
|
|
1251
|
+
|
|
1252
|
+
this._labelEl = this.createElement('span', ['glass-radio__label']);
|
|
1253
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1254
|
+
|
|
1255
|
+
label.appendChild(this._input);
|
|
1256
|
+
label.appendChild(circle);
|
|
1257
|
+
label.appendChild(this._labelEl);
|
|
1258
|
+
|
|
1259
|
+
if (this.getBoolAttr('checked')) this._input.checked = true;
|
|
1260
|
+
if (this.getBoolAttr('disabled')) this._input.disabled = true;
|
|
1261
|
+
|
|
1262
|
+
this._defaultChecked = this.getBoolAttr('checked');
|
|
1263
|
+
this._wrapper.appendChild(label);
|
|
1264
|
+
this._syncFormValue();
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
setupEvents() {
|
|
1268
|
+
this._onChange = () => {
|
|
1269
|
+
this._syncing = true;
|
|
1270
|
+
this.setBoolAttr('checked', this._input.checked);
|
|
1271
|
+
this._syncing = false;
|
|
1272
|
+
this._syncFormValue();
|
|
1273
|
+
this.emit('glk-change', { checked: this._input.checked, value: this._input.value });
|
|
1274
|
+
this.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1275
|
+
};
|
|
1276
|
+
this._input.addEventListener('change', this._onChange);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
teardownEvents() {
|
|
1280
|
+
this._input?.removeEventListener('change', this._onChange);
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
onAttributeChanged(name) {
|
|
1284
|
+
if (this._syncing) return;
|
|
1285
|
+
if (!this._input) return;
|
|
1286
|
+
switch (name) {
|
|
1287
|
+
case 'checked':
|
|
1288
|
+
this._input.checked = this.getBoolAttr('checked');
|
|
1289
|
+
this._syncFormValue();
|
|
1290
|
+
break;
|
|
1291
|
+
case 'disabled':
|
|
1292
|
+
this._input.disabled = this.getBoolAttr('disabled');
|
|
1293
|
+
break;
|
|
1294
|
+
case 'label':
|
|
1295
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1296
|
+
break;
|
|
1297
|
+
case 'name':
|
|
1298
|
+
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
1299
|
+
break;
|
|
1300
|
+
case 'value':
|
|
1301
|
+
this._input.setAttribute('value', this.getAttribute('value') || '');
|
|
1302
|
+
this._syncFormValue();
|
|
1303
|
+
break;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
_syncFormValue() {
|
|
1308
|
+
const val = this.getAttribute('value') || '';
|
|
1309
|
+
this.setFormValue(this._input.checked ? val : null);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
resetValue() {
|
|
1313
|
+
this._input.checked = this._defaultChecked;
|
|
1314
|
+
this.setBoolAttr('checked', this._defaultChecked);
|
|
1315
|
+
this._syncFormValue();
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
get checked() { return this._input?.checked ?? false; }
|
|
1319
|
+
set checked(v) {
|
|
1320
|
+
if (this._input) this._input.checked = v;
|
|
1321
|
+
this.setBoolAttr('checked', v);
|
|
1322
|
+
this._syncFormValue();
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
get disabled() { return this.getBoolAttr('disabled'); }
|
|
1326
|
+
set disabled(v) { this.setBoolAttr('disabled', v); }
|
|
1327
|
+
|
|
1328
|
+
get name() { return this.getAttribute('name'); }
|
|
1329
|
+
set name(v) { this.setAttribute('name', v); }
|
|
1330
|
+
|
|
1331
|
+
get value() { return this.getAttribute('value'); }
|
|
1332
|
+
set value(v) { this.setAttribute('value', v); }
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
customElements.define('glk-radio', GlkRadio);
|
|
1336
|
+
|
|
1337
|
+
class GlkRange extends GlkFormElement {
|
|
1338
|
+
static get observedAttributes() {
|
|
1339
|
+
return ['label', 'min', 'max', 'value', 'step', 'name', 'disabled'];
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
render() {
|
|
1343
|
+
const group = this.createElement('div', ['glass-range-group']);
|
|
1344
|
+
|
|
1345
|
+
const header = this.createElement('div', ['glass-range-header']);
|
|
1346
|
+
|
|
1347
|
+
this._labelEl = this.createElement('label', []);
|
|
1348
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1349
|
+
|
|
1350
|
+
this._valueEl = this.createElement('span', ['glass-range-value']);
|
|
1351
|
+
|
|
1352
|
+
header.appendChild(this._labelEl);
|
|
1353
|
+
header.appendChild(this._valueEl);
|
|
1354
|
+
|
|
1355
|
+
this._input = this.createElement('input', ['glass-range'], {
|
|
1356
|
+
type: 'range',
|
|
1357
|
+
min: this.getAttribute('min') || '0',
|
|
1358
|
+
max: this.getAttribute('max') || '100',
|
|
1359
|
+
value: this.getAttribute('value') || '50'
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
const step = this.getAttribute('step');
|
|
1363
|
+
if (step) this._input.setAttribute('step', step);
|
|
1364
|
+
|
|
1365
|
+
const name = this.getAttribute('name');
|
|
1366
|
+
if (name) this._input.setAttribute('name', name);
|
|
1367
|
+
|
|
1368
|
+
if (this.getBoolAttr('disabled')) this._input.disabled = true;
|
|
1369
|
+
|
|
1370
|
+
this._updateValueDisplay();
|
|
1371
|
+
|
|
1372
|
+
group.appendChild(header);
|
|
1373
|
+
group.appendChild(this._input);
|
|
1374
|
+
|
|
1375
|
+
this._wrapper.appendChild(group);
|
|
1376
|
+
this._defaultValue = this._input.value;
|
|
1377
|
+
this._syncFormValue();
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
setupEvents() {
|
|
1381
|
+
this._onInput = () => {
|
|
1382
|
+
this._updateValueDisplay();
|
|
1383
|
+
this._syncFormValue();
|
|
1384
|
+
this.emit('glk-input', { value: this._input.value });
|
|
1385
|
+
this.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1386
|
+
};
|
|
1387
|
+
this._input.addEventListener('input', this._onInput);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
teardownEvents() {
|
|
1391
|
+
this._input?.removeEventListener('input', this._onInput);
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
onAttributeChanged(name) {
|
|
1395
|
+
if (!this._input) return;
|
|
1396
|
+
switch (name) {
|
|
1397
|
+
case 'label':
|
|
1398
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1399
|
+
break;
|
|
1400
|
+
case 'min':
|
|
1401
|
+
this._input.min = this.getAttribute('min') || '0';
|
|
1402
|
+
break;
|
|
1403
|
+
case 'max':
|
|
1404
|
+
this._input.max = this.getAttribute('max') || '100';
|
|
1405
|
+
break;
|
|
1406
|
+
case 'value':
|
|
1407
|
+
this._input.value = this.getAttribute('value') || '50';
|
|
1408
|
+
this._updateValueDisplay();
|
|
1409
|
+
this._syncFormValue();
|
|
1410
|
+
break;
|
|
1411
|
+
case 'step':
|
|
1412
|
+
this._input.step = this.getAttribute('step') || '';
|
|
1413
|
+
break;
|
|
1414
|
+
case 'name':
|
|
1415
|
+
this._input.setAttribute('name', this.getAttribute('name') || '');
|
|
1416
|
+
break;
|
|
1417
|
+
case 'disabled':
|
|
1418
|
+
this._input.disabled = this.getBoolAttr('disabled');
|
|
1419
|
+
break;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
_updateValueDisplay() {
|
|
1424
|
+
this._valueEl.textContent = `${this._input.value}%`;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
_syncFormValue() {
|
|
1428
|
+
this.setFormValue(this._input.value);
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
resetValue() {
|
|
1432
|
+
this._input.value = this._defaultValue;
|
|
1433
|
+
this._updateValueDisplay();
|
|
1434
|
+
this._syncFormValue();
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
get value() { return this._input?.value ?? ''; }
|
|
1438
|
+
set value(v) {
|
|
1439
|
+
if (this._input) this._input.value = v;
|
|
1440
|
+
this._updateValueDisplay();
|
|
1441
|
+
this._syncFormValue();
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
customElements.define('glk-range', GlkRange);
|
|
1446
|
+
|
|
1447
|
+
const VARIANTS$1 = ['success', 'error'];
|
|
1448
|
+
const SIZES = ['sm', 'lg'];
|
|
1449
|
+
|
|
1450
|
+
class GlkProgress extends GlkElement {
|
|
1451
|
+
static get observedAttributes() {
|
|
1452
|
+
return ['value', 'label', 'variant', 'size'];
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
render() {
|
|
1456
|
+
this._container = this.createElement('div', this._computeClasses());
|
|
1457
|
+
|
|
1458
|
+
// Header
|
|
1459
|
+
const header = this.createElement('div', ['glass-progress__header']);
|
|
1460
|
+
this._labelEl = this.createElement('span', ['glass-progress__label']);
|
|
1461
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1462
|
+
this._valueEl = this.createElement('span', ['glass-progress__value']);
|
|
1463
|
+
|
|
1464
|
+
header.appendChild(this._labelEl);
|
|
1465
|
+
header.appendChild(this._valueEl);
|
|
1466
|
+
|
|
1467
|
+
// Track
|
|
1468
|
+
const track = this.createElement('div', ['glass-progress__track']);
|
|
1469
|
+
this._fill = this.createElement('div', ['glass-progress__fill']);
|
|
1470
|
+
|
|
1471
|
+
track.appendChild(this._fill);
|
|
1472
|
+
|
|
1473
|
+
this._container.appendChild(header);
|
|
1474
|
+
this._container.appendChild(track);
|
|
1475
|
+
|
|
1476
|
+
this._updateValue();
|
|
1477
|
+
|
|
1478
|
+
this._wrapper.appendChild(this._container);
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
onAttributeChanged(name) {
|
|
1482
|
+
if (!this._container) return;
|
|
1483
|
+
switch (name) {
|
|
1484
|
+
case 'value':
|
|
1485
|
+
this._updateValue();
|
|
1486
|
+
break;
|
|
1487
|
+
case 'label':
|
|
1488
|
+
this._labelEl.textContent = this.getAttribute('label') || '';
|
|
1489
|
+
break;
|
|
1490
|
+
case 'variant':
|
|
1491
|
+
case 'size':
|
|
1492
|
+
this._container.className = this._computeClasses().join(' ');
|
|
1493
|
+
break;
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
_updateValue() {
|
|
1498
|
+
const val = Math.min(100, Math.max(0, parseInt(this.getAttribute('value') || '0', 10)));
|
|
1499
|
+
this._fill.style.width = `${val}%`;
|
|
1500
|
+
this._valueEl.textContent = `${val}%`;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
_computeClasses() {
|
|
1504
|
+
const classes = ['glass-progress'];
|
|
1505
|
+
const variant = this.getAttribute('variant');
|
|
1506
|
+
if (variant && VARIANTS$1.includes(variant)) {
|
|
1507
|
+
classes.push(`glass-progress--${variant}`);
|
|
1508
|
+
}
|
|
1509
|
+
const size = this.getAttribute('size');
|
|
1510
|
+
if (size && SIZES.includes(size)) {
|
|
1511
|
+
classes.push(`glass-progress--${size}`);
|
|
1512
|
+
}
|
|
1513
|
+
return classes;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
get value() { return this.getAttribute('value'); }
|
|
1517
|
+
set value(v) { this.setAttribute('value', String(v)); }
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
customElements.define('glk-progress', GlkProgress);
|
|
1521
|
+
|
|
1522
|
+
class GlkModal extends GlkElement {
|
|
1523
|
+
static get observedAttributes() {
|
|
1524
|
+
return ['open', 'title'];
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
render() {
|
|
1528
|
+
this._overlay = this.createElement('div', ['glass-modal-overlay']);
|
|
1529
|
+
|
|
1530
|
+
const modal = this.createElement('div', ['glass-modal']);
|
|
1531
|
+
|
|
1532
|
+
// Header
|
|
1533
|
+
const header = this.createElement('div', ['glass-modal__header']);
|
|
1534
|
+
this._titleEl = this.createElement('h2', ['glass-modal__title']);
|
|
1535
|
+
this._titleEl.textContent = this.getAttribute('title') || '';
|
|
1536
|
+
header.appendChild(this._titleEl);
|
|
1537
|
+
|
|
1538
|
+
// Body
|
|
1539
|
+
const body = this.createElement('div', ['glass-modal__body']);
|
|
1540
|
+
body.appendChild(document.createElement('slot'));
|
|
1541
|
+
|
|
1542
|
+
// Footer — clone action buttons from light DOM into shadow DOM
|
|
1543
|
+
this._footer = this.createElement('div', ['glass-modal__footer']);
|
|
1544
|
+
this._populateFooter();
|
|
1545
|
+
|
|
1546
|
+
modal.appendChild(header);
|
|
1547
|
+
modal.appendChild(body);
|
|
1548
|
+
modal.appendChild(this._footer);
|
|
1549
|
+
|
|
1550
|
+
this._overlay.appendChild(modal);
|
|
1551
|
+
this._wrapper.appendChild(this._overlay);
|
|
1552
|
+
|
|
1553
|
+
if (this.getBoolAttr('open')) {
|
|
1554
|
+
this._overlay.classList.add('is-active');
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
_populateFooter() {
|
|
1559
|
+
this._footer.innerHTML = '';
|
|
1560
|
+
const actionsSlot = this.querySelector('[slot="actions"]');
|
|
1561
|
+
if (actionsSlot) {
|
|
1562
|
+
const buttons = actionsSlot.querySelectorAll('button');
|
|
1563
|
+
buttons.forEach(btn => {
|
|
1564
|
+
const clone = btn.cloneNode(true);
|
|
1565
|
+
// Forward click events to the original button
|
|
1566
|
+
clone.addEventListener('click', () => btn.click());
|
|
1567
|
+
this._footer.appendChild(clone);
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
setupEvents() {
|
|
1573
|
+
// Close on overlay click (outside modal)
|
|
1574
|
+
this._onOverlayClick = (e) => {
|
|
1575
|
+
if (e.target === this._overlay) {
|
|
1576
|
+
this.removeAttribute('open');
|
|
1577
|
+
this.emit('glk-close');
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
this._overlay.addEventListener('click', this._onOverlayClick);
|
|
1581
|
+
|
|
1582
|
+
// Close on Escape
|
|
1583
|
+
this._onKeydown = (e) => {
|
|
1584
|
+
if (e.key === 'Escape' && this.getBoolAttr('open')) {
|
|
1585
|
+
this.removeAttribute('open');
|
|
1586
|
+
this.emit('glk-close');
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
document.addEventListener('keydown', this._onKeydown);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
teardownEvents() {
|
|
1593
|
+
this._overlay?.removeEventListener('click', this._onOverlayClick);
|
|
1594
|
+
document.removeEventListener('keydown', this._onKeydown);
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
onAttributeChanged(name) {
|
|
1598
|
+
if (!this._overlay) return;
|
|
1599
|
+
switch (name) {
|
|
1600
|
+
case 'open':
|
|
1601
|
+
this._overlay.classList.toggle('is-active', this.getBoolAttr('open'));
|
|
1602
|
+
break;
|
|
1603
|
+
case 'title':
|
|
1604
|
+
this._titleEl.textContent = this.getAttribute('title') || '';
|
|
1605
|
+
break;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
show() { this.setAttribute('open', ''); }
|
|
1610
|
+
close() { this.removeAttribute('open'); }
|
|
1611
|
+
|
|
1612
|
+
get open() { return this.getBoolAttr('open'); }
|
|
1613
|
+
set open(v) { this.setBoolAttr('open', v); }
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
customElements.define('glk-modal', GlkModal);
|
|
1617
|
+
|
|
1618
|
+
const VARIANTS = ['success', 'error', 'warning'];
|
|
1619
|
+
|
|
1620
|
+
const ICONS = {
|
|
1621
|
+
success: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>`,
|
|
1622
|
+
error: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>`,
|
|
1623
|
+
warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`
|
|
1624
|
+
};
|
|
1625
|
+
|
|
1626
|
+
class GlkToast extends GlkElement {
|
|
1627
|
+
static get observedAttributes() {
|
|
1628
|
+
return ['message', 'variant', 'duration', 'visible'];
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
render() {
|
|
1632
|
+
this._toast = this.createElement('div', this._computeClasses());
|
|
1633
|
+
|
|
1634
|
+
this._iconEl = this.createElement('span', ['glass-toast__icon']);
|
|
1635
|
+
this._textEl = this.createElement('span', ['glass-toast__text']);
|
|
1636
|
+
this._textEl.textContent = this.getAttribute('message') || '';
|
|
1637
|
+
|
|
1638
|
+
this._updateIcon();
|
|
1639
|
+
|
|
1640
|
+
this._toast.appendChild(this._iconEl);
|
|
1641
|
+
this._toast.appendChild(this._textEl);
|
|
1642
|
+
this._wrapper.appendChild(this._toast);
|
|
1643
|
+
|
|
1644
|
+
if (this.getBoolAttr('visible')) {
|
|
1645
|
+
this._show();
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
onAttributeChanged(name) {
|
|
1650
|
+
if (!this._toast) return;
|
|
1651
|
+
switch (name) {
|
|
1652
|
+
case 'message':
|
|
1653
|
+
this._textEl.textContent = this.getAttribute('message') || '';
|
|
1654
|
+
break;
|
|
1655
|
+
case 'variant':
|
|
1656
|
+
this._toast.className = this._computeClasses().join(' ');
|
|
1657
|
+
this._updateIcon();
|
|
1658
|
+
break;
|
|
1659
|
+
case 'visible':
|
|
1660
|
+
if (this.getBoolAttr('visible')) {
|
|
1661
|
+
this._show();
|
|
1662
|
+
} else {
|
|
1663
|
+
this._hide();
|
|
1664
|
+
}
|
|
1665
|
+
break;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
_computeClasses() {
|
|
1670
|
+
const classes = ['glass-toast'];
|
|
1671
|
+
const variant = this.getAttribute('variant');
|
|
1672
|
+
if (variant && VARIANTS.includes(variant)) {
|
|
1673
|
+
classes.push(`glass-toast--${variant}`);
|
|
1674
|
+
}
|
|
1675
|
+
return classes;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
_updateIcon() {
|
|
1679
|
+
const variant = this.getAttribute('variant');
|
|
1680
|
+
this._iconEl.innerHTML = ICONS[variant] || ICONS.success;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
_show() {
|
|
1684
|
+
this._toast.classList.add('is-visible');
|
|
1685
|
+
const duration = parseInt(this.getAttribute('duration') || '3000', 10);
|
|
1686
|
+
if (duration > 0) {
|
|
1687
|
+
clearTimeout(this._timer);
|
|
1688
|
+
this._timer = setTimeout(() => {
|
|
1689
|
+
this.removeAttribute('visible');
|
|
1690
|
+
this.emit('glk-dismiss');
|
|
1691
|
+
}, duration);
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
_hide() {
|
|
1696
|
+
this._toast.classList.remove('is-visible');
|
|
1697
|
+
clearTimeout(this._timer);
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/** Programmatic show */
|
|
1701
|
+
show(message, variant, duration) {
|
|
1702
|
+
if (message) this.setAttribute('message', message);
|
|
1703
|
+
if (variant) this.setAttribute('variant', variant);
|
|
1704
|
+
if (duration) this.setAttribute('duration', String(duration));
|
|
1705
|
+
this.setAttribute('visible', '');
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
dismiss() {
|
|
1709
|
+
this.removeAttribute('visible');
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
disconnectedCallback() {
|
|
1713
|
+
super.disconnectedCallback();
|
|
1714
|
+
clearTimeout(this._timer);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
customElements.define('glk-toast', GlkToast);
|
|
1719
|
+
|
|
1720
|
+
class GlkAccordion extends GlkElement {
|
|
1721
|
+
render() {
|
|
1722
|
+
const container = this.createElement('div', ['glass-accordion']);
|
|
1723
|
+
container.appendChild(document.createElement('slot'));
|
|
1724
|
+
this._wrapper.appendChild(container);
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
customElements.define('glk-accordion', GlkAccordion);
|
|
1729
|
+
|
|
1730
|
+
const CHEVRON_SVG = `<span class="glass-accordion__trigger-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><polyline points="6 9 12 15 18 9"/></svg></span>`;
|
|
1731
|
+
|
|
1732
|
+
class GlkAccordionItem extends GlkElement {
|
|
1733
|
+
static get observedAttributes() {
|
|
1734
|
+
return ['title', 'open'];
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
render() {
|
|
1738
|
+
this._item = this.createElement('div', ['glass-accordion__item']);
|
|
1739
|
+
if (this.getBoolAttr('open')) this._item.classList.add('is-open');
|
|
1740
|
+
|
|
1741
|
+
// Trigger button
|
|
1742
|
+
this._trigger = this.createElement('button', ['glass-accordion__trigger']);
|
|
1743
|
+
this._triggerText = document.createTextNode(this.getAttribute('title') || '');
|
|
1744
|
+
this._trigger.appendChild(this._triggerText);
|
|
1745
|
+
this._trigger.insertAdjacentHTML('beforeend', CHEVRON_SVG);
|
|
1746
|
+
|
|
1747
|
+
// Content
|
|
1748
|
+
this._content = this.createElement('div', ['glass-accordion__content']);
|
|
1749
|
+
const body = this.createElement('div', ['glass-accordion__body']);
|
|
1750
|
+
body.appendChild(document.createElement('slot'));
|
|
1751
|
+
this._content.appendChild(body);
|
|
1752
|
+
|
|
1753
|
+
this._item.appendChild(this._trigger);
|
|
1754
|
+
this._item.appendChild(this._content);
|
|
1755
|
+
|
|
1756
|
+
this._wrapper.appendChild(this._item);
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
setupEvents() {
|
|
1760
|
+
this._onClick = () => {
|
|
1761
|
+
this.setBoolAttr('open', !this.getBoolAttr('open'));
|
|
1762
|
+
this.emit('glk-toggle', { open: this.getBoolAttr('open') });
|
|
1763
|
+
};
|
|
1764
|
+
this._trigger.addEventListener('click', this._onClick);
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
teardownEvents() {
|
|
1768
|
+
this._trigger?.removeEventListener('click', this._onClick);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
onAttributeChanged(name) {
|
|
1772
|
+
if (!this._item) return;
|
|
1773
|
+
switch (name) {
|
|
1774
|
+
case 'open':
|
|
1775
|
+
this._item.classList.toggle('is-open', this.getBoolAttr('open'));
|
|
1776
|
+
break;
|
|
1777
|
+
case 'title':
|
|
1778
|
+
this._triggerText.textContent = this.getAttribute('title') || '';
|
|
1779
|
+
break;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
get open() { return this.getBoolAttr('open'); }
|
|
1784
|
+
set open(v) { this.setBoolAttr('open', v); }
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
customElements.define('glk-accordion-item', GlkAccordionItem);
|
|
1788
|
+
|
|
1789
|
+
export { GlkAccordion, GlkAccordionItem, GlkAvatar, GlkBadge, GlkButton, GlkCard, GlkCheckbox, GlkDivider, GlkInput, GlkModal, GlkNav, GlkPill, GlkProgress, GlkRadio, GlkRange, GlkSearch, GlkSelect, GlkStatus, GlkTabBar, GlkTabItem, GlkTextarea, GlkTitle, GlkToast, GlkToggle };
|