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