fragment-tools 0.2.12 → 0.2.14
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/package.json +12 -11
- package/src/cli/build.js +1 -0
- package/src/cli/create.js +22 -4
- package/src/cli/createConfig.js +8 -3
- package/src/cli/getEntries.js +10 -1
- package/src/cli/plugins/hot-shader-replacement.js +54 -16
- package/src/cli/plugins/save.js +97 -38
- package/src/cli/prompts.js +89 -36
- package/src/cli/run.js +1 -1
- package/src/client/app/actions/resize.js +8 -1
- package/src/client/app/attachments/draggable.js +93 -0
- package/src/client/app/client.js +90 -18
- package/src/client/app/components/IconFlip.svelte +46 -0
- package/src/client/app/hooks.js +25 -1
- package/src/client/app/lib/canvas-recorder/CanvasRecorder.js +95 -3
- package/src/client/app/lib/canvas-recorder/FrameRecorder.js +45 -3
- package/src/client/app/lib/canvas-recorder/GIFRecorder.js +72 -13
- package/src/client/app/lib/canvas-recorder/MediaBunnyRecorder.js +43 -9
- package/src/client/app/lib/canvas-recorder/utils.js +18 -9
- package/src/client/app/renderers/2DRenderer.js +20 -16
- package/src/client/app/renderers/FragmentRenderer.js +1 -1
- package/src/client/app/renderers/P5GLRenderer.js +14 -6
- package/src/client/app/renderers/P5Renderer.js +9 -1
- package/src/client/app/renderers/THREERenderer.js +62 -48
- package/src/client/app/state/Sketch.svelte.js +149 -9
- package/src/client/app/state/errors.svelte.js +19 -0
- package/src/client/app/state/exports.svelte.js +14 -1
- package/src/client/app/state/rendering.svelte.js +47 -0
- package/src/client/app/state/sketches.svelte.js +43 -7
- package/src/client/app/state/utils.svelte.js +49 -0
- package/src/client/app/ui/Field.svelte +6 -1
- package/src/client/app/ui/FieldSection.svelte +4 -4
- package/src/client/app/ui/ParamsOutput.svelte +1 -1
- package/src/client/app/ui/SketchRenderer.svelte +16 -0
- package/src/client/app/ui/fields/ButtonInput.svelte +2 -0
- package/src/client/app/ui/fields/CheckboxInput.svelte +13 -11
- package/src/client/app/ui/fields/ColorInput.svelte +16 -11
- package/src/client/app/ui/fields/GradientInput.svelte +607 -0
- package/src/client/app/ui/fields/Input.svelte +10 -6
- package/src/client/app/ui/fields/IntervalInput.svelte +27 -35
- package/src/client/app/ui/fields/NumberInput.svelte +51 -13
- package/src/client/app/ui/fields/PaletteInput.svelte +181 -0
- package/src/client/app/ui/fields/ProgressInput.svelte +44 -16
- package/src/client/app/ui/fields/TextareaInput.svelte +10 -10
- package/src/client/app/utils/canvas.utils.js +105 -28
- package/src/client/app/utils/color.utils.js +74 -17
- package/src/client/app/utils/fields.utils.js +68 -26
- package/src/client/app/utils/file.utils.js +86 -31
- package/src/client/app/utils/glsl.utils.js +11 -2
- package/src/client/app/utils/glslErrors.js +31 -21
- package/src/client/app/utils/index.js +28 -12
- package/src/client/main.js +7 -1
- package/src/types/global.d.ts +143 -0
- package/src/types/props.d.ts +40 -15
- package/tsconfig.json +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import FieldInputRow from './FieldInputRow.svelte';
|
|
3
3
|
import NumberInput from './NumberInput.svelte';
|
|
4
4
|
import { map, clamp, roundToStep } from '../../utils/math.utils';
|
|
5
|
+
import { draggable } from '../../attachments/draggable.js';
|
|
5
6
|
|
|
6
7
|
let {
|
|
7
8
|
value = null,
|
|
@@ -21,51 +22,49 @@
|
|
|
21
22
|
/** @type {DOMRect}*/
|
|
22
23
|
let rect;
|
|
23
24
|
/** @type {boolean}*/
|
|
24
|
-
let
|
|
25
|
+
let dragging = $state(false);
|
|
25
26
|
|
|
26
27
|
let proximityIndex = -1;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
*
|
|
30
31
|
* @param {MouseEvent} event
|
|
32
|
+
* @param {DOMRect}
|
|
31
33
|
*/
|
|
32
|
-
function
|
|
33
|
-
document.body.classList.add('fragment-dragging');
|
|
34
|
-
|
|
35
|
-
document.addEventListener('mousemove', handleMouseMove);
|
|
36
|
-
document.addEventListener('mouseup', handleMouseUp);
|
|
37
|
-
|
|
38
|
-
rect = node.getBoundingClientRect();
|
|
39
|
-
|
|
40
|
-
isDragging = true;
|
|
41
|
-
|
|
42
|
-
let dragValue = computeDrag(event);
|
|
43
|
-
|
|
44
|
-
let abs0 = Math.abs(dragValue - value[0]);
|
|
45
|
-
let abs1 = Math.abs(dragValue - value[1]);
|
|
46
|
-
|
|
47
|
-
proximityIndex = abs0 < abs1 ? 0 : 1;
|
|
48
|
-
|
|
49
|
-
onDrag(event);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function computeDrag(event) {
|
|
34
|
+
function computeDrag(event, rect) {
|
|
53
35
|
let dragValue = clamp(
|
|
54
36
|
map(event.clientX, rect.left, rect.right, min, max),
|
|
55
37
|
min,
|
|
56
38
|
max,
|
|
57
39
|
);
|
|
58
40
|
dragValue = roundToStep(dragValue, step);
|
|
41
|
+
|
|
59
42
|
return dragValue;
|
|
60
43
|
}
|
|
61
44
|
|
|
62
|
-
|
|
63
|
-
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {MouseEvent} event
|
|
48
|
+
* @param {object} params
|
|
49
|
+
* @param {DOMRect | undefined} params.rect
|
|
50
|
+
*/
|
|
51
|
+
function onDragStart(event, params) {
|
|
52
|
+
if (params.rect) {
|
|
53
|
+
let dragValue = computeDrag(event, params.rect);
|
|
54
|
+
|
|
55
|
+
let abs0 = Math.abs(dragValue - value[0]);
|
|
56
|
+
let abs1 = Math.abs(dragValue - value[1]);
|
|
57
|
+
|
|
58
|
+
proximityIndex = abs0 < abs1 ? 0 : 1;
|
|
59
|
+
|
|
60
|
+
onDrag(event, params);
|
|
61
|
+
}
|
|
64
62
|
}
|
|
65
63
|
|
|
66
|
-
function onDrag(event) {
|
|
67
|
-
|
|
64
|
+
function onDrag(event, params) {
|
|
65
|
+
dragging = params.isDragging;
|
|
68
66
|
|
|
67
|
+
let dragValue = computeDrag(event, params.rect);
|
|
69
68
|
let prevValue = value[proximityIndex];
|
|
70
69
|
|
|
71
70
|
if (dragValue !== prevValue) {
|
|
@@ -82,13 +81,6 @@
|
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
function handleMouseUp() {
|
|
86
|
-
document.body.classList.remove('fragment-dragging');
|
|
87
|
-
document.removeEventListener('mousemove', handleMouseMove);
|
|
88
|
-
document.removeEventListener('mouseup', handleMouseUp);
|
|
89
|
-
|
|
90
|
-
isDragging = false;
|
|
91
|
-
}
|
|
92
84
|
|
|
93
85
|
function handleValueChange(index, newValue) {
|
|
94
86
|
let newValues = [...value];
|
|
@@ -119,9 +111,9 @@
|
|
|
119
111
|
<FieldInputRow --grid-template-columns="1fr 0.5fr">
|
|
120
112
|
<div
|
|
121
113
|
class="range"
|
|
122
|
-
class:dragging={
|
|
114
|
+
class:dragging={dragging}
|
|
123
115
|
bind:this={node}
|
|
124
|
-
|
|
116
|
+
{@attach draggable({ onDragStart, onDrag })}
|
|
125
117
|
>
|
|
126
118
|
<div class="handler" style="--position: {p1};" />
|
|
127
119
|
<div class="filler" style="--p1: {p1}; --p2: {p2};"></div>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
|
+
import { tick } from 'svelte';
|
|
2
3
|
import FieldInputRow from './FieldInputRow.svelte';
|
|
3
4
|
import Input from './Input.svelte';
|
|
4
5
|
import ProgressInput from './ProgressInput.svelte';
|
|
@@ -17,16 +18,30 @@
|
|
|
17
18
|
key = '',
|
|
18
19
|
progress = true,
|
|
19
20
|
onchange,
|
|
21
|
+
onfocus,
|
|
22
|
+
onblur,
|
|
23
|
+
node = $bindable(),
|
|
20
24
|
} = $props();
|
|
21
25
|
|
|
22
26
|
let hasProgress = $derived(progress && isFinite(min) && isFinite(max));
|
|
23
27
|
let isFocused = $state(false);
|
|
24
28
|
let precision = $derived(step.toString().split('.')[1]?.length || 0);
|
|
25
29
|
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} v
|
|
32
|
+
* @param {string} suffix
|
|
33
|
+
*/
|
|
26
34
|
function sanitize(v, suffix) {
|
|
27
35
|
return suffix && suffix !== '' ? Number(v.split(suffix)[0]) : Number(v);
|
|
28
36
|
}
|
|
29
37
|
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @param {number} v
|
|
41
|
+
* @param {boolean} isFocused
|
|
42
|
+
* @param {string} suffix
|
|
43
|
+
* @param {number} precision
|
|
44
|
+
*/
|
|
30
45
|
function composeValue(v, isFocused, suffix = '', precision) {
|
|
31
46
|
const clampedValue = clamp(
|
|
32
47
|
v,
|
|
@@ -45,32 +60,53 @@
|
|
|
45
60
|
composeValue(value, isFocused, suffix, precision),
|
|
46
61
|
);
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
/**
|
|
64
|
+
* @param {FocusEvent} event
|
|
65
|
+
*/
|
|
66
|
+
function onFocus(event) {
|
|
49
67
|
isFocused = true;
|
|
68
|
+
onfocus?.(event);
|
|
50
69
|
}
|
|
51
70
|
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
/**
|
|
72
|
+
* @param {KeyboardEvent} event
|
|
73
|
+
*/
|
|
74
|
+
async function onBlur(event) {
|
|
75
|
+
let currentTarget = event.currentTarget;
|
|
54
76
|
|
|
55
|
-
|
|
56
|
-
let isNotValid = isNaN(Number(event.currentTarget.value));
|
|
77
|
+
await tick();
|
|
57
78
|
|
|
58
|
-
if (
|
|
59
|
-
newValue =
|
|
60
|
-
|
|
79
|
+
if (currentTarget instanceof HTMLInputElement) {
|
|
80
|
+
let newValue = currentTarget.value;
|
|
81
|
+
isFocused = false;
|
|
82
|
+
let sanitizedValue = sanitize(newValue, suffix);
|
|
83
|
+
|
|
84
|
+
if (isNaN(sanitizedValue)) {
|
|
85
|
+
onchange(value, true);
|
|
86
|
+
} else {
|
|
87
|
+
onchange(sanitizedValue, true);
|
|
88
|
+
}
|
|
61
89
|
|
|
62
|
-
|
|
90
|
+
onblur?.(event);
|
|
91
|
+
}
|
|
63
92
|
}
|
|
64
93
|
|
|
94
|
+
/**
|
|
95
|
+
* @param {KeyboardEvent} event
|
|
96
|
+
*/
|
|
65
97
|
function onKeyDown(event) {
|
|
66
|
-
if (
|
|
98
|
+
if (
|
|
99
|
+
event.currentTarget instanceof HTMLInputElement &&
|
|
100
|
+
['ArrowDown', 'ArrowUp'].includes(event.key)
|
|
101
|
+
) {
|
|
67
102
|
event.preventDefault();
|
|
68
103
|
|
|
69
104
|
const diff = Keyboard.getStepFromEvent(event) * step;
|
|
70
|
-
const direction = event.
|
|
71
|
-
const
|
|
105
|
+
const direction = event.key === 'ArrowUp' ? 1 : -1;
|
|
106
|
+
const sanitizedValue =
|
|
107
|
+
sanitize(event.currentTarget.value, suffix) + direction * diff;
|
|
72
108
|
|
|
73
|
-
onchange(
|
|
109
|
+
onchange(sanitizedValue, false);
|
|
74
110
|
}
|
|
75
111
|
}
|
|
76
112
|
</script>
|
|
@@ -93,6 +129,7 @@
|
|
|
93
129
|
{disabled}
|
|
94
130
|
{context}
|
|
95
131
|
{key}
|
|
132
|
+
bind:node
|
|
96
133
|
onkeydown={onKeyDown}
|
|
97
134
|
onfocus={onFocus}
|
|
98
135
|
onblur={onBlur}
|
|
@@ -108,6 +145,7 @@
|
|
|
108
145
|
onkeydown={onKeyDown}
|
|
109
146
|
onfocus={onFocus}
|
|
110
147
|
onblur={onBlur}
|
|
148
|
+
bind:node
|
|
111
149
|
value={composedValue}
|
|
112
150
|
/>
|
|
113
151
|
{/if}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import * as color from '../../utils/color.utils.js';
|
|
3
|
+
import ButtonInput from './ButtonInput.svelte';
|
|
4
|
+
import ColorInput from './ColorInput.svelte';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
value,
|
|
8
|
+
context = null,
|
|
9
|
+
key = '',
|
|
10
|
+
disabled = false,
|
|
11
|
+
onchange,
|
|
12
|
+
editable = true,
|
|
13
|
+
extensible = true,
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
let selected = $state(-1);
|
|
17
|
+
|
|
18
|
+
let hexValues = $derived.by(() => {
|
|
19
|
+
return value.map((v) => {
|
|
20
|
+
const format = color.getColorFormat(v);
|
|
21
|
+
return color.toHex(v, format);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
$effect(() => {
|
|
26
|
+
// handle value length changes when a color is selected
|
|
27
|
+
if (selected >= hexValues.length) {
|
|
28
|
+
selected = hexValues.length - 1;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* @param {PointerEvent} event
|
|
35
|
+
* @param {number} index
|
|
36
|
+
*/
|
|
37
|
+
function handleClick(event, index) {
|
|
38
|
+
if (selected !== index) {
|
|
39
|
+
selected = index;
|
|
40
|
+
} else {
|
|
41
|
+
selected = -1;
|
|
42
|
+
event.currentTarget.blur();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param {string} color
|
|
49
|
+
*/
|
|
50
|
+
function handleColorChange(color) {
|
|
51
|
+
let palette = value.map((v) => v);
|
|
52
|
+
palette[selected] = color;
|
|
53
|
+
|
|
54
|
+
onchange(palette);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function handleClickAdd() {
|
|
58
|
+
let palette = value.map((v) => v);
|
|
59
|
+
palette.push('#ffffff');
|
|
60
|
+
|
|
61
|
+
onchange(palette);
|
|
62
|
+
|
|
63
|
+
selected = palette.length - 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function handleClickDelete() {
|
|
67
|
+
let index = selected;
|
|
68
|
+
let palette = value.map((v) => v);
|
|
69
|
+
palette.splice(index, 1);
|
|
70
|
+
|
|
71
|
+
onchange(palette);
|
|
72
|
+
selected = -1;
|
|
73
|
+
}
|
|
74
|
+
</script>
|
|
75
|
+
|
|
76
|
+
<div class="palette-input" class:disabled style="--count: {hexValues.length}">
|
|
77
|
+
<div class="palette-list">
|
|
78
|
+
{#if editable && extensible}
|
|
79
|
+
<div class="palette-action">
|
|
80
|
+
<ButtonInput label="+" onclick={handleClickAdd} />
|
|
81
|
+
</div>
|
|
82
|
+
{/if}
|
|
83
|
+
{#each hexValues as hexValue, index}
|
|
84
|
+
{#if editable}
|
|
85
|
+
<button
|
|
86
|
+
class="palette-action"
|
|
87
|
+
style="--current-color: {hexValue}"
|
|
88
|
+
class:selected={selected === index}
|
|
89
|
+
onclick={(event) => handleClick(event, index)}
|
|
90
|
+
>
|
|
91
|
+
<span class="visually-hidden">Change color {index}</span>
|
|
92
|
+
</button>
|
|
93
|
+
{:else}
|
|
94
|
+
<div
|
|
95
|
+
class="palette-action"
|
|
96
|
+
style="--current-color: {hexValue}"
|
|
97
|
+
></div>
|
|
98
|
+
{/if}
|
|
99
|
+
{/each}
|
|
100
|
+
</div>
|
|
101
|
+
{#if value[selected] && editable}
|
|
102
|
+
<div class="palette-editor">
|
|
103
|
+
<ColorInput value={value[selected]} onchange={handleColorChange} />
|
|
104
|
+
</div>
|
|
105
|
+
{#if extensible}
|
|
106
|
+
<div class="palette-delete">
|
|
107
|
+
<ButtonInput label="delete" onclick={handleClickDelete} />
|
|
108
|
+
</div>
|
|
109
|
+
{/if}
|
|
110
|
+
{/if}
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<style>
|
|
114
|
+
.palette-input {
|
|
115
|
+
position: relative;
|
|
116
|
+
width: 100%;
|
|
117
|
+
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
row-gap: var(--column-gap);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.palette-list {
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-wrap: wrap;
|
|
126
|
+
|
|
127
|
+
column-gap: var(--column-gap);
|
|
128
|
+
row-gap: var(--column-gap);
|
|
129
|
+
align-items: center;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.palette-action {
|
|
133
|
+
position: relative;
|
|
134
|
+
aspect-ratio: 1;
|
|
135
|
+
height: var(--fragment-input-height);
|
|
136
|
+
|
|
137
|
+
border-radius: var(--fragment-input-border-radius);
|
|
138
|
+
background-color: var(
|
|
139
|
+
--background-color,
|
|
140
|
+
var(--fragment-input-background-color)
|
|
141
|
+
);
|
|
142
|
+
box-shadow: inset 0 0 0 1px
|
|
143
|
+
var(--box-shadow-color, var(--fragment-input-border-color));
|
|
144
|
+
outline: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
button.palette-action {
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.palette-input:focus-within .palette-action.selected {
|
|
152
|
+
box-shadow: 0 0 0 2px var(--fragment-accent-color);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.palette-action:before {
|
|
156
|
+
--gap: 1px;
|
|
157
|
+
content: '';
|
|
158
|
+
|
|
159
|
+
position: absolute;
|
|
160
|
+
z-index: 1;
|
|
161
|
+
top: var(--gap);
|
|
162
|
+
left: var(--gap);
|
|
163
|
+
right: var(--gap);
|
|
164
|
+
bottom: var(--gap);
|
|
165
|
+
|
|
166
|
+
background-color: var(--current-color);
|
|
167
|
+
border-radius: calc(var(--fragment-input-border-radius) - var(--gap));
|
|
168
|
+
opacity: var(--opacity, 1);
|
|
169
|
+
pointer-events: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
:global(body:not(.fragment-dragging))
|
|
173
|
+
button.palette-action:not(.disabled):hover {
|
|
174
|
+
box-shadow: inset 0 0 0 1px var(--fragment-accent-color);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
:global(body:not(.fragment-dragging))
|
|
178
|
+
button.palette-action:not(.disabled):focus-within {
|
|
179
|
+
box-shadow: 0 0 0 2px var(--fragment-accent-color);
|
|
180
|
+
}
|
|
181
|
+
</style>
|
|
@@ -2,46 +2,74 @@
|
|
|
2
2
|
import Keyboard from '../../inputs/Keyboard.js';
|
|
3
3
|
import { map, clamp, roundToStep } from '../../utils/math.utils.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} Props
|
|
7
|
+
* @property {number} value
|
|
8
|
+
* @property {number} min
|
|
9
|
+
* @property {number} max
|
|
10
|
+
* @property {number} step
|
|
11
|
+
* @property {boolean} disabled
|
|
12
|
+
* @property {(value: number) => void|undefined} onchange
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** @type {Props} */
|
|
5
16
|
let { value, min, max, step, disabled = false, onchange } = $props();
|
|
6
17
|
|
|
18
|
+
/** @type {HTMLElement|undefined} */
|
|
7
19
|
let node;
|
|
20
|
+
/** @type {DOMRect|undefined} */
|
|
8
21
|
let rect;
|
|
9
22
|
|
|
10
23
|
let isDragging = $state(false);
|
|
11
24
|
let steppedValue = $derived(roundToStep(value, step));
|
|
12
25
|
|
|
13
|
-
|
|
26
|
+
/**
|
|
27
|
+
* @param {MouseEvent} event
|
|
28
|
+
*/
|
|
14
29
|
function handleMouseDown(event) {
|
|
15
30
|
if (disabled) return;
|
|
16
31
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
32
|
+
if (node) {
|
|
33
|
+
document.body.classList.add('fragment-dragging');
|
|
34
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
35
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
20
36
|
|
|
21
|
-
|
|
37
|
+
rect = node.getBoundingClientRect();
|
|
22
38
|
|
|
23
|
-
|
|
39
|
+
isDragging = true;
|
|
24
40
|
|
|
25
|
-
|
|
41
|
+
onDrag(event);
|
|
42
|
+
}
|
|
26
43
|
}
|
|
27
44
|
|
|
45
|
+
/**
|
|
46
|
+
* @param {MouseEvent} event
|
|
47
|
+
*/
|
|
28
48
|
function handleMouseMove(event) {
|
|
29
49
|
onDrag(event);
|
|
30
50
|
}
|
|
31
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @param {MouseEvent} event
|
|
54
|
+
*/
|
|
32
55
|
function onDrag(event) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
if (rect) {
|
|
57
|
+
let dragValue = clamp(
|
|
58
|
+
map(event.clientX, rect.left, rect.right, min, max),
|
|
59
|
+
min,
|
|
60
|
+
max,
|
|
61
|
+
);
|
|
62
|
+
dragValue = roundToStep(dragValue, step);
|
|
39
63
|
|
|
40
|
-
|
|
41
|
-
|
|
64
|
+
if (dragValue !== value) {
|
|
65
|
+
onchange?.(dragValue);
|
|
66
|
+
}
|
|
42
67
|
}
|
|
43
68
|
}
|
|
44
69
|
|
|
70
|
+
/**
|
|
71
|
+
* @param {KeyboardEvent} event
|
|
72
|
+
*/
|
|
45
73
|
function handleKeyDown(event) {
|
|
46
74
|
const direction = ['ArrowUp', 'ArrowRight'].includes(event.key)
|
|
47
75
|
? 1
|
|
@@ -59,7 +87,7 @@
|
|
|
59
87
|
);
|
|
60
88
|
|
|
61
89
|
if (newValue !== value) {
|
|
62
|
-
onchange(newValue);
|
|
90
|
+
onchange?.(newValue);
|
|
63
91
|
}
|
|
64
92
|
}
|
|
65
93
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
let {
|
|
3
|
-
label,
|
|
4
3
|
value = $bindable(),
|
|
5
4
|
height,
|
|
6
5
|
disabled = false,
|
|
@@ -11,14 +10,16 @@
|
|
|
11
10
|
onblur,
|
|
12
11
|
} = $props();
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
let node;
|
|
18
|
-
|
|
13
|
+
/**
|
|
14
|
+
* @param {KeyboardEvent} event
|
|
15
|
+
*/
|
|
19
16
|
function onKeyPress(event) {
|
|
20
|
-
if (
|
|
21
|
-
|
|
17
|
+
if (
|
|
18
|
+
event.currentTarget instanceof HTMLTextAreaElement &&
|
|
19
|
+
event.key === 'Enter' &&
|
|
20
|
+
!event.shiftKey
|
|
21
|
+
) {
|
|
22
|
+
event.currentTarget.blur();
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
</script>
|
|
@@ -30,7 +31,6 @@
|
|
|
30
31
|
>
|
|
31
32
|
<textarea
|
|
32
33
|
class="input"
|
|
33
|
-
bind:this={node}
|
|
34
34
|
bind:value
|
|
35
35
|
{oninput}
|
|
36
36
|
{onchange}
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
{onfocus}
|
|
39
39
|
{onblur}
|
|
40
40
|
onkeypress={onKeyPress}
|
|
41
|
-
|
|
41
|
+
{disabled}
|
|
42
42
|
autocomplete="off"
|
|
43
43
|
spellcheck="false"
|
|
44
44
|
></textarea>
|