sh3-core 0.22.2 → 0.22.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/dist/apps/types.d.ts +4 -0
- package/dist/primitives/widgets/Field.svelte +5 -0
- package/dist/primitives/widgets/Field.svelte.d.ts +1 -0
- package/dist/primitives/widgets/Field.svelte.test.js +16 -0
- package/dist/primitives/widgets/NumberInput.svelte +21 -12
- package/dist/primitives/widgets/NumberInput.svelte.d.ts +2 -0
- package/dist/primitives/widgets/NumberInput.svelte.test.js +26 -0
- package/dist/primitives/widgets/Textarea.svelte +5 -0
- package/dist/primitives/widgets/Textarea.svelte.d.ts +1 -0
- package/dist/primitives/widgets/Textarea.svelte.test.js +16 -0
- package/dist/primitives/widgets/_selectOnFocus.d.ts +15 -0
- package/dist/primitives/widgets/_selectOnFocus.js +24 -0
- package/dist/sh3core-shard/Sh3Home.svelte +2 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/apps/types.d.ts
CHANGED
|
@@ -82,6 +82,10 @@ export interface AppManifest {
|
|
|
82
82
|
* set the framework falls back to `box`.
|
|
83
83
|
*/
|
|
84
84
|
icon?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Optional default home-card color default to transparent if not set
|
|
87
|
+
*/
|
|
88
|
+
color?: string;
|
|
85
89
|
}
|
|
86
90
|
/**
|
|
87
91
|
* Context object passed to `App.activate`. Provides app-scoped state zones
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { LiveInputEvents } from './_contract';
|
|
4
|
+
import { selectOnPointerDown, selectOnFocusEvent } from './_selectOnFocus';
|
|
4
5
|
|
|
5
6
|
let {
|
|
6
7
|
value = $bindable(''),
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
size = 'md',
|
|
17
18
|
required = false,
|
|
18
19
|
autocomplete,
|
|
20
|
+
selectOnFocus = false,
|
|
19
21
|
oninput,
|
|
20
22
|
onchange,
|
|
21
23
|
}: {
|
|
@@ -32,6 +34,7 @@
|
|
|
32
34
|
size?: 'sm' | 'md';
|
|
33
35
|
required?: boolean;
|
|
34
36
|
autocomplete?: AutoFill;
|
|
37
|
+
selectOnFocus?: boolean;
|
|
35
38
|
} & LiveInputEvents<string> = $props();
|
|
36
39
|
|
|
37
40
|
const showError = $derived(invalid && !!error);
|
|
@@ -53,6 +56,8 @@
|
|
|
53
56
|
bind:value
|
|
54
57
|
oninput={() => oninput?.(value)}
|
|
55
58
|
onblur={() => onchange?.(value)}
|
|
59
|
+
onpointerdown={selectOnFocus ? selectOnPointerDown : undefined}
|
|
60
|
+
onfocus={selectOnFocus ? selectOnFocusEvent : undefined}
|
|
56
61
|
/>
|
|
57
62
|
{#if suffix}<span class="sh3-field__affix">{@render suffix()}</span>{/if}
|
|
58
63
|
</span>
|
|
@@ -14,6 +14,7 @@ type $$ComponentProps = {
|
|
|
14
14
|
size?: 'sm' | 'md';
|
|
15
15
|
required?: boolean;
|
|
16
16
|
autocomplete?: AutoFill;
|
|
17
|
+
selectOnFocus?: boolean;
|
|
17
18
|
} & LiveInputEvents<string>;
|
|
18
19
|
declare const Field: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
19
20
|
type Field = ReturnType<typeof Field>;
|
|
@@ -31,3 +31,19 @@ describe('Field event contract', () => {
|
|
|
31
31
|
expect(getByText('Required')).toBeInTheDocument();
|
|
32
32
|
});
|
|
33
33
|
});
|
|
34
|
+
describe('Field selectOnFocus opt-in', () => {
|
|
35
|
+
it('does not select on focus by default', async () => {
|
|
36
|
+
const { container } = render(Field, { props: { value: 'hello' } });
|
|
37
|
+
const input = container.querySelector('input');
|
|
38
|
+
const spy = vi.spyOn(input, 'select');
|
|
39
|
+
await fireEvent.focus(input);
|
|
40
|
+
expect(spy).not.toHaveBeenCalled();
|
|
41
|
+
});
|
|
42
|
+
it('selects on focus when selectOnFocus is true', async () => {
|
|
43
|
+
const { container } = render(Field, { props: { value: 'hello', selectOnFocus: true } });
|
|
44
|
+
const input = container.querySelector('input');
|
|
45
|
+
const spy = vi.spyOn(input, 'select');
|
|
46
|
+
await fireEvent.focus(input);
|
|
47
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { LiveInputEvents } from './_contract';
|
|
4
4
|
import { clamp, applyStep } from './NumberInput';
|
|
5
|
+
import { selectOnPointerDown, selectOnFocusEvent } from './_selectOnFocus';
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
value = $bindable(0),
|
|
@@ -15,6 +16,8 @@
|
|
|
15
16
|
disabled = false,
|
|
16
17
|
invalid = false,
|
|
17
18
|
size = 'md',
|
|
19
|
+
steppers = false,
|
|
20
|
+
selectOnFocus = false,
|
|
18
21
|
oninput,
|
|
19
22
|
onchange,
|
|
20
23
|
}: {
|
|
@@ -29,6 +32,8 @@
|
|
|
29
32
|
disabled?: boolean;
|
|
30
33
|
invalid?: boolean;
|
|
31
34
|
size?: 'sm' | 'md';
|
|
35
|
+
steppers?: boolean;
|
|
36
|
+
selectOnFocus?: boolean;
|
|
32
37
|
} & LiveInputEvents<number> = $props();
|
|
33
38
|
|
|
34
39
|
function commit(next: number) {
|
|
@@ -95,20 +100,24 @@
|
|
|
95
100
|
commit(value);
|
|
96
101
|
onchange?.(value);
|
|
97
102
|
}}
|
|
103
|
+
onpointerdown={selectOnFocus ? selectOnPointerDown : undefined}
|
|
104
|
+
onfocus={selectOnFocus ? selectOnFocusEvent : undefined}
|
|
98
105
|
/>
|
|
99
106
|
{#if suffix}<span class="sh3-num__affix">{@render suffix()}</span>{/if}
|
|
100
|
-
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
{#if steppers}
|
|
108
|
+
<span class="sh3-num__steppers">
|
|
109
|
+
<button type="button" {disabled}
|
|
110
|
+
onpointerdown={() => startHold(1)}
|
|
111
|
+
onpointerup={stopHold}
|
|
112
|
+
onpointerleave={stopHold}
|
|
113
|
+
aria-label="Increase">▲</button>
|
|
114
|
+
<button type="button" {disabled}
|
|
115
|
+
onpointerdown={() => startHold(-1)}
|
|
116
|
+
onpointerup={stopHold}
|
|
117
|
+
onpointerleave={stopHold}
|
|
118
|
+
aria-label="Decrease">▼</button>
|
|
119
|
+
</span>
|
|
120
|
+
{/if}
|
|
112
121
|
</span>
|
|
113
122
|
</label>
|
|
114
123
|
|
|
@@ -12,6 +12,8 @@ type $$ComponentProps = {
|
|
|
12
12
|
disabled?: boolean;
|
|
13
13
|
invalid?: boolean;
|
|
14
14
|
size?: 'sm' | 'md';
|
|
15
|
+
steppers?: boolean;
|
|
16
|
+
selectOnFocus?: boolean;
|
|
15
17
|
} & LiveInputEvents<number>;
|
|
16
18
|
declare const NumberInput: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
17
19
|
type NumberInput = ReturnType<typeof NumberInput>;
|
|
@@ -46,3 +46,29 @@ describe('NumberInput event contract', () => {
|
|
|
46
46
|
expect(onchange.mock.calls[0][0]).toBeCloseTo(1.23, 5);
|
|
47
47
|
});
|
|
48
48
|
});
|
|
49
|
+
describe('NumberInput steppers opt-in', () => {
|
|
50
|
+
it('hides ▲▼ buttons by default', () => {
|
|
51
|
+
const { container } = render(NumberInput, { props: { value: 0 } });
|
|
52
|
+
expect(container.querySelectorAll('button').length).toBe(0);
|
|
53
|
+
});
|
|
54
|
+
it('renders ▲▼ buttons when steppers is true', () => {
|
|
55
|
+
const { container } = render(NumberInput, { props: { value: 0, steppers: true } });
|
|
56
|
+
expect(container.querySelectorAll('button').length).toBe(2);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('NumberInput selectOnFocus opt-in', () => {
|
|
60
|
+
it('does not select on focus by default', async () => {
|
|
61
|
+
const { container } = render(NumberInput, { props: { value: 42 } });
|
|
62
|
+
const input = container.querySelector('input');
|
|
63
|
+
const spy = vi.spyOn(input, 'select');
|
|
64
|
+
await fireEvent.focus(input);
|
|
65
|
+
expect(spy).not.toHaveBeenCalled();
|
|
66
|
+
});
|
|
67
|
+
it('selects on focus when selectOnFocus is true', async () => {
|
|
68
|
+
const { container } = render(NumberInput, { props: { value: 42, selectOnFocus: true } });
|
|
69
|
+
const input = container.querySelector('input');
|
|
70
|
+
const spy = vi.spyOn(input, 'select');
|
|
71
|
+
await fireEvent.focus(input);
|
|
72
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { LiveInputEvents } from './_contract';
|
|
3
|
+
import { selectOnPointerDown, selectOnFocusEvent } from './_selectOnFocus';
|
|
3
4
|
|
|
4
5
|
let {
|
|
5
6
|
value = $bindable(''),
|
|
@@ -13,6 +14,7 @@
|
|
|
13
14
|
required = false,
|
|
14
15
|
rows = 3,
|
|
15
16
|
resize = 'vertical',
|
|
17
|
+
selectOnFocus = false,
|
|
16
18
|
oninput,
|
|
17
19
|
onchange,
|
|
18
20
|
}: {
|
|
@@ -27,6 +29,7 @@
|
|
|
27
29
|
required?: boolean;
|
|
28
30
|
rows?: number;
|
|
29
31
|
resize?: 'none' | 'vertical' | 'both';
|
|
32
|
+
selectOnFocus?: boolean;
|
|
30
33
|
} & LiveInputEvents<string> = $props();
|
|
31
34
|
|
|
32
35
|
const showError = $derived(invalid && !!error);
|
|
@@ -46,6 +49,8 @@
|
|
|
46
49
|
bind:value
|
|
47
50
|
oninput={() => oninput?.(value)}
|
|
48
51
|
onblur={() => onchange?.(value)}
|
|
52
|
+
onpointerdown={selectOnFocus ? selectOnPointerDown : undefined}
|
|
53
|
+
onfocus={selectOnFocus ? selectOnFocusEvent : undefined}
|
|
49
54
|
></textarea>
|
|
50
55
|
{#if helperText}<span class="sh3-textarea__helper" class:sh3-textarea__helper--error={showError}>{helperText}</span>{/if}
|
|
51
56
|
</label>
|
|
@@ -11,6 +11,7 @@ type $$ComponentProps = {
|
|
|
11
11
|
required?: boolean;
|
|
12
12
|
rows?: number;
|
|
13
13
|
resize?: 'none' | 'vertical' | 'both';
|
|
14
|
+
selectOnFocus?: boolean;
|
|
14
15
|
} & LiveInputEvents<string>;
|
|
15
16
|
declare const Textarea: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
16
17
|
type Textarea = ReturnType<typeof Textarea>;
|
|
@@ -27,3 +27,19 @@ describe('Textarea event contract', () => {
|
|
|
27
27
|
expect(ta.style.resize).toBe('none');
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
|
+
describe('Textarea selectOnFocus opt-in', () => {
|
|
31
|
+
it('does not select on focus by default', async () => {
|
|
32
|
+
const { container } = render(Textarea, { props: { value: 'hello' } });
|
|
33
|
+
const ta = container.querySelector('textarea');
|
|
34
|
+
const spy = vi.spyOn(ta, 'select');
|
|
35
|
+
await fireEvent.focus(ta);
|
|
36
|
+
expect(spy).not.toHaveBeenCalled();
|
|
37
|
+
});
|
|
38
|
+
it('selects on focus when selectOnFocus is true', async () => {
|
|
39
|
+
const { container } = render(Textarea, { props: { value: 'hello', selectOnFocus: true } });
|
|
40
|
+
const ta = container.querySelector('textarea');
|
|
41
|
+
const spy = vi.spyOn(ta, 'select');
|
|
42
|
+
await fireEvent.focus(ta);
|
|
43
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "select content on focus" opt-in handlers for keystroke widgets
|
|
3
|
+
* (Field, Textarea, NumberInput).
|
|
4
|
+
*
|
|
5
|
+
* Two paths:
|
|
6
|
+
* - pointer click on an unfocused input: preventDefault on pointerdown to
|
|
7
|
+
* suppress native caret placement, then focus() + select() ourselves.
|
|
8
|
+
* Clicking again while already focused falls through to normal caret
|
|
9
|
+
* placement, so users can still edit a single character.
|
|
10
|
+
* - tab / label click: onfocus selects all.
|
|
11
|
+
*
|
|
12
|
+
* Internal — not exported from api.ts.
|
|
13
|
+
*/
|
|
14
|
+
export declare function selectOnPointerDown(e: PointerEvent): void;
|
|
15
|
+
export declare function selectOnFocusEvent(e: FocusEvent): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "select content on focus" opt-in handlers for keystroke widgets
|
|
3
|
+
* (Field, Textarea, NumberInput).
|
|
4
|
+
*
|
|
5
|
+
* Two paths:
|
|
6
|
+
* - pointer click on an unfocused input: preventDefault on pointerdown to
|
|
7
|
+
* suppress native caret placement, then focus() + select() ourselves.
|
|
8
|
+
* Clicking again while already focused falls through to normal caret
|
|
9
|
+
* placement, so users can still edit a single character.
|
|
10
|
+
* - tab / label click: onfocus selects all.
|
|
11
|
+
*
|
|
12
|
+
* Internal — not exported from api.ts.
|
|
13
|
+
*/
|
|
14
|
+
export function selectOnPointerDown(e) {
|
|
15
|
+
const el = e.currentTarget;
|
|
16
|
+
if (document.activeElement !== el) {
|
|
17
|
+
e.preventDefault();
|
|
18
|
+
el.focus();
|
|
19
|
+
el.select();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function selectOnFocusEvent(e) {
|
|
23
|
+
e.currentTarget.select();
|
|
24
|
+
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import { makeSelectionApi } from '../actions/selection.svelte';
|
|
19
19
|
import { getAppearance } from '../app-appearance';
|
|
20
20
|
import iconsUrl from '../assets/icons.svg';
|
|
21
|
+
import { manifest } from '../shell-shard/manifest';
|
|
21
22
|
|
|
22
23
|
const homeSelection = makeSelectionApi('__sh3core__');
|
|
23
24
|
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
type="button"
|
|
94
95
|
class="sh3-home-card"
|
|
95
96
|
class:sh3-home-card--tinted={appearance?.color}
|
|
96
|
-
style:--card-color={appearance?.color ?? 'transparent'}
|
|
97
|
+
style:--card-color={appearance?.color ?? manifest.color ?? 'transparent'}
|
|
97
98
|
data-sh3-scope="element:app"
|
|
98
99
|
onclick={() => launchApp(manifest.id)}
|
|
99
100
|
oncontextmenu={(e) => openAppContextMenu(e, manifest.id)}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export declare const VERSION = "0.22.
|
|
2
|
+
export declare const VERSION = "0.22.5";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Auto-generated from package.json — do not edit manually. */
|
|
2
|
-
export const VERSION = '0.22.
|
|
2
|
+
export const VERSION = '0.22.5';
|