@geoffcox/sterling-svelte 0.0.17 → 0.0.19

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/Checkbox.svelte CHANGED
@@ -2,7 +2,12 @@
2
2
  import Label from "./Label.svelte";
3
3
  export let checked = false;
4
4
  export let disabled = false;
5
- const inputId = uuid();
5
+ export let id = void 0;
6
+ $: {
7
+ if ($$slots.default && id === void 0) {
8
+ id = uuid();
9
+ }
10
+ }
6
11
  </script>
7
12
 
8
13
  <!--
@@ -12,6 +17,8 @@ const inputId = uuid();
12
17
  <div class="sterling-checkbox">
13
18
  <div class="container">
14
19
  <input
20
+ {disabled}
21
+ {id}
15
22
  type="checkbox"
16
23
  on:blur
17
24
  on:click
@@ -35,15 +42,13 @@ const inputId = uuid();
35
42
  on:wheel
36
43
  bind:checked
37
44
  {...$$restProps}
38
- id={inputId}
39
- {disabled}
40
45
  />
41
46
  <div class="indicator" />
42
47
  </div>
43
- {#if $$slots.label}
48
+ {#if $$slots.default}
44
49
  <div class="label">
45
- <Label {disabled} for={inputId}>
46
- <slot name="label" {checked} {disabled} />
50
+ <Label {disabled} for={id}>
51
+ <slot {checked} {disabled} />
47
52
  </Label>
48
53
  </div>
49
54
  {/if}
@@ -4,6 +4,7 @@ declare const __propDef: {
4
4
  [x: string]: any;
5
5
  checked?: boolean | undefined;
6
6
  disabled?: boolean | undefined;
7
+ id?: string | undefined;
7
8
  };
8
9
  events: {
9
10
  blur: FocusEvent;
@@ -30,7 +31,7 @@ declare const __propDef: {
30
31
  [evt: string]: CustomEvent<any>;
31
32
  };
32
33
  slots: {
33
- label: {
34
+ default: {
34
35
  checked: boolean;
35
36
  disabled: boolean;
36
37
  };
@@ -0,0 +1,241 @@
1
+ <script>import { computePosition, flip, offset, shift, autoUpdate } from "@floating-ui/dom";
2
+ import { createEventDispatcher, onMount, tick } from "svelte";
3
+ import { v4 as uuid } from "uuid";
4
+ import { clickOutside } from "./actions/clickOutside";
5
+ const popupId = uuid();
6
+ export let composed = false;
7
+ export let disabled = false;
8
+ export let open = false;
9
+ export let stayOpenOnClickAway = false;
10
+ let dropdownRef;
11
+ let popupRef;
12
+ let popupPosition = {
13
+ x: void 0,
14
+ y: void 0
15
+ };
16
+ const dispatch = createEventDispatcher();
17
+ const raiseOpen = (open2) => {
18
+ dispatch("open", { open: open2 });
19
+ };
20
+ $: {
21
+ raiseOpen(open);
22
+ }
23
+ let mounted = false;
24
+ onMount(() => {
25
+ mounted = true;
26
+ const cleanup = autoUpdate(dropdownRef, popupRef, async () => {
27
+ const { x, y } = await computePosition(dropdownRef, popupRef, {
28
+ placement: "bottom-end",
29
+ middleware: [offset({ mainAxis: 2 }), flip(), shift({ padding: 0 })]
30
+ });
31
+ if (open) {
32
+ popupPosition = { x, y };
33
+ }
34
+ });
35
+ return cleanup;
36
+ });
37
+ const onClick = (event) => {
38
+ if (!disabled && mounted) {
39
+ const targetNode = event.target;
40
+ const withinPopup = popupRef?.contains(targetNode);
41
+ if (!withinPopup) {
42
+ open = !open;
43
+ event.preventDefault();
44
+ event.stopPropagation();
45
+ }
46
+ }
47
+ };
48
+ const onKeydown = (event) => {
49
+ if (!event.altKey && !event.ctrlKey && !event.shiftKey) {
50
+ switch (event.key) {
51
+ case "Escape":
52
+ open = false;
53
+ event.preventDefault();
54
+ return false;
55
+ }
56
+ }
57
+ };
58
+ const onClickOutside = (event) => {
59
+ if (!stayOpenOnClickAway) {
60
+ open = false;
61
+ }
62
+ };
63
+ </script>
64
+
65
+ <div
66
+ bind:this={dropdownRef}
67
+ aria-controls={popupId}
68
+ aria-haspopup={true}
69
+ aria-expanded={open}
70
+ class="sterling-dropdown"
71
+ class:composed
72
+ class:disabled
73
+ role="combobox"
74
+ tabindex="0"
75
+ use:clickOutside
76
+ on:blur
77
+ on:click
78
+ on:click={onClick}
79
+ on:copy
80
+ on:cut
81
+ on:dblclick
82
+ on:focus
83
+ on:focusin
84
+ on:focusout
85
+ on:keydown
86
+ on:keydown={onKeydown}
87
+ on:keypress
88
+ on:keyup
89
+ on:mousedown
90
+ on:mouseenter
91
+ on:mouseleave
92
+ on:mousemove
93
+ on:mouseover
94
+ on:mouseout
95
+ on:mouseup
96
+ on:wheel
97
+ on:paste
98
+ on:click_outside={onClickOutside}
99
+ {...$$restProps}
100
+ >
101
+ <slot name="value" {composed} {disabled} {open} />
102
+ <slot name="button" {composed} {disabled} {open}>
103
+ <div class="button">
104
+ <div class="chevron" />
105
+ </div>
106
+ </slot>
107
+
108
+ <div
109
+ bind:this={popupRef}
110
+ class="popup"
111
+ class:open
112
+ id={popupId}
113
+ style="left:{popupPosition.x}px; top:{popupPosition.y}px"
114
+ >
115
+ <slot {composed} {disabled} {open} />
116
+ </div>
117
+ </div>
118
+
119
+ <style>
120
+ .sterling-dropdown {
121
+ align-content: stretch;
122
+ align-items: stretch;
123
+ background-color: var(--stsv-Input__background-color);
124
+ border-color: var(--stsv-Input__border-color);
125
+ border-radius: var(--stsv-Input__border-radius);
126
+ border-style: var(--stsv-Input__border-style);
127
+ border-width: var(--stsv-Input__border-width);
128
+ color: var(--stsv-Input__color);
129
+ display: grid;
130
+ grid-template-columns: 1fr 2em;
131
+ grid-template-rows: auto;
132
+ outline: none;
133
+ padding: 0;
134
+ transition: background-color 250ms, color 250ms, border-color 250ms;
135
+ }
136
+
137
+ .sterling-dropdown:hover {
138
+ background-color: var(--stsv-Input__background-color--hover);
139
+ border-color: var(--stsv-Input__border-color--hover);
140
+ color: var(--stsv-Input__color--hover);
141
+ }
142
+
143
+ .sterling-dropdown:focus {
144
+ background-color: var(--stsv-Input__background-color--focus);
145
+ border-color: var(--stsv-Input__border-color--focus);
146
+ color: var(--stsv-Input__color--focus);
147
+ outline-color: var(--stsv-Common__outline-color);
148
+ outline-offset: var(--stsv-Common__outline-offset);
149
+ outline-style: var(--stsv-Common__outline-style);
150
+ outline-width: var(--stsv-Common__outline-width);
151
+ }
152
+
153
+ .sterling-dropdown.disabled {
154
+ background-color: var(--stsv-Common__background-color--disabled);
155
+ border-color: var(--stsv--Common__border-color--disabled);
156
+ color: var(--stsv-Common__color--disabled);
157
+ cursor: not-allowed;
158
+ outline: none;
159
+ }
160
+
161
+ .sterling-dropdown.composed,
162
+ .sterling-dropdown.composed:hover,
163
+ .sterling-dropdown.composed.focus,
164
+ .sterling-dropdown.composed.disabled {
165
+ background: none;
166
+ border: none;
167
+ outline: none;
168
+ }
169
+
170
+ .button {
171
+ grid-column-start: 2;
172
+ grid-row-start: 1;
173
+ cursor: pointer;
174
+ }
175
+
176
+ .chevron {
177
+ display: block;
178
+ position: relative;
179
+ border: none;
180
+ background: none;
181
+ margin: 0;
182
+ height: 100%;
183
+ width: 32px;
184
+ }
185
+
186
+ .chevron::after {
187
+ position: absolute;
188
+ content: '';
189
+ top: 50%;
190
+ left: 50%;
191
+ width: 7px;
192
+ height: 7px;
193
+ border-right: 3px solid currentColor;
194
+ border-top: 3px solid currentColor;
195
+ /*
196
+ The chevron is a right triangle, rotated to face down.
197
+ It should be moved up so it is centered vertically after rotation.
198
+ The amount to move is the hypotenuse of the right triangle of the chevron.
199
+ For a right triangle with equal a and b where c=1
200
+ a^2 + b^2 = c^2
201
+ a^2 + a^2 = c^2
202
+ 2a^2 = c^2
203
+ 2a^2 = 1
204
+ a^2 = 0.5
205
+ a = sqrt(0.5)
206
+ a = 0.707
207
+ */
208
+ transform: translate(-50%, calc(-50% / 0.707)) rotate(135deg);
209
+ transform-origin: 50% 50%;
210
+ }
211
+
212
+ .popup {
213
+ background-color: var(--stsv-Common__background-color);
214
+ border-color: var(--stsv-Common__border-color);
215
+ border-radius: var(--stsv-Common__border-radius);
216
+ border-style: var(--stsv-Common__border-style);
217
+ border-width: var(--stsv-Common__border-width);
218
+ box-sizing: border-box;
219
+ color: var(--stsv-Common__color);
220
+ display: none;
221
+ overflow: visible;
222
+ outline: none;
223
+ position: absolute;
224
+ box-shadow: rgba(0, 0, 0, 0.4) 2px 2px 4px -1px;
225
+ width: fit-content;
226
+ height: fit-content;
227
+ z-index: 1;
228
+ }
229
+
230
+ .popup.open {
231
+ display: grid;
232
+ grid-template-columns: 1fr;
233
+ grid-template-rows: 1fr;
234
+ }
235
+
236
+ @media (prefers-reduced-motion) {
237
+ .sterling-dropdown {
238
+ transition: none;
239
+ }
240
+ }
241
+ </style>
@@ -0,0 +1,58 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ [x: string]: any;
5
+ composed?: boolean | undefined;
6
+ disabled?: boolean | undefined;
7
+ open?: boolean | undefined;
8
+ stayOpenOnClickAway?: boolean | undefined;
9
+ };
10
+ events: {
11
+ blur: FocusEvent;
12
+ click: MouseEvent;
13
+ copy: ClipboardEvent;
14
+ cut: ClipboardEvent;
15
+ dblclick: MouseEvent;
16
+ focus: FocusEvent;
17
+ focusin: FocusEvent;
18
+ focusout: FocusEvent;
19
+ keydown: KeyboardEvent;
20
+ keypress: KeyboardEvent;
21
+ keyup: KeyboardEvent;
22
+ mousedown: MouseEvent;
23
+ mouseenter: MouseEvent;
24
+ mouseleave: MouseEvent;
25
+ mousemove: MouseEvent;
26
+ mouseover: MouseEvent;
27
+ mouseout: MouseEvent;
28
+ mouseup: MouseEvent;
29
+ wheel: WheelEvent;
30
+ paste: ClipboardEvent;
31
+ open: CustomEvent<any>;
32
+ } & {
33
+ [evt: string]: CustomEvent<any>;
34
+ };
35
+ slots: {
36
+ value: {
37
+ composed: boolean;
38
+ disabled: boolean;
39
+ open: boolean;
40
+ };
41
+ button: {
42
+ composed: boolean;
43
+ disabled: boolean;
44
+ open: boolean;
45
+ };
46
+ default: {
47
+ composed: boolean;
48
+ disabled: boolean;
49
+ open: boolean;
50
+ };
51
+ };
52
+ };
53
+ export type DropdownProps = typeof __propDef.props;
54
+ export type DropdownEvents = typeof __propDef.events;
55
+ export type DropdownSlots = typeof __propDef.slots;
56
+ export default class Dropdown extends SvelteComponentTyped<DropdownProps, DropdownEvents, DropdownSlots> {
57
+ }
58
+ export {};
package/Field.svelte ADDED
@@ -0,0 +1,257 @@
1
+ <script>import { createKeyborg } from "keyborg";
2
+ import { onMount } from "svelte";
3
+ import Tooltip from "./Tooltip.svelte";
4
+ export let forwardClick = false;
5
+ let htmlFor = void 0;
6
+ export { htmlFor as for };
7
+ export let label = void 0;
8
+ export let message = void 0;
9
+ export let required = false;
10
+ export let requiredTip = "This field is required";
11
+ export let status = "none";
12
+ let fieldRef;
13
+ let targetDisabled = false;
14
+ let targetRef = null;
15
+ const findTarget = () => {
16
+ let candidate = null;
17
+ if (htmlFor) {
18
+ candidate = fieldRef?.querySelector(`[id="${htmlFor}"]`);
19
+ }
20
+ if (!candidate) {
21
+ candidate = fieldRef?.querySelector(
22
+ 'a[href], audio[controls], button, details, div[contenteditable], form, input, select, textarea, video[controls], [tabindex]:not([tabindex="-1"])'
23
+ );
24
+ }
25
+ targetRef = candidate;
26
+ };
27
+ const isElementDisabled = (element) => {
28
+ if (element) {
29
+ return element.getAttribute("aria-disabled") === "true" || element?.matches(":disabled") || element?.disabled === true || element?.classList.contains("disabled");
30
+ }
31
+ return false;
32
+ };
33
+ $:
34
+ $$slots.default, fieldRef, htmlFor, findTarget();
35
+ $: {
36
+ targetDisabled = isElementDisabled(targetRef);
37
+ }
38
+ const mutationCallback = (mutations) => {
39
+ let disabled = void 0;
40
+ for (let i = 0; i < mutations.length; i++) {
41
+ const mutation = mutations[i];
42
+ if (mutation.type === "attributes") {
43
+ if (mutation.attributeName === "aria-disabled" || mutation.attributeName === "disabled" || mutation.attributeName === "class") {
44
+ if (isElementDisabled(targetRef)) {
45
+ disabled = true;
46
+ break;
47
+ }
48
+ disabled = false;
49
+ }
50
+ }
51
+ }
52
+ if (disabled !== void 0) {
53
+ targetDisabled = disabled;
54
+ }
55
+ };
56
+ let mutationObserver = new MutationObserver(mutationCallback);
57
+ $: {
58
+ mutationObserver.disconnect();
59
+ if (targetRef) {
60
+ mutationObserver.observe(targetRef, {
61
+ attributes: true
62
+ });
63
+ }
64
+ }
65
+ let keyborg = createKeyborg(window);
66
+ let usingKeyboard = keyborg.isNavigatingWithKeyboard();
67
+ const keyborgHandler = (value) => {
68
+ usingKeyboard = value;
69
+ };
70
+ onMount(() => {
71
+ keyborg.subscribe(keyborgHandler);
72
+ return () => {
73
+ keyborg.unsubscribe(keyborgHandler);
74
+ };
75
+ });
76
+ const onClick = () => {
77
+ if (forwardClick) {
78
+ targetRef?.click();
79
+ }
80
+ };
81
+ </script>
82
+
83
+ <label
84
+ aria-disabled={targetDisabled}
85
+ bind:this={fieldRef}
86
+ class="sterling-field"
87
+ class:disabled={targetDisabled}
88
+ class:using-keyboard={usingKeyboard}
89
+ for={htmlFor}
90
+ on:blur
91
+ on:click
92
+ on:click={onClick}
93
+ on:copy
94
+ on:cut
95
+ on:dblclick
96
+ on:focus
97
+ on:focusin
98
+ on:focusout
99
+ on:keydown
100
+ on:keypress
101
+ on:keyup
102
+ on:mousedown
103
+ on:mouseenter
104
+ on:mouseleave
105
+ on:mousemove
106
+ on:mouseover
107
+ on:mouseout
108
+ on:mouseup
109
+ on:scroll
110
+ on:wheel
111
+ on:paste
112
+ {...$$restProps}
113
+ >
114
+ <slot name="label" disabled={targetDisabled} for={htmlFor} {label} {required}>
115
+ <div class="label-text">
116
+ {label}
117
+ </div>
118
+ </slot>
119
+ <slot />
120
+ {#if message}
121
+ <slot name="message" disabled={targetDisabled} {message} {required} {status}>
122
+ <div
123
+ class="message"
124
+ class:info={status === 'info'}
125
+ class:success={status === 'success'}
126
+ class:warning={status === 'warning'}
127
+ class:error={status === 'error'}
128
+ >
129
+ {message}
130
+ </div>
131
+ </slot>
132
+ {/if}
133
+ {#if required}
134
+ <slot name="required" {requiredTip}>
135
+ <Tooltip autoShow="hover">
136
+ <span class="required-tip" slot="tip">{requiredTip}</span>
137
+ <div class="required">*</div>
138
+ </Tooltip>
139
+ </slot>
140
+ {/if}
141
+ </label>
142
+
143
+ <style>
144
+ .sterling-field {
145
+ background-color: var(--stsv-Input__background-color);
146
+ border-color: var(--stsv-Input__border-color);
147
+ border-radius: var(--stsv-Input__border-radius);
148
+ border-style: var(--stsv-Input__border-style);
149
+ border-width: var(--stsv-Input__border-width);
150
+ box-sizing: border-box;
151
+ color: var(--stsv-Input__color);
152
+ display: flex;
153
+ flex-direction: column;
154
+ font: inherit;
155
+ margin: 0;
156
+ overflow: visible;
157
+ padding: 0;
158
+ position: relative;
159
+ transition: background-color 250ms, color 250ms, border-color 250ms;
160
+ }
161
+
162
+ .sterling-field:hover {
163
+ background-color: var(--stsv-Input__background-color--hover);
164
+ border-color: var(--stsv-Input__border-color--hover);
165
+ color: var(--stsv-Input__color--hover);
166
+ }
167
+
168
+ .sterling-field.using-keyboard:focus-within {
169
+ border-color: var(--stsv-Common__border-color--focus);
170
+ color: var(--stsv-Common__color--focus);
171
+ outline-color: var(--stsv-Common__outline-color);
172
+ outline-offset: var(--stsv-Common__outline-offset);
173
+ outline-style: var(--stsv-Common__outline-style);
174
+ outline-width: var(--stsv-Common__outline-width);
175
+ }
176
+
177
+ .sterling-field.disabled {
178
+ background-color: var(--stsv-Common__background-color--disabled);
179
+ border-color: var(--stsv--Common__border-color--disabled);
180
+ color: var(--stsv-Common__color--disabled);
181
+ }
182
+
183
+ .label-text {
184
+ color: var(--stsv-Display__color);
185
+ font-size: 0.8em;
186
+ margin: 0.5em 0.7em 0.2em 0.2em;
187
+ }
188
+
189
+ .label-text:empty {
190
+ margin: 0;
191
+ }
192
+
193
+ .sterling-field.disabled .label-text {
194
+ color: var(--stsv-Common__color--disabled);
195
+ }
196
+
197
+ .message {
198
+ font-size: 0.8em;
199
+ background-color: var(--stsv-Display__background-color);
200
+ color: var(--stsv-Display__color);
201
+ padding: 0.5em;
202
+ transition: background-color 250ms, color 250ms, border-color 250ms;
203
+ }
204
+
205
+ .message.info {
206
+ background-color: var(--stsv-Info__background-color);
207
+ border-color: var(--stsv-Info__border-color);
208
+ color: var(--stsv-Info__color);
209
+ }
210
+
211
+ .message.success {
212
+ background-color: var(--stsv-Success__background-color);
213
+ border-color: var(--stsv-Success__border-color);
214
+ color: var(--stsv-Success__color);
215
+ }
216
+
217
+ .message.warning {
218
+ background-color: var(--stsv-Warning__background-color);
219
+ border-color: var(--stsv-Warning__border-color);
220
+ color: var(--stsv-Warning__color);
221
+ }
222
+
223
+ .message.error {
224
+ background-color: var(--stsv-Error__background-color);
225
+ border-color: var(--stsv__Error-color);
226
+ color: var(--stsv-Error__color);
227
+ }
228
+
229
+ .sterling-field.disabled .message {
230
+ background-color: var(--stsv-Common__background-color--disabled);
231
+ border-color: var(--stsv--Common__border-color--disabled);
232
+ color: var(--stsv-Common__color--disabled);
233
+ }
234
+
235
+ .required {
236
+ text-align: center;
237
+ font-weight: bold;
238
+ box-sizing: border-box;
239
+ width: 1em;
240
+ height: 1em;
241
+ position: absolute;
242
+ top: 0;
243
+ right: 0;
244
+ }
245
+
246
+ .required-tip {
247
+ display: block;
248
+ padding: 4px;
249
+ }
250
+
251
+ @media (prefers-reduced-motion) {
252
+ .sterling-field,
253
+ .message {
254
+ transition: none;
255
+ }
256
+ }
257
+ </style>
@@ -0,0 +1,63 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { FieldStatus } from './Field.types';
3
+ declare const __propDef: {
4
+ props: {
5
+ [x: string]: any;
6
+ forwardClick?: boolean | undefined;
7
+ for?: string | undefined;
8
+ label?: string | undefined;
9
+ message?: string | undefined;
10
+ required?: boolean | undefined;
11
+ requiredTip?: string | undefined;
12
+ status?: FieldStatus | undefined;
13
+ };
14
+ events: {
15
+ blur: FocusEvent;
16
+ click: MouseEvent;
17
+ copy: ClipboardEvent;
18
+ cut: ClipboardEvent;
19
+ dblclick: MouseEvent;
20
+ focus: FocusEvent;
21
+ focusin: FocusEvent;
22
+ focusout: FocusEvent;
23
+ keydown: KeyboardEvent;
24
+ keypress: KeyboardEvent;
25
+ keyup: KeyboardEvent;
26
+ mousedown: MouseEvent;
27
+ mouseenter: MouseEvent;
28
+ mouseleave: MouseEvent;
29
+ mousemove: MouseEvent;
30
+ mouseover: MouseEvent;
31
+ mouseout: MouseEvent;
32
+ mouseup: MouseEvent;
33
+ scroll: Event;
34
+ wheel: WheelEvent;
35
+ paste: ClipboardEvent;
36
+ } & {
37
+ [evt: string]: CustomEvent<any>;
38
+ };
39
+ slots: {
40
+ label: {
41
+ disabled: boolean;
42
+ for: string | undefined;
43
+ label: string | undefined;
44
+ required: boolean;
45
+ };
46
+ default: {};
47
+ message: {
48
+ disabled: boolean;
49
+ message: string | undefined;
50
+ required: boolean;
51
+ status: FieldStatus;
52
+ };
53
+ required: {
54
+ requiredTip: string;
55
+ };
56
+ };
57
+ };
58
+ export type FieldProps = typeof __propDef.props;
59
+ export type FieldEvents = typeof __propDef.events;
60
+ export type FieldSlots = typeof __propDef.slots;
61
+ export default class Field extends SvelteComponentTyped<FieldProps, FieldEvents, FieldSlots> {
62
+ }
63
+ export {};
@@ -0,0 +1 @@
1
+ export type FieldStatus = 'none' | 'info' | 'success' | 'warning' | 'error';
package/Field.types.js ADDED
@@ -0,0 +1 @@
1
+ export {};