@geoffcox/sterling-svelte 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Callout.svelte CHANGED
@@ -30,7 +30,7 @@ let bodyHeight = 0;
30
30
  let resizeObserver = undefined;
31
31
  // ----- Portal Host ----- //
32
32
  const ensurePortalHost = () => {
33
- if (document) {
33
+ if (globalThis?.document) {
34
34
  if (portalHost) {
35
35
  return portalHost;
36
36
  }
@@ -142,7 +142,7 @@ ensurePortalHost();
142
142
 
143
143
  {#if open || !conditionalRender}
144
144
  <div
145
- use:portal={{ target: portalHost ?? document.body }}
145
+ use:portal={{ target: portalHost ?? globalThis?.document?.body }}
146
146
  class="sterling-callout-portal"
147
147
  transition:fadeMotion|global={{ duration: 250 }}
148
148
  >
package/Checkbox.svelte CHANGED
@@ -1,4 +1,5 @@
1
1
  <script>import { idGenerator } from './idGenerator';
2
+ import { usingKeyboard } from './mediaQueries/usingKeyboard';
2
3
  // ----- Props ----- //
3
4
  export let checked = false;
4
5
  export let disabled = false;
@@ -28,7 +29,12 @@ export const focus = (options) => {
28
29
  @component
29
30
  A styled HTML input type=checkbox element.
30
31
  -->
31
- <div class={`sterling-checkbox ${variant}`} class:disabled>
32
+ <div
33
+ class={`sterling-checkbox ${variant}`}
34
+ class:checked
35
+ class:disabled
36
+ class:using-keyboard={$usingKeyboard}
37
+ >
32
38
  <div class="container">
33
39
  <input
34
40
  bind:this={inputRef}
@@ -36,107 +36,139 @@ let tabListRef;
36
36
  let tabsRef;
37
37
  // ----- Update HSL, RGB, Text ----- //
38
38
  const updateFromRgb = async () => {
39
- if (!updating && (colorFormat === 'hex' || colorFormat === 'rgb')) {
40
- updating = true;
41
- const newAlpha = colorFormat === 'hex' ? hexAlpha / 255 : alpha;
42
- const color = tinycolor({ r: red, g: green, b: blue, a: newAlpha });
43
- const hsl = color.toHsl();
44
- hue = Math.round(hsl.h);
45
- saturation = Math.round(hsl.s * 100);
46
- lightness = Math.round(hsl.l * 100);
47
- switch (colorFormat) {
48
- case 'rgb':
49
- colorText = color.toRgbString();
50
- hexAlpha = Math.round(alpha * 255);
51
- break;
52
- case 'hex':
53
- colorText = alpha === 100 ? color.toHexString() : color.toHex8String();
54
- alpha = round(hexAlpha / 255, 2);
55
- break;
56
- }
57
- await tick();
58
- updating = false;
59
- }
60
- };
61
- const updateFromHsl = async () => {
62
- if (!updating && colorFormat === 'hsl') {
63
- updating = true;
64
- const color = tinycolor({ h: hue, s: saturation / 100, l: lightness / 100, a: alpha });
65
- const rgb = color.toRgb();
66
- red = rgb.r;
67
- green = rgb.g;
68
- blue = rgb.b;
69
- colorText = color.toHslString();
70
- hexAlpha = Math.round(alpha * 255);
71
- await tick();
72
- updating = false;
73
- }
74
- };
75
- const updateColorsFromText = async () => {
76
- const color = tinycolor(colorText);
77
- if (color.isValid) {
78
- if (!updating) {
39
+ // tinycolor requires window
40
+ if (globalThis.window) {
41
+ if (!updating && (colorFormat === 'hex' || colorFormat === 'rgb')) {
79
42
  updating = true;
80
- switch (color.format) {
81
- case 'hsl':
82
- colorFormat = 'hsl';
83
- break;
43
+ const newAlpha = colorFormat === 'hex' ? hexAlpha / 255 : alpha;
44
+ const color = tinycolor({ r: red, g: green, b: blue, a: newAlpha });
45
+ const hsl = color.toHsl();
46
+ hue = Math.round(hsl.h);
47
+ saturation = Math.round(hsl.s * 100);
48
+ lightness = Math.round(hsl.l * 100);
49
+ switch (colorFormat) {
84
50
  case 'rgb':
85
- colorFormat = 'rgb';
51
+ colorText = color.toRgbString();
52
+ hexAlpha = Math.round(alpha * 255);
86
53
  break;
87
54
  case 'hex':
88
- case 'hex4':
89
- case 'hex8':
90
- colorFormat = 'hex';
91
- break;
92
- default:
55
+ colorText = alpha === 100 ? color.toHexString() : color.toHex8String();
56
+ alpha = round(hexAlpha / 255, 2);
93
57
  break;
94
58
  }
95
- const hsl = color.toHsl();
96
- hue = Math.round(hsl.h);
97
- saturation = Math.round(hsl.s * 100);
98
- lightness = Math.round(hsl.l * 100);
59
+ await tick();
60
+ updating = false;
61
+ }
62
+ }
63
+ };
64
+ const updateFromHsl = async () => {
65
+ // tinycolor requires window
66
+ if (globalThis.window) {
67
+ if (!updating && colorFormat === 'hsl') {
68
+ updating = true;
69
+ const color = tinycolor({ h: hue, s: saturation / 100, l: lightness / 100, a: alpha });
99
70
  const rgb = color.toRgb();
100
71
  red = rgb.r;
101
72
  green = rgb.g;
102
73
  blue = rgb.b;
103
- if (rgb.a) {
104
- alpha = hsl.a;
105
- hexAlpha = Math.round(alpha * 255);
106
- }
74
+ colorText = color.toHslString();
75
+ hexAlpha = Math.round(alpha * 255);
107
76
  await tick();
108
77
  updating = false;
109
78
  }
110
79
  }
111
80
  };
112
- $: colorText, updateColorsFromText();
113
- $: colorFormat, hue, saturation, lightness, alpha, updateFromHsl();
114
- $: colorFormat, red, green, blue, alpha, hexAlpha, updateFromRgb();
115
- // ----- Event handlers ----- //
116
- const onInputBlur = async () => {
117
- if (!updating) {
118
- if (colorText.trim().length === 0) {
119
- colorText = defaultColorText;
120
- return;
121
- }
81
+ const updateColorsFromText = async () => {
82
+ // tinycolor requires window
83
+ if (globalThis.window) {
122
84
  const color = tinycolor(colorText);
123
85
  if (color.isValid) {
124
- updating = true;
125
- switch (colorFormat) {
126
- case 'hsl':
127
- colorText = color.toHslString();
128
- break;
129
- case 'rgb':
130
- colorText = color.toRgbString();
131
- break;
132
- case 'hex':
133
- colorText = alpha === 1 ? color.toHexString() : color.toHex8String();
134
- break;
135
- default:
136
- break;
86
+ if (!updating) {
87
+ updating = true;
88
+ switch (color.format) {
89
+ case 'hsl':
90
+ colorFormat = 'hsl';
91
+ break;
92
+ case 'rgb':
93
+ colorFormat = 'rgb';
94
+ break;
95
+ case 'hex':
96
+ case 'hex4':
97
+ case 'hex8':
98
+ colorFormat = 'hex';
99
+ break;
100
+ default:
101
+ break;
102
+ }
103
+ const hsl = color.toHsl();
104
+ hue = Math.round(hsl.h);
105
+ saturation = Math.round(hsl.s * 100);
106
+ lightness = Math.round(hsl.l * 100);
107
+ const rgb = color.toRgb();
108
+ red = rgb.r;
109
+ green = rgb.g;
110
+ blue = rgb.b;
111
+ if (rgb.a) {
112
+ alpha = hsl.a;
113
+ hexAlpha = Math.round(alpha * 255);
114
+ }
115
+ await tick();
116
+ updating = false;
117
+ }
118
+ }
119
+ }
120
+ };
121
+ $: {
122
+ globalThis.window;
123
+ colorText;
124
+ updateColorsFromText();
125
+ }
126
+ $: {
127
+ globalThis.window;
128
+ colorFormat;
129
+ hue;
130
+ saturation;
131
+ lightness;
132
+ alpha;
133
+ updateFromHsl();
134
+ }
135
+ $: {
136
+ globalThis.window;
137
+ colorFormat;
138
+ red;
139
+ green;
140
+ blue;
141
+ alpha;
142
+ hexAlpha;
143
+ updateFromRgb();
144
+ }
145
+ // ----- Event handlers ----- //
146
+ const onInputBlur = async () => {
147
+ if (globalThis.window && tinycolor) {
148
+ if (!updating) {
149
+ if (colorText.trim().length === 0) {
150
+ colorText = defaultColorText;
151
+ return;
152
+ }
153
+ const color = tinycolor(colorText);
154
+ if (color.isValid) {
155
+ updating = true;
156
+ switch (colorFormat) {
157
+ case 'hsl':
158
+ colorText = color.toHslString();
159
+ break;
160
+ case 'rgb':
161
+ colorText = color.toRgbString();
162
+ break;
163
+ case 'hex':
164
+ colorText = alpha === 1 ? color.toHexString() : color.toHex8String();
165
+ break;
166
+ default:
167
+ break;
168
+ }
169
+ await tick();
170
+ updating = false;
137
171
  }
138
- await tick();
139
- updating = false;
140
172
  }
141
173
  }
142
174
  };
package/Input.svelte CHANGED
@@ -44,7 +44,7 @@ export const setRangeText = (replacement, start, end, selectionMode) => {
44
44
  <slot {disabled} {value} {variant} />
45
45
  </label>
46
46
  {/if}
47
- <div class={`sterling-input ${variant}`} class:disabled>
47
+ <div class={`sterling-input ${variant}`} class:disabled class:using-keyboard={$usingKeyboard}>
48
48
  <input
49
49
  bind:this={inputRef}
50
50
  class:using-keyboard={$usingKeyboard}
package/Label.svelte CHANGED
@@ -1,4 +1,5 @@
1
- <script>import Tooltip from './Tooltip.svelte';
1
+ <script>import { onMount } from 'svelte';
2
+ import Tooltip from './Tooltip.svelte';
2
3
  import { usingKeyboard } from './mediaQueries/usingKeyboard';
3
4
  // ----- Props ----- //
4
5
  let htmlFor = undefined;
@@ -82,11 +83,15 @@ const mutationCallback = (mutations) => {
82
83
  targetDisabled = disabled;
83
84
  }
84
85
  };
85
- let mutationObserver = new MutationObserver(mutationCallback);
86
+ let mutationObserver;
87
+ onMount(() => {
88
+ mutationObserver = new MutationObserver(mutationCallback);
89
+ return () => mutationObserver?.disconnect();
90
+ });
86
91
  $: {
87
- mutationObserver.disconnect();
92
+ mutationObserver?.disconnect();
88
93
  if (targetRef) {
89
- mutationObserver.observe(targetRef, {
94
+ mutationObserver?.observe(targetRef, {
90
95
  attributes: true
91
96
  });
92
97
  }
package/Popover.svelte CHANGED
@@ -27,7 +27,7 @@ let bodyHeight = 0;
27
27
  let resizeObserver = undefined;
28
28
  // ----- Portal Host ----- //
29
29
  const ensurePortalHost = () => {
30
- if (document) {
30
+ if (globalThis?.document) {
31
31
  if (portalHost) {
32
32
  return portalHost;
33
33
  }
@@ -87,7 +87,10 @@ ensurePortalHost();
87
87
  </script>
88
88
 
89
89
  {#if open || !conditionalRender}
90
- <div use:portal={{ target: portalHost ?? document.body }} class="sterling-popover-portal">
90
+ <div
91
+ use:portal={{ target: portalHost ?? globalThis?.document?.body }}
92
+ class="sterling-popover-portal"
93
+ >
91
94
  <!-- svelte-ignore a11y-no-static-element-interactions -->
92
95
  <div
93
96
  bind:this={popupRef}
package/Radio.svelte CHANGED
@@ -1,4 +1,5 @@
1
1
  <script>import { idGenerator } from './idGenerator';
2
+ import { usingKeyboard } from './mediaQueries/usingKeyboard';
2
3
  // ----- Props ----- //
3
4
  export let checked = false;
4
5
  export let disabled = false;
@@ -73,7 +74,12 @@ const onChange = (e) => {
73
74
  @component
74
75
  A styled HTML input type=radio element with optional label.
75
76
  -->
76
- <div class={`sterling-radio ${variant}`} class:disabled>
77
+ <div
78
+ class={`sterling-radio ${variant}`}
79
+ class:checked
80
+ class:disabled
81
+ class:using-keyboard={$usingKeyboard}
82
+ >
77
83
  <div class="container">
78
84
  <input
79
85
  bind:this={inputRef}
package/Switch.svelte CHANGED
@@ -1,4 +1,5 @@
1
1
  <script>import { idGenerator } from './idGenerator';
2
+ import { usingKeyboard } from './mediaQueries/usingKeyboard';
2
3
  // ----- Props ----- //
3
4
  export let checked = false;
4
5
  export let disabled = false;
@@ -39,7 +40,13 @@ export const focus = (options) => {
39
40
  @component
40
41
  A styled HTML input type=checkbox element.
41
42
  -->
42
- <div class={`sterling-switch ${variant}`} class:disabled class:vertical>
43
+ <div
44
+ class={`sterling-switch ${variant}`}
45
+ class:checked
46
+ class:disabled
47
+ class:vertical
48
+ class:using-keyboard={$usingKeyboard}
49
+ >
43
50
  <input
44
51
  bind:this={inputRef}
45
52
  bind:checked
@@ -0,0 +1,113 @@
1
+ .sterling-radio.button {
2
+ align-content: center;
3
+ align-items: center;
4
+ background-color: var(--stsv-button__background-color);
5
+ border-color: var(--stsv-button__border-color);
6
+ border-radius: 0;
7
+ border-style: solid;
8
+ border-width: 2px;
9
+ box-sizing: border-box;
10
+ color: var(--stsv-button__color);
11
+ cursor: pointer;
12
+ display: inline-flex;
13
+ flex-direction: row;
14
+ font: inherit;
15
+ justify-content: center;
16
+ justify-items: center;
17
+ margin-left: -2px;
18
+ overflow: hidden;
19
+ padding: 0.5em 1em;
20
+ position: relative;
21
+ text-decoration: none;
22
+ text-overflow: ellipsis;
23
+ transition: background-color 250ms, color 250ms, border-color 250ms;
24
+ white-space: nowrap;
25
+ user-select: none;
26
+ }
27
+
28
+ /* .sterling-radio.button:first-child {
29
+ border-top-left-radius: 8px;
30
+ border-bottom-left-radius: 8px;
31
+ border-left-width: 2px;
32
+ }
33
+
34
+ .sterling-radio.button:last-child {
35
+ border-top-right-radius: 8px;
36
+ border-bottom-right-radius: 8px;
37
+ } */
38
+
39
+ .sterling-radio.button:not(.checked):not(.disabled):hover {
40
+ background-color: var(--stsv-button__background-color--hover);
41
+ border-color: var(--stsv-button__border-color--hover);
42
+ color: var(--stsv-button__color--hover);
43
+ }
44
+
45
+ .sterling-radio.button.checked {
46
+ background-color: var(--stsv-button__background-color--active);
47
+ border-color: var(--stsv-button__border-color--active);
48
+ color: var(--stsv-button__color--active);
49
+ }
50
+
51
+ .sterling-radio.button.using-keyboard:focus-within {
52
+ border-color: var(--stsv-button__border-color--focus);
53
+ outline-color: var(--stsv-common__outline-color);
54
+ outline-offset: 0;
55
+ outline-style: solid;
56
+ outline-width: 2px;
57
+ z-index: 1;
58
+ }
59
+
60
+ .sterling-radio.button .container {
61
+ margin-right: 0;
62
+ }
63
+
64
+ .sterling-radio.button input {
65
+ height: 0;
66
+ width: 0;
67
+ }
68
+
69
+ .sterling-radio.button.disabled * {
70
+ cursor: not-allowed;
71
+ }
72
+
73
+ .sterling-radio.button.disabled::after {
74
+ content: '';
75
+ position: absolute;
76
+ left: 0;
77
+ right: 0;
78
+ top: 0;
79
+ bottom: 0;
80
+ background: repeating-linear-gradient(
81
+ var(--stsv-common--disabled__stripe-angle),
82
+ var(--stsv-common--disabled__stripe-color),
83
+ var(--stsv-common--disabled__stripe-color) var(--stsv-common--disabled__stripe-width),
84
+ var(--stsv-common--disabled__stripe-color--alt) var(--stsv-common--disabled__stripe-width),
85
+ var(--stsv-common--disabled__stripe-color--alt)
86
+ calc(2 * var(--stsv-common--disabled__stripe-width))
87
+ );
88
+ pointer-events: none;
89
+ }
90
+
91
+ .sterling-radio.button .indicator,
92
+ .sterling-radio.button input:checked + .indicator,
93
+ .sterling-radio.button:not(.disabled):hover .indicator,
94
+ .sterling-radio.button input:focus-visible + .indicator {
95
+ display: none;
96
+ }
97
+
98
+ .sterling-radio.button .indicator::after,
99
+ .sterling-radio.button input:checked + .indicator::after {
100
+ display: none;
101
+ }
102
+ .sterling-radio.button .container::after,
103
+ .sterling-radio.button.disabled .container::after {
104
+ display: none;
105
+ opacity: 0;
106
+ }
107
+
108
+ @media (prefers-reduced-motion) {
109
+ .sterling-radio.button,
110
+ .sterling-radio.button::after {
111
+ transition: none;
112
+ }
113
+ }
package/css/Radio.css CHANGED
@@ -1,2 +1,3 @@
1
1
  @import url('./Radio.base.css');
2
2
  @import url('./Radio.colorful.css');
3
+ @import url('./Radio.button.css');
package/css/Tab.base.css CHANGED
@@ -36,6 +36,7 @@
36
36
  justify-content: flex-end;
37
37
  justify-items: flex-end;
38
38
  column-gap: 0.15em;
39
+ padding: 0.25em 1em;
39
40
  }
40
41
 
41
42
  .sterling-tab.using-keyboard:focus-visible {
@@ -82,10 +83,6 @@
82
83
  padding: 0 0.2em;
83
84
  }
84
85
 
85
- .sterling-tab.vertical .content .text {
86
- padding-top: 0.25em;
87
- }
88
-
89
86
  /* ----- indicator -----*/
90
87
 
91
88
  .sterling-tab .indicator {
@@ -1,10 +1,14 @@
1
+ import { onMount } from 'svelte';
1
2
  import { writable } from 'svelte/store';
2
3
  export const prefersColorSchemeDark = writable(false, (set) => {
3
- const matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
4
- set(matchMedia.matches);
4
+ let matchMedia = undefined;
5
5
  const mediaChangeHandler = (e) => set(e.matches);
6
- matchMedia.addEventListener('change', mediaChangeHandler);
6
+ onMount(() => {
7
+ matchMedia = window.matchMedia('(prefers-color-scheme: dark)');
8
+ set(matchMedia.matches);
9
+ matchMedia.addEventListener('change', mediaChangeHandler);
10
+ });
7
11
  return () => {
8
- matchMedia.removeEventListener('change', mediaChangeHandler);
12
+ matchMedia?.removeEventListener('change', mediaChangeHandler);
9
13
  };
10
14
  });
@@ -1,10 +1,14 @@
1
+ import { onMount } from 'svelte';
1
2
  import { writable } from 'svelte/store';
2
3
  export const prefersReducedMotion = writable(false, (set) => {
3
- const matchMedia = window.matchMedia('(prefers-reduced-motion: reduce)');
4
- set(matchMedia.matches);
4
+ let matchMedia = undefined;
5
5
  const mediaChangeHandler = (e) => set(e.matches);
6
- matchMedia.addEventListener('change', mediaChangeHandler);
6
+ onMount(() => {
7
+ matchMedia = window.matchMedia('(prefers-reduced-motion: reduce)');
8
+ set(matchMedia.matches);
9
+ matchMedia.addEventListener('change', mediaChangeHandler);
10
+ });
7
11
  return () => {
8
- matchMedia.removeEventListener('change', mediaChangeHandler);
12
+ matchMedia?.removeEventListener('change', mediaChangeHandler);
9
13
  };
10
14
  });
@@ -1,13 +1,17 @@
1
- import { createKeyborg } from 'keyborg';
1
+ import { onMount } from 'svelte';
2
2
  import { writable } from 'svelte/store';
3
+ import { createKeyborg } from 'keyborg';
3
4
  export const usingKeyboard = writable(false, (set) => {
4
- let keyborg = createKeyborg(window);
5
- set(keyborg.isNavigatingWithKeyboard());
5
+ let keyborg = undefined;
6
6
  const keyborgHandler = (value) => {
7
7
  set(value);
8
8
  };
9
- keyborg.subscribe(keyborgHandler);
9
+ onMount(() => {
10
+ keyborg = createKeyborg(window);
11
+ set(keyborg.isNavigatingWithKeyboard());
12
+ keyborg.subscribe(keyborgHandler);
13
+ });
10
14
  return () => {
11
- keyborg.unsubscribe(keyborgHandler);
15
+ keyborg?.unsubscribe(keyborgHandler);
12
16
  };
13
17
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geoffcox/sterling-svelte",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "author": "Geoff Cox",
5
5
  "description": "A modern, accessible, and lightweight component library for Svelte.",
6
6
  "license": "MIT",
@@ -78,6 +78,7 @@
78
78
  },
79
79
  "exports": {
80
80
  "./package.json": "./package.json",
81
+ "./.DS_Store": "./.DS_Store",
81
82
  "./Button.constants": "./Button.constants.js",
82
83
  "./Button.svelte": "./Button.svelte",
83
84
  "./Button.types": "./Button.types.js",
@@ -204,6 +205,7 @@
204
205
  "./css/Progress.base.css": "./css/Progress.base.css",
205
206
  "./css/Progress.css": "./css/Progress.css",
206
207
  "./css/Radio.base.css": "./css/Radio.base.css",
208
+ "./css/Radio.button.css": "./css/Radio.button.css",
207
209
  "./css/Radio.colorful.css": "./css/Radio.colorful.css",
208
210
  "./css/Radio.css": "./css/Radio.css",
209
211
  "./css/RgbColorSliders.base.css": "./css/RgbColorSliders.base.css",