@wavelengthusaf/web-components 1.17.0 → 1.18.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.
@@ -6826,7 +6826,7 @@ if (!customElements.get("wavelength-search")) {
6826
6826
  }
6827
6827
 
6828
6828
  // src/web-components/wavelength-switch.template.html
6829
- var wavelength_switch_template_default = '<style>\n :host {\n display: block;\n --font-color: #333;\n --font-size: 20px;\n --font-family: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n --switch-color: #2196f3;\n --label-font: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n\n /* Variable-based sizing */\n --container-width: 60px;\n --container-height: 34px;\n --track-height: 22px;\n --knob-size: 28px;\n\n /* Internal Calculations */\n --track-top: calc((var(--container-height) - var(--track-height)) / 2);\n --knob-top: calc((var(--container-height) - var(--knob-size)) / 2);\n --knob-translate: calc(var(--container-width) - var(--knob-size));\n }\n\n :host([size="small"]) {\n --container-width: 40px;\n --container-height: 24px;\n --track-height: 14px;\n --knob-size: 20px;\n --font-size: 16px;\n }\n\n :host([size="large"]) {\n --container-width: 80px;\n --container-height: 48px;\n --track-height: 30px;\n --knob-size: 40px;\n --font-size: 24px;\n }\n\n /* Switch Base Styles */\n .switch {\n position: relative;\n display: inline-block;\n width: var(--container-width);\n height: var(--container-height);\n }\n\n .switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n /* Slider (The Track) */\n .slider {\n position: absolute;\n cursor: pointer;\n top: var(--track-top);\n left: 0;\n right: 0;\n bottom: var(--track-top);\n background-color: #ccc;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--track-height);\n }\n\n .slider:hover {\n cursor: pointer;\n }\n\n /* Handle (The Slider Portion) */\n .slider:before {\n position: absolute;\n content: "";\n height: var(--knob-size);\n width: var(--knob-size);\n left: 0;\n top: calc((var(--track-height) - var(--knob-size)) / 2);\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--knob-size);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n z-index: 2;\n }\n\n .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Input Checked State Styles */\n input:checked + .slider {\n background-color: var(--switch-color);\n }\n\n input:focus + .slider {\n box-shadow: 0 0 1px #2196f3;\n }\n\n input:checked + .slider:before {\n -webkit-transform: translateX(var(--knob-translate));\n -ms-transform: translateX(var(--knob-translate));\n transform: translateX(var(--knob-translate));\n }\n\n input:checked + .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px color-mix(in srgb, var(--switch-color), transparent 80%);\n }\n\n /* Disabled State Styles */\n input:disabled + .slider {\n background-color: #e0e0e0;\n cursor: not-allowed;\n }\n\n input:disabled + .slider:before {\n background-color: #f5f5f5;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled + .slider:hover:before {\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled:checked + .slider {\n background-color: color-mix(in srgb, var(--switch-color), white 50%);\n opacity: 0.8;\n }\n\n /* Label Styles */\n .label {\n font-size: var(--font-size);\n font-weight: 500;\n color: var(--font-color);\n font-family: var(--label-font);\n }\n\n :host([disabled]) .label {\n color: #9e9e9e;\n }\n\n .flex-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 8px;\n }\n</style>\n\n<div class="flex-container">\n <span id="label-span" class="label"></span>\n <label class="switch">\n <input type="checkbox" aria-labelledby="label" />\n <div class="slider"></div>\n </label>\n</div>\n';
6829
+ var wavelength_switch_template_default = '<style>\n :host {\n display: block;\n --font-color: #333;\n --font-size: 20px;\n --font-family: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n --switch-color: #2196f3;\n --label-font: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n\n /* Variable-based sizing */\n --container-width: 60px;\n --container-height: 34px;\n --track-height: 22px;\n --knob-size: 28px;\n\n /* Internal Calculations */\n --track-top: calc((var(--container-height) - var(--track-height)) / 2);\n --knob-top: calc((var(--container-height) - var(--knob-size)) / 2);\n --knob-translate: calc(var(--container-width) - var(--knob-size));\n }\n\n :host([size="small"]) {\n --container-width: 40px;\n --container-height: 24px;\n --track-height: 14px;\n --knob-size: 20px;\n --font-size: 16px;\n }\n\n :host([size="large"]) {\n --container-width: 80px;\n --container-height: 48px;\n --track-height: 30px;\n --knob-size: 40px;\n --font-size: 24px;\n }\n\n /* Switch Base Styles */\n .switch {\n position: relative;\n display: inline-block;\n width: var(--container-width);\n height: var(--container-height);\n }\n\n .switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n /* Slider (The Track) */\n .slider {\n position: absolute;\n cursor: pointer;\n top: var(--track-top);\n left: 0;\n right: 0;\n bottom: var(--track-top);\n background-color: #ccc;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--track-height);\n }\n\n .slider:hover {\n cursor: pointer;\n }\n\n /* Handle (The Slider Portion) */\n .slider:before {\n position: absolute;\n content: "";\n height: var(--knob-size);\n width: var(--knob-size);\n left: 0;\n top: calc((var(--track-height) - var(--knob-size)) / 2);\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--knob-size);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n z-index: 2;\n }\n\n .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Default \u2014 no ripple */\n input[type="range"]::-webkit-slider-thumb {\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);\n transition: box-shadow 0.2s ease;\n }\n\n /* Hover \u2014 small ripple */\n input[type="range"]:hover::-webkit-slider-thumb {\n box-shadow: 0 0 0 8px rgba(26, 111, 196, 0.15);\n }\n\n /* Active (click/hold) \u2014 larger ripple */\n input[type="range"]:active::-webkit-slider-thumb {\n box-shadow: 0 0 0 14px rgba(26, 111, 196, 0.2);\n }\n\n /* Input Checked State Styles */\n input:checked + .slider {\n background-color: var(--switch-color);\n }\n\n input:focus + .slider {\n box-shadow: 0 0 1px #2196f3;\n }\n\n input:checked + .slider:before {\n -webkit-transform: translateX(var(--knob-translate));\n -ms-transform: translateX(var(--knob-translate));\n transform: translateX(var(--knob-translate));\n }\n\n input:checked + .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px color-mix(in srgb, var(--switch-color), transparent 80%);\n }\n\n /* Disabled State Styles */\n input:disabled + .slider {\n background-color: #e0e0e0;\n cursor: not-allowed;\n }\n\n input:disabled + .slider:before {\n background-color: #f5f5f5;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled + .slider:hover:before {\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled:checked + .slider {\n background-color: color-mix(in srgb, var(--switch-color), white 50%);\n opacity: 0.8;\n }\n\n /* Label Styles */\n .label {\n font-size: var(--font-size);\n font-weight: 500;\n color: var(--font-color);\n font-family: var(--label-font);\n }\n\n :host([disabled]) .label {\n color: #9e9e9e;\n }\n\n .flex-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 8px;\n }\n</style>\n\n<div class="flex-container">\n <span id="label-span" class="label"></span>\n <label class="switch">\n <input type="checkbox" aria-labelledby="label" />\n <div class="slider"></div>\n </label>\n</div>\n';
6830
6830
 
6831
6831
  // src/web-components/wavelength-switch.ts
6832
6832
  var WavelengthSwitch = class extends HTMLElement {
@@ -7339,6 +7339,341 @@ if (!customElements.get("wavelength-popup-menu")) {
7339
7339
  customElements.define("wavelength-popup-menu", WavelengthPopUpMenu);
7340
7340
  }
7341
7341
 
7342
+ // src/web-components/wavelength-slider.ts
7343
+ var template6 = `
7344
+ <style>
7345
+ :host {
7346
+ display: block;
7347
+ min-width: 200px;
7348
+ width: 100%;
7349
+ height: auto;
7350
+
7351
+ --selected-color: var(--track-selected-color, #1e90ff);
7352
+ --track-color: color-mix(in srgb, var(--selected-color), transparent 85%);
7353
+ --ripple-color: color-mix(in srgb, var(--selected-color), transparent 85%);
7354
+ --ripple-active-color: color-mix(in srgb, var(--selected-color), transparent 75%);
7355
+
7356
+ --thumb-size: 18px;
7357
+ --slider-width: 100%;
7358
+ }
7359
+
7360
+ /* \u2500\u2500 Container \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7361
+ .container {
7362
+ position: relative;
7363
+ padding: 36px 14px 14px 14px; /* Slightly more padding */
7364
+ width: var(--slider-width);
7365
+ box-sizing: border-box;
7366
+ }
7367
+
7368
+ /* \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7369
+ .tooltip {
7370
+ position: absolute;
7371
+ top: 0;
7372
+ transform: translateX(-50%);
7373
+ background: var(--selected-color);
7374
+ color: #fff;
7375
+ font-size: 11px;
7376
+ padding: 3px 9px;
7377
+ border-radius: 5px;
7378
+ pointer-events: none;
7379
+ white-space: nowrap;
7380
+ letter-spacing: 0.06em;
7381
+ font-family: "Montserrat", sans-serif;
7382
+ z-index: 10;
7383
+ }
7384
+ /* Downward arrow on the tooltip */
7385
+ .tooltip::after {
7386
+ content: "";
7387
+ position: absolute;
7388
+ top: 100%;
7389
+ left: 50%;
7390
+ transform: translateX(-50%);
7391
+ border: 5px solid transparent;
7392
+ border-top-color: var(--selected-color);
7393
+ }
7394
+
7395
+ /* \u2500\u2500 Track \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7396
+ .track {
7397
+ position: relative;
7398
+ height: 6px;
7399
+ border-radius: 999px;
7400
+ background: var(--track-color);
7401
+ width: 100%;
7402
+ display: block;
7403
+ }
7404
+
7405
+ /* \u2500\u2500 Selected portion of the track \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7406
+ .track-selected {
7407
+ position: absolute;
7408
+ left: 0;
7409
+ top: 0;
7410
+ height: 100%;
7411
+ border-radius: 999px;
7412
+ background: var(--selected-color);
7413
+ pointer-events: none;
7414
+ display: block;
7415
+ }
7416
+
7417
+ /* \u2500\u2500 Marks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7418
+ .marks-container {
7419
+ position: absolute;
7420
+ left: 0;
7421
+ top: 0;
7422
+ width: 100%;
7423
+ height: 100%;
7424
+ pointer-events: none;
7425
+ z-index: 2;
7426
+ }
7427
+ .mark {
7428
+ position: absolute;
7429
+ top: 50%;
7430
+ transform: translate(-50%, -50%);
7431
+ width: 4px;
7432
+ height: 4px;
7433
+ background: #fff;
7434
+ border-radius: 50%;
7435
+ opacity: 0.6;
7436
+ }
7437
+
7438
+ /* \u2500\u2500 Mark Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7439
+ .marks-labels {
7440
+ position: relative;
7441
+ width: 100%;
7442
+ margin-top: 10px;
7443
+ height: 14px;
7444
+ pointer-events: none;
7445
+ }
7446
+ .mark-label {
7447
+ position: absolute;
7448
+ transform: translateX(-50%);
7449
+ font-size: 10px;
7450
+ color: var(--selected-color);
7451
+ font-family: "Montserrat", sans-serif;
7452
+ font-weight: 500;
7453
+ white-space: nowrap;
7454
+ opacity: 0.8;
7455
+ }
7456
+
7457
+ /* \u2500\u2500 Native <input> \u2014 reset and stretch over the track \u2500\u2500\u2500 */
7458
+ input[type="range"] {
7459
+ -webkit-appearance: none;
7460
+ appearance: none;
7461
+ position: absolute;
7462
+ left: 0;
7463
+ top: 50%;
7464
+ transform: translateY(-50%);
7465
+ width: 100%;
7466
+ height: 100%;
7467
+ background: transparent;
7468
+ cursor: pointer;
7469
+ margin: 0;
7470
+ padding: 0;
7471
+ z-index: 5;
7472
+ }
7473
+
7474
+ /* \u2500\u2500 Thumb \u2014 WebKit (Chrome, Safari, Edge) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7475
+ input[type="range"]::-webkit-slider-thumb {
7476
+ -webkit-appearance: none;
7477
+ width: var(--thumb-size);
7478
+ height: var(--thumb-size);
7479
+ border-radius: 50%;
7480
+ background: var(--selected-color);
7481
+ border: 2px solid var(--selected-color);
7482
+ box-shadow: 0 0 0 0px var(--ripple-color);
7483
+ transition:
7484
+ box-shadow 0.2s ease,
7485
+ transform 0.15s ease;
7486
+ }
7487
+ input[type="range"]::-webkit-slider-thumb:hover {
7488
+ box-shadow: 0 0 0 8px var(--ripple-color);
7489
+ transform: scale(1.1);
7490
+ }
7491
+ input[type="range"]:active::-webkit-slider-thumb {
7492
+ box-shadow: 0 0 0 14px var(--ripple-active-color);
7493
+ transform: scale(1.15);
7494
+ }
7495
+
7496
+ /* \u2500\u2500 Thumb \u2014 Firefox \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7497
+ input[type="range"]::-moz-range-thumb {
7498
+ width: var(--thumb-size);
7499
+ height: var(--thumb-size);
7500
+ border-radius: 50%;
7501
+ background: var(--selected-color);
7502
+ border: 2px solid var(--selected-color);
7503
+ box-shadow: 0 0 0 0px var(--ripple-color);
7504
+ transition: box-shadow 0.2s ease;
7505
+ cursor: grab;
7506
+ }
7507
+ input[type="range"]::-moz-range-thumb:hover {
7508
+ box-shadow: 0 0 0 8px var(--ripple-color);
7509
+ }
7510
+ input[type="range"]:active::-moz-range-thumb {
7511
+ box-shadow: 0 0 0 14px var(--ripple-active-color);
7512
+ cursor: grabbing;
7513
+ }
7514
+ input[type="range"]::-moz-range-track {
7515
+ background: transparent;
7516
+ }
7517
+ </style>
7518
+
7519
+ <div class="container" id="container">
7520
+ <span class="tooltip" id="tooltip"></span>
7521
+ <div class="track">
7522
+ <div class="marks-container" id="marks-container"></div>
7523
+ <span class="track-selected" id="track-selected"></span>
7524
+ <input type="range" id="slider" />
7525
+ </div>
7526
+ <div class="marks-labels" id="marks-labels"></div>
7527
+ </div>
7528
+ `;
7529
+ var WavelengthSlider = class extends HTMLElement {
7530
+ constructor() {
7531
+ super();
7532
+ this._cachedMarks = [];
7533
+ this.onInput = (e) => {
7534
+ const value = Number(e.target.value);
7535
+ const marks = this._cachedMarks;
7536
+ let finalValue = value;
7537
+ if (marks.length > 0) {
7538
+ const { range } = this.stats;
7539
+ const threshold = range * 0.01;
7540
+ const markValues = marks.map((m) => m.value);
7541
+ const nearest = markValues.reduce((prev, curr) => Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev);
7542
+ if (Math.abs(nearest - value) <= threshold) {
7543
+ finalValue = nearest;
7544
+ }
7545
+ }
7546
+ this.sliderEl.value = finalValue.toString();
7547
+ this._updateVisualState(finalValue);
7548
+ this.dispatchEvent(
7549
+ new CustomEvent("slider-change", {
7550
+ detail: { value: finalValue },
7551
+ bubbles: true,
7552
+ composed: true
7553
+ })
7554
+ );
7555
+ };
7556
+ this.shadow = this.attachShadow({ mode: "open" });
7557
+ this.shadow.innerHTML = template6;
7558
+ }
7559
+ static get observedAttributes() {
7560
+ return ["min-val", "max-val", "step", "value", "value-displayed", "marks", "color", "width"];
7561
+ }
7562
+ connectedCallback() {
7563
+ this._updateCachedMarks();
7564
+ this.sliderEl = this.shadow.querySelector("#slider");
7565
+ this.tooltipEl = this.shadow.querySelector("#tooltip");
7566
+ this.trackSelectedEl = this.shadow.querySelector("#track-selected");
7567
+ this.containerEl = this.shadow.querySelector("#container");
7568
+ this.marksEl = this.shadow.querySelector("#marks-container");
7569
+ this.labelsEl = this.shadow.querySelector("#marks-labels");
7570
+ this.sliderEl.addEventListener("input", this.onInput);
7571
+ this._syncCoreAttributes();
7572
+ if (this.hasAttribute("value")) {
7573
+ this.sliderEl.value = this.getAttribute("value");
7574
+ }
7575
+ this._renderMarks();
7576
+ this.resizeObserver = new ResizeObserver(() => this._updateVisualState(this.value));
7577
+ this.resizeObserver.observe(this.containerEl);
7578
+ }
7579
+ attributeChangedCallback(name, oldValue, newValue) {
7580
+ if (oldValue === newValue || !this.sliderEl) return;
7581
+ if (["min-val", "max-val", "step", "color", "width"].includes(name)) {
7582
+ this._syncCoreAttributes();
7583
+ this._renderMarks();
7584
+ this._updateVisualState(this.value);
7585
+ }
7586
+ if (name === "value") {
7587
+ if (this.sliderEl) this.sliderEl.value = newValue;
7588
+ this._updateVisualState(this.value);
7589
+ }
7590
+ if (name === "marks") {
7591
+ this._updateCachedMarks();
7592
+ this._renderMarks();
7593
+ }
7594
+ }
7595
+ disconnectedCallback() {
7596
+ this.sliderEl.removeEventListener("input", this.onInput);
7597
+ if (this.resizeObserver) this.resizeObserver.disconnect();
7598
+ }
7599
+ get stats() {
7600
+ const min = Number(_optionalChain([this, 'access', _113 => _113.sliderEl, 'optionalAccess', _114 => _114.min]) || this.getAttribute("min-val") || 0);
7601
+ const max = Number(_optionalChain([this, 'access', _115 => _115.sliderEl, 'optionalAccess', _116 => _116.max]) || this.getAttribute("max-val") || 100);
7602
+ const range = max - min;
7603
+ return { min, max, range };
7604
+ }
7605
+ _updateCachedMarks() {
7606
+ const attr = this.getAttribute("marks");
7607
+ if (!attr) {
7608
+ this._cachedMarks = [];
7609
+ return;
7610
+ }
7611
+ try {
7612
+ const parsed = JSON.parse(attr);
7613
+ this._cachedMarks = parsed.map((m) => typeof m === "number" ? { value: m, label: m.toString() } : m);
7614
+ } catch (e2) {
7615
+ this._cachedMarks = [];
7616
+ }
7617
+ }
7618
+ _syncCoreAttributes() {
7619
+ if (!this.sliderEl) return;
7620
+ this.sliderEl.setAttribute("min", _nullishCoalesce(this.getAttribute("min-val"), () => ( "0")));
7621
+ this.sliderEl.setAttribute("max", _nullishCoalesce(this.getAttribute("max-val"), () => ( "100")));
7622
+ this.sliderEl.setAttribute("step", _nullishCoalesce(this.getAttribute("step"), () => ( "1")));
7623
+ const width = this.getAttribute("width");
7624
+ if (width) this.style.setProperty("--slider-width", width);
7625
+ else this.style.removeProperty("--slider-width");
7626
+ const color = this.getAttribute("color");
7627
+ if (color) this.style.setProperty("--track-selected-color", color);
7628
+ else this.style.removeProperty("--track-selected-color");
7629
+ }
7630
+ _renderMarks() {
7631
+ if (!this.marksEl || !this.labelsEl) return;
7632
+ this.marksEl.innerHTML = this.labelsEl.innerHTML = "";
7633
+ const marks = this._cachedMarks;
7634
+ const { min, max, range } = this.stats;
7635
+ marks.forEach(({ value, label: txt }) => {
7636
+ if (value < min || value > max) return;
7637
+ const percent = range > 0 ? (value - min) / range : 0;
7638
+ const pos = this.calculatePosition(percent);
7639
+ const mark = document.createElement("div");
7640
+ mark.className = "mark";
7641
+ mark.style.left = `${pos}px`;
7642
+ this.marksEl.appendChild(mark);
7643
+ const label = document.createElement("div");
7644
+ label.className = "mark-label";
7645
+ label.style.left = `${pos}px`;
7646
+ label.textContent = txt;
7647
+ this.labelsEl.appendChild(label);
7648
+ });
7649
+ }
7650
+ _updateVisualState(value) {
7651
+ if (!this.sliderEl || !this.containerEl) return;
7652
+ const { min, range } = this.stats;
7653
+ const percent = range > 0 ? (value - min) / range : 0;
7654
+ const localPos = this.calculatePosition(percent);
7655
+ const trackOffset = this.sliderEl.parentElement.offsetLeft || 0;
7656
+ this.trackSelectedEl.style.width = `${percent * 100}%`;
7657
+ this.tooltipEl.textContent = this.getAttribute("value-displayed") || String(value);
7658
+ this.tooltipEl.style.left = `${trackOffset + localPos}px`;
7659
+ }
7660
+ get value() {
7661
+ return Number(_nullishCoalesce(_nullishCoalesce(_optionalChain([this, 'access', _117 => _117.sliderEl, 'optionalAccess', _118 => _118.value]), () => ( this.getAttribute("value"))), () => ( 0)));
7662
+ }
7663
+ set value(v) {
7664
+ this.setAttribute("value", String(v));
7665
+ }
7666
+ calculatePosition(percent) {
7667
+ const thumbHalf = 9;
7668
+ const trackWidth = _optionalChain([this, 'access', _119 => _119.sliderEl, 'optionalAccess', _120 => _120.offsetWidth]) || 0;
7669
+ return percent * trackWidth + thumbHalf - percent * (thumbHalf * 2);
7670
+ }
7671
+ };
7672
+ if (!customElements.get("wavelength-slider")) {
7673
+ customElements.define("wavelength-slider", WavelengthSlider);
7674
+ }
7675
+
7676
+
7342
7677
 
7343
7678
 
7344
7679
 
@@ -7368,7 +7703,7 @@ if (!customElements.get("wavelength-popup-menu")) {
7368
7703
 
7369
7704
 
7370
7705
 
7371
- exports.BaseWavelengthInput = BaseWavelengthInput; exports.BaseWavelengthMultiSelectAutocomplete = BaseWavelengthMultiSelectAutocomplete; exports.ChildDataTable = ChildDataTable; exports.SampleComponent = SampleComponent; exports.WavelengthBadge = WavelengthBadge; exports.WavelengthBanner = WavelengthBanner; exports.WavelengthButton = WavelengthButton; exports.WavelengthCheckbox = WavelengthCheckbox; exports.WavelengthConfirmationModal = WavelengthConfirmationModal; exports.WavelengthDatePicker = WavelengthDatePicker; exports.WavelengthDialog = WavelengthDialog; exports.WavelengthDropdown = WavelengthDropdown; exports.WavelengthFileDropZone = WavelengthFileDropZone; exports.WavelengthForm = WavelengthForm; exports.WavelengthInput = WavelengthInput; exports.WavelengthManyPlanes = WavelengthManyPlanes; exports.WavelengthMenu = WavelengthMenu; exports.WavelengthMultiSelectAutocomplete = WavelengthMultiSelectAutocomplete; exports.WavelengthNavBar = WavelengthNavBar; exports.WavelengthNotificationPanel = WavelengthNotificationPanel; exports.WavelengthPagination = WavelengthPagination; exports.WavelengthPlaneTrail = WavelengthPlaneTrail; exports.WavelengthPopUpMenu = WavelengthPopUpMenu; exports.WavelengthProgressBar = WavelengthProgressBar; exports.WavelengthSearch = WavelengthSearch; exports.WavelengthSnackbar = WavelengthSnackbar; exports.WavelengthSwitch = WavelengthSwitch; exports.WavelengthTitleBar = WavelengthTitleBar; exports.WavelengthToolTip = WavelengthToolTip;
7706
+ exports.BaseWavelengthInput = BaseWavelengthInput; exports.BaseWavelengthMultiSelectAutocomplete = BaseWavelengthMultiSelectAutocomplete; exports.ChildDataTable = ChildDataTable; exports.SampleComponent = SampleComponent; exports.WavelengthBadge = WavelengthBadge; exports.WavelengthBanner = WavelengthBanner; exports.WavelengthButton = WavelengthButton; exports.WavelengthCheckbox = WavelengthCheckbox; exports.WavelengthConfirmationModal = WavelengthConfirmationModal; exports.WavelengthDatePicker = WavelengthDatePicker; exports.WavelengthDialog = WavelengthDialog; exports.WavelengthDropdown = WavelengthDropdown; exports.WavelengthFileDropZone = WavelengthFileDropZone; exports.WavelengthForm = WavelengthForm; exports.WavelengthInput = WavelengthInput; exports.WavelengthManyPlanes = WavelengthManyPlanes; exports.WavelengthMenu = WavelengthMenu; exports.WavelengthMultiSelectAutocomplete = WavelengthMultiSelectAutocomplete; exports.WavelengthNavBar = WavelengthNavBar; exports.WavelengthNotificationPanel = WavelengthNotificationPanel; exports.WavelengthPagination = WavelengthPagination; exports.WavelengthPlaneTrail = WavelengthPlaneTrail; exports.WavelengthPopUpMenu = WavelengthPopUpMenu; exports.WavelengthProgressBar = WavelengthProgressBar; exports.WavelengthSearch = WavelengthSearch; exports.WavelengthSlider = WavelengthSlider; exports.WavelengthSnackbar = WavelengthSnackbar; exports.WavelengthSwitch = WavelengthSwitch; exports.WavelengthTitleBar = WavelengthTitleBar; exports.WavelengthToolTip = WavelengthToolTip;
7372
7707
  /*! Bundled license information:
7373
7708
 
7374
7709
  react/cjs/react.production.min.js:
@@ -2824,4 +2824,30 @@ declare class WavelengthPopUpMenu extends WavelengthPopUpMenu_base {
2824
2824
  private _handleOutsideClick;
2825
2825
  }
2826
2826
 
2827
- export { BaseWavelengthInput, BaseWavelengthMultiSelectAutocomplete, type CheckboxElements, ChildDataTable, SampleComponent, WavelengthBadge, WavelengthBanner, WavelengthButton, WavelengthCheckbox, WavelengthConfirmationModal, type WavelengthConfirmationModalElements, WavelengthDatePicker, WavelengthDialog, WavelengthDropdown, WavelengthFileDropZone, WavelengthForm, WavelengthInput, WavelengthManyPlanes, WavelengthMenu, WavelengthMultiSelectAutocomplete, WavelengthNavBar, WavelengthNotificationPanel, WavelengthPagination, WavelengthPlaneTrail, WavelengthPopUpMenu, WavelengthProgressBar, WavelengthSearch, WavelengthSnackbar, WavelengthSwitch, WavelengthTitleBar, WavelengthToolTip };
2827
+ declare class WavelengthSlider extends HTMLElement {
2828
+ static get observedAttributes(): string[];
2829
+ private shadow;
2830
+ private sliderEl;
2831
+ private tooltipEl;
2832
+ private trackSelectedEl;
2833
+ private containerEl;
2834
+ private marksEl;
2835
+ private labelsEl;
2836
+ private resizeObserver;
2837
+ private _cachedMarks;
2838
+ private onInput;
2839
+ constructor();
2840
+ connectedCallback(): void;
2841
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
2842
+ disconnectedCallback(): void;
2843
+ private get stats();
2844
+ private _updateCachedMarks;
2845
+ private _syncCoreAttributes;
2846
+ private _renderMarks;
2847
+ private _updateVisualState;
2848
+ get value(): number;
2849
+ set value(v: number);
2850
+ private calculatePosition;
2851
+ }
2852
+
2853
+ export { BaseWavelengthInput, BaseWavelengthMultiSelectAutocomplete, type CheckboxElements, ChildDataTable, SampleComponent, WavelengthBadge, WavelengthBanner, WavelengthButton, WavelengthCheckbox, WavelengthConfirmationModal, type WavelengthConfirmationModalElements, WavelengthDatePicker, WavelengthDialog, WavelengthDropdown, WavelengthFileDropZone, WavelengthForm, WavelengthInput, WavelengthManyPlanes, WavelengthMenu, WavelengthMultiSelectAutocomplete, WavelengthNavBar, WavelengthNotificationPanel, WavelengthPagination, WavelengthPlaneTrail, WavelengthPopUpMenu, WavelengthProgressBar, WavelengthSearch, WavelengthSlider, WavelengthSnackbar, WavelengthSwitch, WavelengthTitleBar, WavelengthToolTip };
@@ -2824,4 +2824,30 @@ declare class WavelengthPopUpMenu extends WavelengthPopUpMenu_base {
2824
2824
  private _handleOutsideClick;
2825
2825
  }
2826
2826
 
2827
- export { BaseWavelengthInput, BaseWavelengthMultiSelectAutocomplete, type CheckboxElements, ChildDataTable, SampleComponent, WavelengthBadge, WavelengthBanner, WavelengthButton, WavelengthCheckbox, WavelengthConfirmationModal, type WavelengthConfirmationModalElements, WavelengthDatePicker, WavelengthDialog, WavelengthDropdown, WavelengthFileDropZone, WavelengthForm, WavelengthInput, WavelengthManyPlanes, WavelengthMenu, WavelengthMultiSelectAutocomplete, WavelengthNavBar, WavelengthNotificationPanel, WavelengthPagination, WavelengthPlaneTrail, WavelengthPopUpMenu, WavelengthProgressBar, WavelengthSearch, WavelengthSnackbar, WavelengthSwitch, WavelengthTitleBar, WavelengthToolTip };
2827
+ declare class WavelengthSlider extends HTMLElement {
2828
+ static get observedAttributes(): string[];
2829
+ private shadow;
2830
+ private sliderEl;
2831
+ private tooltipEl;
2832
+ private trackSelectedEl;
2833
+ private containerEl;
2834
+ private marksEl;
2835
+ private labelsEl;
2836
+ private resizeObserver;
2837
+ private _cachedMarks;
2838
+ private onInput;
2839
+ constructor();
2840
+ connectedCallback(): void;
2841
+ attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
2842
+ disconnectedCallback(): void;
2843
+ private get stats();
2844
+ private _updateCachedMarks;
2845
+ private _syncCoreAttributes;
2846
+ private _renderMarks;
2847
+ private _updateVisualState;
2848
+ get value(): number;
2849
+ set value(v: number);
2850
+ private calculatePosition;
2851
+ }
2852
+
2853
+ export { BaseWavelengthInput, BaseWavelengthMultiSelectAutocomplete, type CheckboxElements, ChildDataTable, SampleComponent, WavelengthBadge, WavelengthBanner, WavelengthButton, WavelengthCheckbox, WavelengthConfirmationModal, type WavelengthConfirmationModalElements, WavelengthDatePicker, WavelengthDialog, WavelengthDropdown, WavelengthFileDropZone, WavelengthForm, WavelengthInput, WavelengthManyPlanes, WavelengthMenu, WavelengthMultiSelectAutocomplete, WavelengthNavBar, WavelengthNotificationPanel, WavelengthPagination, WavelengthPlaneTrail, WavelengthPopUpMenu, WavelengthProgressBar, WavelengthSearch, WavelengthSlider, WavelengthSnackbar, WavelengthSwitch, WavelengthTitleBar, WavelengthToolTip };
package/dist/esm/index.js CHANGED
@@ -6826,7 +6826,7 @@ if (!customElements.get("wavelength-search")) {
6826
6826
  }
6827
6827
 
6828
6828
  // src/web-components/wavelength-switch.template.html
6829
- var wavelength_switch_template_default = '<style>\n :host {\n display: block;\n --font-color: #333;\n --font-size: 20px;\n --font-family: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n --switch-color: #2196f3;\n --label-font: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n\n /* Variable-based sizing */\n --container-width: 60px;\n --container-height: 34px;\n --track-height: 22px;\n --knob-size: 28px;\n\n /* Internal Calculations */\n --track-top: calc((var(--container-height) - var(--track-height)) / 2);\n --knob-top: calc((var(--container-height) - var(--knob-size)) / 2);\n --knob-translate: calc(var(--container-width) - var(--knob-size));\n }\n\n :host([size="small"]) {\n --container-width: 40px;\n --container-height: 24px;\n --track-height: 14px;\n --knob-size: 20px;\n --font-size: 16px;\n }\n\n :host([size="large"]) {\n --container-width: 80px;\n --container-height: 48px;\n --track-height: 30px;\n --knob-size: 40px;\n --font-size: 24px;\n }\n\n /* Switch Base Styles */\n .switch {\n position: relative;\n display: inline-block;\n width: var(--container-width);\n height: var(--container-height);\n }\n\n .switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n /* Slider (The Track) */\n .slider {\n position: absolute;\n cursor: pointer;\n top: var(--track-top);\n left: 0;\n right: 0;\n bottom: var(--track-top);\n background-color: #ccc;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--track-height);\n }\n\n .slider:hover {\n cursor: pointer;\n }\n\n /* Handle (The Slider Portion) */\n .slider:before {\n position: absolute;\n content: "";\n height: var(--knob-size);\n width: var(--knob-size);\n left: 0;\n top: calc((var(--track-height) - var(--knob-size)) / 2);\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--knob-size);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n z-index: 2;\n }\n\n .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Input Checked State Styles */\n input:checked + .slider {\n background-color: var(--switch-color);\n }\n\n input:focus + .slider {\n box-shadow: 0 0 1px #2196f3;\n }\n\n input:checked + .slider:before {\n -webkit-transform: translateX(var(--knob-translate));\n -ms-transform: translateX(var(--knob-translate));\n transform: translateX(var(--knob-translate));\n }\n\n input:checked + .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px color-mix(in srgb, var(--switch-color), transparent 80%);\n }\n\n /* Disabled State Styles */\n input:disabled + .slider {\n background-color: #e0e0e0;\n cursor: not-allowed;\n }\n\n input:disabled + .slider:before {\n background-color: #f5f5f5;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled + .slider:hover:before {\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled:checked + .slider {\n background-color: color-mix(in srgb, var(--switch-color), white 50%);\n opacity: 0.8;\n }\n\n /* Label Styles */\n .label {\n font-size: var(--font-size);\n font-weight: 500;\n color: var(--font-color);\n font-family: var(--label-font);\n }\n\n :host([disabled]) .label {\n color: #9e9e9e;\n }\n\n .flex-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 8px;\n }\n</style>\n\n<div class="flex-container">\n <span id="label-span" class="label"></span>\n <label class="switch">\n <input type="checkbox" aria-labelledby="label" />\n <div class="slider"></div>\n </label>\n</div>\n';
6829
+ var wavelength_switch_template_default = '<style>\n :host {\n display: block;\n --font-color: #333;\n --font-size: 20px;\n --font-family: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n --switch-color: #2196f3;\n --label-font: "Montserrat", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;\n\n /* Variable-based sizing */\n --container-width: 60px;\n --container-height: 34px;\n --track-height: 22px;\n --knob-size: 28px;\n\n /* Internal Calculations */\n --track-top: calc((var(--container-height) - var(--track-height)) / 2);\n --knob-top: calc((var(--container-height) - var(--knob-size)) / 2);\n --knob-translate: calc(var(--container-width) - var(--knob-size));\n }\n\n :host([size="small"]) {\n --container-width: 40px;\n --container-height: 24px;\n --track-height: 14px;\n --knob-size: 20px;\n --font-size: 16px;\n }\n\n :host([size="large"]) {\n --container-width: 80px;\n --container-height: 48px;\n --track-height: 30px;\n --knob-size: 40px;\n --font-size: 24px;\n }\n\n /* Switch Base Styles */\n .switch {\n position: relative;\n display: inline-block;\n width: var(--container-width);\n height: var(--container-height);\n }\n\n .switch input {\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n /* Slider (The Track) */\n .slider {\n position: absolute;\n cursor: pointer;\n top: var(--track-top);\n left: 0;\n right: 0;\n bottom: var(--track-top);\n background-color: #ccc;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--track-height);\n }\n\n .slider:hover {\n cursor: pointer;\n }\n\n /* Handle (The Slider Portion) */\n .slider:before {\n position: absolute;\n content: "";\n height: var(--knob-size);\n width: var(--knob-size);\n left: 0;\n top: calc((var(--track-height) - var(--knob-size)) / 2);\n background-color: white;\n -webkit-transition: 0.4s;\n transition: 0.4s;\n border-radius: var(--knob-size);\n box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);\n z-index: 2;\n }\n\n .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px rgba(0, 0, 0, 0.1);\n }\n\n /* Default \u2014 no ripple */\n input[type="range"]::-webkit-slider-thumb {\n box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);\n transition: box-shadow 0.2s ease;\n }\n\n /* Hover \u2014 small ripple */\n input[type="range"]:hover::-webkit-slider-thumb {\n box-shadow: 0 0 0 8px rgba(26, 111, 196, 0.15);\n }\n\n /* Active (click/hold) \u2014 larger ripple */\n input[type="range"]:active::-webkit-slider-thumb {\n box-shadow: 0 0 0 14px rgba(26, 111, 196, 0.2);\n }\n\n /* Input Checked State Styles */\n input:checked + .slider {\n background-color: var(--switch-color);\n }\n\n input:focus + .slider {\n box-shadow: 0 0 1px #2196f3;\n }\n\n input:checked + .slider:before {\n -webkit-transform: translateX(var(--knob-translate));\n -ms-transform: translateX(var(--knob-translate));\n transform: translateX(var(--knob-translate));\n }\n\n input:checked + .slider:hover:before {\n box-shadow:\n 0 2px 4px rgba(0, 0, 0, 0.3),\n 0 0 0 8px color-mix(in srgb, var(--switch-color), transparent 80%);\n }\n\n /* Disabled State Styles */\n input:disabled + .slider {\n background-color: #e0e0e0;\n cursor: not-allowed;\n }\n\n input:disabled + .slider:before {\n background-color: #f5f5f5;\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled + .slider:hover:before {\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);\n }\n\n input:disabled:checked + .slider {\n background-color: color-mix(in srgb, var(--switch-color), white 50%);\n opacity: 0.8;\n }\n\n /* Label Styles */\n .label {\n font-size: var(--font-size);\n font-weight: 500;\n color: var(--font-color);\n font-family: var(--label-font);\n }\n\n :host([disabled]) .label {\n color: #9e9e9e;\n }\n\n .flex-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 8px;\n }\n</style>\n\n<div class="flex-container">\n <span id="label-span" class="label"></span>\n <label class="switch">\n <input type="checkbox" aria-labelledby="label" />\n <div class="slider"></div>\n </label>\n</div>\n';
6830
6830
 
6831
6831
  // src/web-components/wavelength-switch.ts
6832
6832
  var WavelengthSwitch = class extends HTMLElement {
@@ -7338,6 +7338,340 @@ var WavelengthPopUpMenu = class extends StylingMixin(HTMLElement) {
7338
7338
  if (!customElements.get("wavelength-popup-menu")) {
7339
7339
  customElements.define("wavelength-popup-menu", WavelengthPopUpMenu);
7340
7340
  }
7341
+
7342
+ // src/web-components/wavelength-slider.ts
7343
+ var template6 = `
7344
+ <style>
7345
+ :host {
7346
+ display: block;
7347
+ min-width: 200px;
7348
+ width: 100%;
7349
+ height: auto;
7350
+
7351
+ --selected-color: var(--track-selected-color, #1e90ff);
7352
+ --track-color: color-mix(in srgb, var(--selected-color), transparent 85%);
7353
+ --ripple-color: color-mix(in srgb, var(--selected-color), transparent 85%);
7354
+ --ripple-active-color: color-mix(in srgb, var(--selected-color), transparent 75%);
7355
+
7356
+ --thumb-size: 18px;
7357
+ --slider-width: 100%;
7358
+ }
7359
+
7360
+ /* \u2500\u2500 Container \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7361
+ .container {
7362
+ position: relative;
7363
+ padding: 36px 14px 14px 14px; /* Slightly more padding */
7364
+ width: var(--slider-width);
7365
+ box-sizing: border-box;
7366
+ }
7367
+
7368
+ /* \u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7369
+ .tooltip {
7370
+ position: absolute;
7371
+ top: 0;
7372
+ transform: translateX(-50%);
7373
+ background: var(--selected-color);
7374
+ color: #fff;
7375
+ font-size: 11px;
7376
+ padding: 3px 9px;
7377
+ border-radius: 5px;
7378
+ pointer-events: none;
7379
+ white-space: nowrap;
7380
+ letter-spacing: 0.06em;
7381
+ font-family: "Montserrat", sans-serif;
7382
+ z-index: 10;
7383
+ }
7384
+ /* Downward arrow on the tooltip */
7385
+ .tooltip::after {
7386
+ content: "";
7387
+ position: absolute;
7388
+ top: 100%;
7389
+ left: 50%;
7390
+ transform: translateX(-50%);
7391
+ border: 5px solid transparent;
7392
+ border-top-color: var(--selected-color);
7393
+ }
7394
+
7395
+ /* \u2500\u2500 Track \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7396
+ .track {
7397
+ position: relative;
7398
+ height: 6px;
7399
+ border-radius: 999px;
7400
+ background: var(--track-color);
7401
+ width: 100%;
7402
+ display: block;
7403
+ }
7404
+
7405
+ /* \u2500\u2500 Selected portion of the track \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7406
+ .track-selected {
7407
+ position: absolute;
7408
+ left: 0;
7409
+ top: 0;
7410
+ height: 100%;
7411
+ border-radius: 999px;
7412
+ background: var(--selected-color);
7413
+ pointer-events: none;
7414
+ display: block;
7415
+ }
7416
+
7417
+ /* \u2500\u2500 Marks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7418
+ .marks-container {
7419
+ position: absolute;
7420
+ left: 0;
7421
+ top: 0;
7422
+ width: 100%;
7423
+ height: 100%;
7424
+ pointer-events: none;
7425
+ z-index: 2;
7426
+ }
7427
+ .mark {
7428
+ position: absolute;
7429
+ top: 50%;
7430
+ transform: translate(-50%, -50%);
7431
+ width: 4px;
7432
+ height: 4px;
7433
+ background: #fff;
7434
+ border-radius: 50%;
7435
+ opacity: 0.6;
7436
+ }
7437
+
7438
+ /* \u2500\u2500 Mark Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7439
+ .marks-labels {
7440
+ position: relative;
7441
+ width: 100%;
7442
+ margin-top: 10px;
7443
+ height: 14px;
7444
+ pointer-events: none;
7445
+ }
7446
+ .mark-label {
7447
+ position: absolute;
7448
+ transform: translateX(-50%);
7449
+ font-size: 10px;
7450
+ color: var(--selected-color);
7451
+ font-family: "Montserrat", sans-serif;
7452
+ font-weight: 500;
7453
+ white-space: nowrap;
7454
+ opacity: 0.8;
7455
+ }
7456
+
7457
+ /* \u2500\u2500 Native <input> \u2014 reset and stretch over the track \u2500\u2500\u2500 */
7458
+ input[type="range"] {
7459
+ -webkit-appearance: none;
7460
+ appearance: none;
7461
+ position: absolute;
7462
+ left: 0;
7463
+ top: 50%;
7464
+ transform: translateY(-50%);
7465
+ width: 100%;
7466
+ height: 100%;
7467
+ background: transparent;
7468
+ cursor: pointer;
7469
+ margin: 0;
7470
+ padding: 0;
7471
+ z-index: 5;
7472
+ }
7473
+
7474
+ /* \u2500\u2500 Thumb \u2014 WebKit (Chrome, Safari, Edge) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7475
+ input[type="range"]::-webkit-slider-thumb {
7476
+ -webkit-appearance: none;
7477
+ width: var(--thumb-size);
7478
+ height: var(--thumb-size);
7479
+ border-radius: 50%;
7480
+ background: var(--selected-color);
7481
+ border: 2px solid var(--selected-color);
7482
+ box-shadow: 0 0 0 0px var(--ripple-color);
7483
+ transition:
7484
+ box-shadow 0.2s ease,
7485
+ transform 0.15s ease;
7486
+ }
7487
+ input[type="range"]::-webkit-slider-thumb:hover {
7488
+ box-shadow: 0 0 0 8px var(--ripple-color);
7489
+ transform: scale(1.1);
7490
+ }
7491
+ input[type="range"]:active::-webkit-slider-thumb {
7492
+ box-shadow: 0 0 0 14px var(--ripple-active-color);
7493
+ transform: scale(1.15);
7494
+ }
7495
+
7496
+ /* \u2500\u2500 Thumb \u2014 Firefox \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
7497
+ input[type="range"]::-moz-range-thumb {
7498
+ width: var(--thumb-size);
7499
+ height: var(--thumb-size);
7500
+ border-radius: 50%;
7501
+ background: var(--selected-color);
7502
+ border: 2px solid var(--selected-color);
7503
+ box-shadow: 0 0 0 0px var(--ripple-color);
7504
+ transition: box-shadow 0.2s ease;
7505
+ cursor: grab;
7506
+ }
7507
+ input[type="range"]::-moz-range-thumb:hover {
7508
+ box-shadow: 0 0 0 8px var(--ripple-color);
7509
+ }
7510
+ input[type="range"]:active::-moz-range-thumb {
7511
+ box-shadow: 0 0 0 14px var(--ripple-active-color);
7512
+ cursor: grabbing;
7513
+ }
7514
+ input[type="range"]::-moz-range-track {
7515
+ background: transparent;
7516
+ }
7517
+ </style>
7518
+
7519
+ <div class="container" id="container">
7520
+ <span class="tooltip" id="tooltip"></span>
7521
+ <div class="track">
7522
+ <div class="marks-container" id="marks-container"></div>
7523
+ <span class="track-selected" id="track-selected"></span>
7524
+ <input type="range" id="slider" />
7525
+ </div>
7526
+ <div class="marks-labels" id="marks-labels"></div>
7527
+ </div>
7528
+ `;
7529
+ var WavelengthSlider = class extends HTMLElement {
7530
+ constructor() {
7531
+ super();
7532
+ this._cachedMarks = [];
7533
+ this.onInput = (e) => {
7534
+ const value = Number(e.target.value);
7535
+ const marks = this._cachedMarks;
7536
+ let finalValue = value;
7537
+ if (marks.length > 0) {
7538
+ const { range } = this.stats;
7539
+ const threshold = range * 0.01;
7540
+ const markValues = marks.map((m) => m.value);
7541
+ const nearest = markValues.reduce((prev, curr) => Math.abs(curr - value) < Math.abs(prev - value) ? curr : prev);
7542
+ if (Math.abs(nearest - value) <= threshold) {
7543
+ finalValue = nearest;
7544
+ }
7545
+ }
7546
+ this.sliderEl.value = finalValue.toString();
7547
+ this._updateVisualState(finalValue);
7548
+ this.dispatchEvent(
7549
+ new CustomEvent("slider-change", {
7550
+ detail: { value: finalValue },
7551
+ bubbles: true,
7552
+ composed: true
7553
+ })
7554
+ );
7555
+ };
7556
+ this.shadow = this.attachShadow({ mode: "open" });
7557
+ this.shadow.innerHTML = template6;
7558
+ }
7559
+ static get observedAttributes() {
7560
+ return ["min-val", "max-val", "step", "value", "value-displayed", "marks", "color", "width"];
7561
+ }
7562
+ connectedCallback() {
7563
+ this._updateCachedMarks();
7564
+ this.sliderEl = this.shadow.querySelector("#slider");
7565
+ this.tooltipEl = this.shadow.querySelector("#tooltip");
7566
+ this.trackSelectedEl = this.shadow.querySelector("#track-selected");
7567
+ this.containerEl = this.shadow.querySelector("#container");
7568
+ this.marksEl = this.shadow.querySelector("#marks-container");
7569
+ this.labelsEl = this.shadow.querySelector("#marks-labels");
7570
+ this.sliderEl.addEventListener("input", this.onInput);
7571
+ this._syncCoreAttributes();
7572
+ if (this.hasAttribute("value")) {
7573
+ this.sliderEl.value = this.getAttribute("value");
7574
+ }
7575
+ this._renderMarks();
7576
+ this.resizeObserver = new ResizeObserver(() => this._updateVisualState(this.value));
7577
+ this.resizeObserver.observe(this.containerEl);
7578
+ }
7579
+ attributeChangedCallback(name, oldValue, newValue) {
7580
+ if (oldValue === newValue || !this.sliderEl) return;
7581
+ if (["min-val", "max-val", "step", "color", "width"].includes(name)) {
7582
+ this._syncCoreAttributes();
7583
+ this._renderMarks();
7584
+ this._updateVisualState(this.value);
7585
+ }
7586
+ if (name === "value") {
7587
+ if (this.sliderEl) this.sliderEl.value = newValue;
7588
+ this._updateVisualState(this.value);
7589
+ }
7590
+ if (name === "marks") {
7591
+ this._updateCachedMarks();
7592
+ this._renderMarks();
7593
+ }
7594
+ }
7595
+ disconnectedCallback() {
7596
+ this.sliderEl.removeEventListener("input", this.onInput);
7597
+ if (this.resizeObserver) this.resizeObserver.disconnect();
7598
+ }
7599
+ get stats() {
7600
+ const min = Number(this.sliderEl?.min || this.getAttribute("min-val") || 0);
7601
+ const max = Number(this.sliderEl?.max || this.getAttribute("max-val") || 100);
7602
+ const range = max - min;
7603
+ return { min, max, range };
7604
+ }
7605
+ _updateCachedMarks() {
7606
+ const attr = this.getAttribute("marks");
7607
+ if (!attr) {
7608
+ this._cachedMarks = [];
7609
+ return;
7610
+ }
7611
+ try {
7612
+ const parsed = JSON.parse(attr);
7613
+ this._cachedMarks = parsed.map((m) => typeof m === "number" ? { value: m, label: m.toString() } : m);
7614
+ } catch {
7615
+ this._cachedMarks = [];
7616
+ }
7617
+ }
7618
+ _syncCoreAttributes() {
7619
+ if (!this.sliderEl) return;
7620
+ this.sliderEl.setAttribute("min", this.getAttribute("min-val") ?? "0");
7621
+ this.sliderEl.setAttribute("max", this.getAttribute("max-val") ?? "100");
7622
+ this.sliderEl.setAttribute("step", this.getAttribute("step") ?? "1");
7623
+ const width = this.getAttribute("width");
7624
+ if (width) this.style.setProperty("--slider-width", width);
7625
+ else this.style.removeProperty("--slider-width");
7626
+ const color = this.getAttribute("color");
7627
+ if (color) this.style.setProperty("--track-selected-color", color);
7628
+ else this.style.removeProperty("--track-selected-color");
7629
+ }
7630
+ _renderMarks() {
7631
+ if (!this.marksEl || !this.labelsEl) return;
7632
+ this.marksEl.innerHTML = this.labelsEl.innerHTML = "";
7633
+ const marks = this._cachedMarks;
7634
+ const { min, max, range } = this.stats;
7635
+ marks.forEach(({ value, label: txt }) => {
7636
+ if (value < min || value > max) return;
7637
+ const percent = range > 0 ? (value - min) / range : 0;
7638
+ const pos = this.calculatePosition(percent);
7639
+ const mark = document.createElement("div");
7640
+ mark.className = "mark";
7641
+ mark.style.left = `${pos}px`;
7642
+ this.marksEl.appendChild(mark);
7643
+ const label = document.createElement("div");
7644
+ label.className = "mark-label";
7645
+ label.style.left = `${pos}px`;
7646
+ label.textContent = txt;
7647
+ this.labelsEl.appendChild(label);
7648
+ });
7649
+ }
7650
+ _updateVisualState(value) {
7651
+ if (!this.sliderEl || !this.containerEl) return;
7652
+ const { min, range } = this.stats;
7653
+ const percent = range > 0 ? (value - min) / range : 0;
7654
+ const localPos = this.calculatePosition(percent);
7655
+ const trackOffset = this.sliderEl.parentElement.offsetLeft || 0;
7656
+ this.trackSelectedEl.style.width = `${percent * 100}%`;
7657
+ this.tooltipEl.textContent = this.getAttribute("value-displayed") || String(value);
7658
+ this.tooltipEl.style.left = `${trackOffset + localPos}px`;
7659
+ }
7660
+ get value() {
7661
+ return Number(this.sliderEl?.value ?? this.getAttribute("value") ?? 0);
7662
+ }
7663
+ set value(v) {
7664
+ this.setAttribute("value", String(v));
7665
+ }
7666
+ calculatePosition(percent) {
7667
+ const thumbHalf = 9;
7668
+ const trackWidth = this.sliderEl?.offsetWidth || 0;
7669
+ return percent * trackWidth + thumbHalf - percent * (thumbHalf * 2);
7670
+ }
7671
+ };
7672
+ if (!customElements.get("wavelength-slider")) {
7673
+ customElements.define("wavelength-slider", WavelengthSlider);
7674
+ }
7341
7675
  export {
7342
7676
  BaseWavelengthInput,
7343
7677
  BaseWavelengthMultiSelectAutocomplete,
@@ -7364,6 +7698,7 @@ export {
7364
7698
  WavelengthPopUpMenu,
7365
7699
  WavelengthProgressBar,
7366
7700
  WavelengthSearch,
7701
+ WavelengthSlider,
7367
7702
  WavelengthSnackbar,
7368
7703
  WavelengthSwitch,
7369
7704
  WavelengthTitleBar,
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@wavelengthusaf/web-components",
3
3
  "author": "563 EWS - Wavelength",
4
4
  "license": "MIT",
5
- "version": "1.17.0",
5
+ "version": "1.18.0",
6
6
  "description": "Common component library used by Wavelength developers (NATIVE WEB COMPONENTS)",
7
7
  "main": "/dist/cjs/index.cjs",
8
8
  "module": "/dist/esm/index.js",