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