svelte-tweakpane-ui 1.4.0 → 1.5.1

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.
@@ -82,10 +82,3 @@
82
82
  <ClsPad keysAdd={fillWith('containerUnitSpacing', 2)} theme={$$props.theme} />
83
83
  {/if}
84
84
  {/if}
85
-
86
- <style>
87
- /* Fix overflow bug from the plugin TODO PR */
88
- :global(div.svelte-tweakpane-ui div.tp-cbzv:not(tp-cbzv-expanded) div.tp-cbzv_p) {
89
- overflow: hidden !important;
90
- }
91
- </style>
@@ -0,0 +1,55 @@
1
+ <script context="module"></script>
2
+
3
+ <script>
4
+ import ClsPad from '../internal/ClsPad.svelte';
5
+ import GenericInput from '../internal/GenericInput.svelte';
6
+ import { fillWith } from '../utils';
7
+ import * as pluginModule from '@kitschpatrol/tweakpane-plugin-file-import';
8
+ import { BROWSER } from 'esm-env';
9
+ import { shallowEqual } from 'fast-equals';
10
+ export let value = void 0;
11
+ export let rows = void 0;
12
+ export let invalidExtensionMessage = void 0;
13
+ export let extensions = void 0;
14
+ let internalValue;
15
+ function updateInternalValueFromValue() {
16
+ const newInternalValue = value ?? '';
17
+ if (!shallowEqual(internalValue, newInternalValue)) {
18
+ internalValue = newInternalValue;
19
+ }
20
+ }
21
+ function updateValueFromInternalValue() {
22
+ if (internalValue instanceof File) {
23
+ if (!shallowEqual(value, internalValue)) {
24
+ value = internalValue;
25
+ }
26
+ } else if (value !== void 0) {
27
+ value = void 0;
28
+ }
29
+ }
30
+ let options;
31
+ $: options = {
32
+ extensions,
33
+ filetypes: extensions,
34
+ invalidFiletypeMessage: invalidExtensionMessage,
35
+ lineCount: rows,
36
+ view: 'file-input'
37
+ };
38
+ $: value, updateInternalValueFromValue();
39
+ $: internalValue, updateValueFromInternalValue();
40
+ </script>
41
+
42
+ <GenericInput
43
+ bind:value={internalValue}
44
+ on:change
45
+ {options}
46
+ plugin={pluginModule}
47
+ {...$$restProps}
48
+ />
49
+ {#if !BROWSER}
50
+ {#if rows}
51
+ <ClsPad keysAdd={fillWith('containerUnitSize', rows)} theme={$$props.theme} />
52
+ {:else}
53
+ <ClsPad keysAdd={fillWith('containerUnitSize', 3)} theme={$$props.theme} />
54
+ {/if}
55
+ {/if}
@@ -0,0 +1,204 @@
1
+ import { SvelteComponent } from 'svelte';
2
+ import type { ValueChangeEvent } from '../utils.js';
3
+ export type FileValue = File | undefined;
4
+ export type FileChangeEvent = ValueChangeEvent<FileValue>;
5
+ declare const __propDef: {
6
+ props: {
7
+ /**
8
+ * File data, or `undefined` to clear the file input.
9
+ * @default `undefined`
10
+ * @bindable
11
+ */
12
+ value?: FileValue;
13
+ /**
14
+ * Array of valid file extensions.
15
+ * @default Any file extension
16
+ */
17
+ extensions?: string[] | undefined;
18
+ /**
19
+ * String shown when the user tries to upload an invalid filetype.
20
+ * @default `'Unaccepted file type.'`
21
+ */
22
+ invalidExtensionMessage?: string | undefined;
23
+ /**
24
+ * Height of the file input drop zone, in rows.
25
+ * @default `3`
26
+ */
27
+ rows?: number | undefined;
28
+ } & Omit<
29
+ {
30
+ /**
31
+ * File data, or `undefined` to clear the file input.
32
+ * @default `undefined`
33
+ * @bindable
34
+ */
35
+ value: string | File | null;
36
+ } & Omit<
37
+ {
38
+ /**
39
+ * The binding's target object with values to manipulate.
40
+ * @bindable
41
+ */
42
+ object: import('@tweakpane/core').Bindable & Record<string, string | File | null>;
43
+ /** The key for the value in the target `object` that the control should manipulate. */
44
+ key: string;
45
+ /**
46
+ * Prevent interactivity and gray out the control.
47
+ * @default `false`
48
+ */
49
+ disabled?: boolean;
50
+ /**
51
+ * Text displayed next to control.
52
+ * @default `undefined`
53
+ */
54
+ label?: string | undefined;
55
+ /**
56
+ * Tweakpane's internal options object.
57
+ *
58
+ * See [`BindingParams`](https://tweakpane.github.io/docs/api/types/BindingParams.html).
59
+ *
60
+ * Valid types are contingent on the type of the value `key` points to in `object`.
61
+ *
62
+ * This is intended internal use, when implementing convenience components wrapping Binding's
63
+ * functionality. Options of interest are instead exposed as top-level props in _Svelte
64
+ * Tweakpane UI_.
65
+ * @default `undefined`
66
+ */
67
+ options?: import('@tweakpane/core').BaseInputParams | undefined;
68
+ /**
69
+ * Custom color scheme.
70
+ *
71
+ * @default `undefined` \
72
+ * Inherits default Tweakpane theme equivalent to `ThemeUtils.presets.standard`, or the theme
73
+ * set with `setGlobalDefaultTheme()`.
74
+ */
75
+ theme?: import('..').Theme | undefined;
76
+ /**
77
+ * Reference to internal Tweakpane
78
+ * [`BindingApi`](https://tweakpane.github.io/docs/api/classes/_internal_.BindingApi.html) for
79
+ * this control.
80
+ *
81
+ * This property is exposed for advanced use cases only, such as when implementing convenience
82
+ * components wrapping `<Binding>`'s functionality.
83
+ *
84
+ * Direct manipulation of Tweakpane's internals can break _Svelte Tweakpane UI_ abstractions.
85
+ *
86
+ * @bindable
87
+ * @readonly
88
+ */
89
+ ref?: import('../internal/GenericInput.svelte').GenericInputRef | undefined;
90
+ /**
91
+ * Imported Tweakpane `TpPluginBundle` (aliased as `Plugin`) module to automatically register in
92
+ * the `<Binding>`'s containing `<Pane>`.
93
+ *
94
+ * This property is exposed for advanced use cases only, such as when implementing convenience
95
+ * components wrapping `<Binding>`'s functionality in combination with a Tweakpane plugin.
96
+ *
97
+ * Direct manipulation of Tweakpane's internals can break _Svelte Tweakpane UI_ abstractions.
98
+ *
99
+ * @default `undefined`
100
+ */
101
+ plugin?: import('../utils.js').Plugin | undefined;
102
+ },
103
+ 'object' | 'key'
104
+ >,
105
+ 'ref' | 'plugin' | 'value'
106
+ >;
107
+ slots: {};
108
+ events: {
109
+ /**
110
+ * Fires when `value` changes.
111
+ *
112
+ * _This event is provided for advanced use cases. It's usually preferred to bind to the `value` prop instead._
113
+ *
114
+ * The `event.details` payload includes a copy of the value and an `origin` field to distinguish between user-interactive changes (`internal`)
115
+ * and changes resulting from programmatic manipulation of the `value` (`external`).
116
+ *
117
+ * @extends ValueChangeEvent
118
+ * @event
119
+ * */
120
+ change: FileChangeEvent;
121
+ };
122
+ };
123
+ export type FileProps = typeof __propDef.props;
124
+ export type FileEvents = typeof __propDef.events;
125
+ export type FileSlots = typeof __propDef.slots;
126
+ /**
127
+ * A file input control.
128
+ *
129
+ * _Important: This component has some rough edges, and should be considered experimental._
130
+ *
131
+ * Integrates the [File Input](https://github.com/LuchoTurtle/tweakpane-plugin-file-import/blob/main/src/plugin.ts) control from [LuchoTurtle's](https://github.com/LuchoTurtle) [tweakpane-plugin-file-import](https://github.com/LuchoTurtle/tweakpane-plugin-file-import) plugin. Some of the control's parameter names have been changed for consistency with the `<Image>` CompositionEvent.
132
+ *
133
+ * Use the `<Image>` control instead if you're working with images and want to see a thumbnail preview of the image.
134
+ *
135
+ * There is currently a known bug where change events' `origin` values are sometimes incorrect. (This issue is limited to this component.)
136
+ *
137
+ * Usage outside of a `<Pane>` component will implicitly wrap the image control in `<Pane position="inline">`.
138
+ *
139
+ * Note that _Svelte Tweakpane UI_ embeds a functionally identical [fork](https://github.com/kitschpatrol/tweakpane-plugin-file-import) of the plugin with build optimizations.
140
+ *
141
+ *
142
+ * @emits {FileChangeEvent} change - When `value` changes. (This event is provided for advanced use cases. Prefer binding to `value`.)
143
+ *
144
+ * @example
145
+ * ```svelte
146
+ * <script lang="ts">
147
+ * import { File, type FileValue } from '..';
148
+ *
149
+ * let file: FileValue;
150
+ *
151
+ * async function getFileBase64(file: FileValue): Promise<string> {
152
+ * if (file === undefined) return 'Your bytes here...';
153
+ * return new Promise((resolve, reject) => {
154
+ * const reader = new FileReader();
155
+ * reader.addEventListener('load', () => {
156
+ * const { result } = reader;
157
+ * if (result && typeof result === 'string') resolve(result);
158
+ * else reject(new Error('Empty result'));
159
+ * });
160
+ * reader.addEventListener('error', reject);
161
+ * reader.readAsDataURL(file);
162
+ * });
163
+ * }
164
+ *
165
+ * function truncate(text: string, length: number) {
166
+ * return text.length > length ? text.slice(0, length - 1) + '...' : text;
167
+ * }
168
+ * </script>
169
+ *
170
+ * <File bind:value={file} label="File" />
171
+ *
172
+ * <div class="demo">
173
+ * <p>
174
+ * {#await getFileBase64(file)}
175
+ * Loading...
176
+ * {:then value}
177
+ * {truncate(value, 512)}
178
+ * {/await}
179
+ * </p>
180
+ * </div>
181
+ *
182
+ * <style>
183
+ * .demo {
184
+ * width: 100%;
185
+ * background: linear-gradient(45deg, orange, magenta);
186
+ * }
187
+ *
188
+ * .demo > p {
189
+ * margin: 0;
190
+ * padding: 0.5rem;
191
+ * font-family: monospace;
192
+ * line-height: 1.2;
193
+ * color: white;
194
+ * word-break: break-all;
195
+ * white-space: pre-wrap;
196
+ * }
197
+ * </style>
198
+ * ```
199
+ *
200
+ * @sourceLink
201
+ * [File.svelte](https://github.com/kitschpatrol/svelte-tweakpane-ui/blob/main/src/lib/control/File.svelte)
202
+ */
203
+ export default class File extends SvelteComponent<FileProps, FileEvents, FileSlots> {}
204
+ export {};
@@ -6,18 +6,52 @@
6
6
  import { fillWith } from '../utils';
7
7
  import * as pluginModule from '@kitschpatrol/tweakpane-plugin-image';
8
8
  import { BROWSER } from 'esm-env';
9
- export let value = 'placeholder';
9
+ import { shallowEqual } from 'fast-equals';
10
+ export let value = void 0;
10
11
  export let fit = void 0;
11
12
  export let extensions = void 0;
13
+ let internalValue = 'placeholder';
14
+ function updateInternalValueFromValue() {
15
+ const newInternalValue = value ?? 'placeholder';
16
+ if (!shallowEqual(internalValue, newInternalValue)) {
17
+ internalValue = newInternalValue;
18
+ }
19
+ }
20
+ function updateValueFromInternalValue() {
21
+ if (internalValue === 'placeholder') {
22
+ if (value !== void 0) {
23
+ value = void 0;
24
+ }
25
+ } else if (internalValue instanceof HTMLImageElement) {
26
+ if (value !== internalValue.src) {
27
+ value = internalValue.src;
28
+ }
29
+ } else if (internalValue instanceof File) {
30
+ console.warn('Image control does not support File objects.');
31
+ if (value !== void 0) {
32
+ value = void 0;
33
+ }
34
+ } else if (value !== internalValue) {
35
+ value = internalValue;
36
+ }
37
+ }
12
38
  let options;
13
39
  $: options = {
14
40
  extensions,
15
41
  imageFit: fit,
16
42
  view: 'input-image'
17
43
  };
44
+ $: value, updateInternalValueFromValue();
45
+ $: internalValue, updateValueFromInternalValue();
18
46
  </script>
19
47
 
20
- <GenericInput bind:value on:change {options} plugin={pluginModule} {...$$restProps} />
48
+ <GenericInput
49
+ bind:value={internalValue}
50
+ on:change
51
+ {options}
52
+ plugin={pluginModule}
53
+ {...$$restProps}
54
+ />
21
55
  {#if !BROWSER}
22
56
  <ClsPad keysAdd={fillWith('containerVerticalPadding', 2)} theme={$$props.theme} />
23
57
  {/if}
@@ -1,12 +1,12 @@
1
1
  import { SvelteComponent } from 'svelte';
2
2
  import type { ValueChangeEvent } from '../utils.js';
3
- export type ImageValue = 'placeholder' | File | HTMLImageElement | string | undefined;
3
+ export type ImageValue = string | undefined;
4
4
  export type ImageChangeEvent = ValueChangeEvent<ImageValue>;
5
5
  declare const __propDef: {
6
6
  props: {
7
7
  /**
8
- * Image data
9
- * @default `'placeholder'`
8
+ * Image data as Base64-encoded string, or `undefined` to clear.
9
+ * @default `'undefined'`
10
10
  * @bindable
11
11
  */
12
12
  value?: ImageValue;
@@ -25,18 +25,19 @@ declare const __propDef: {
25
25
  } & Omit<
26
26
  {
27
27
  /**
28
- * Image data
29
- * @default `'placeholder'`
28
+ * Image data as Base64-encoded string, or `undefined` to clear.
29
+ * @default `'undefined'`
30
30
  * @bindable
31
31
  */
32
- value: ImageValue;
32
+ value: string | HTMLImageElement | File | undefined;
33
33
  } & Omit<
34
34
  {
35
35
  /**
36
36
  * The binding's target object with values to manipulate.
37
37
  * @bindable
38
38
  */
39
- object: import('@tweakpane/core').Bindable & Record<string, ImageValue>;
39
+ object: import('@tweakpane/core').Bindable &
40
+ Record<string, string | HTMLImageElement | File | undefined>;
40
41
  /** The key for the value in the target `object` that the control should manipulate. */
41
42
  key: string;
42
43
  /**
@@ -130,6 +131,8 @@ export type ImageSlots = typeof __propDef.slots;
130
131
  * Dias](https://www.linkedin.com/in/matheusdbs/), [Palash Bansal](https://github.com/repalash), and
131
132
  * others.
132
133
  *
134
+ * Use the `<File>` control instead if you're working with other file types, or don't wish to display a thumbnail preview of an uploaded image.
135
+ *
133
136
  * There is currently a known bug where change events' `origin` values are sometimes incorrect. (This issue is limited to this component.)
134
137
  *
135
138
  * Usage outside of a `<Pane>` component will implicitly wrap the image control in `<Pane
@@ -142,9 +145,9 @@ export type ImageSlots = typeof __propDef.slots;
142
145
  * @example
143
146
  * ```svelte
144
147
  * <script lang="ts">
145
- * import { Button, Image } from 'svelte-tweakpane-ui';
148
+ * import { Button, Image, type ImageValue } from '..';
146
149
  *
147
- * let source = 'placeholder';
150
+ * let source: ImageValue;
148
151
  *
149
152
  * async function getRandomKittenUrl() {
150
153
  * const { url } = await fetch('https://loremflickr.com/800/800/kitten', {
@@ -165,9 +168,9 @@ export type ImageSlots = typeof __propDef.slots;
165
168
  * />
166
169
  *
167
170
  * <div class="demo">
168
- * {#if source === 'placeholder'}
171
+ * {#if source === undefined}
169
172
  * <p>Tap “No Image” above to load an image from disk.</p>
170
- * {:else}
173
+ * {:else if typeof source === 'string'}
171
174
  * <img alt="" src={source} />
172
175
  * {/if}
173
176
  * </div>
@@ -2,6 +2,7 @@
2
2
 
3
3
  <script>
4
4
  import GenericSlider from '../internal/GenericSlider.svelte';
5
+ import {} from 'svelte';
5
6
  export let value;
6
7
  export let wide = void 0;
7
8
  let ref;
@@ -16,4 +17,4 @@
16
17
  $: ref && wide !== void 0 && updateWide(wide);
17
18
  </script>
18
19
 
19
- <GenericSlider bind:value bind:ref bind:wide on:change {...$$restProps} />
20
+ <GenericSlider bind:value bind:ref on:change {...$$restProps} />
@@ -0,0 +1,30 @@
1
+ <script context="module"></script>
2
+
3
+ <script>
4
+ import GenericSlider from '../internal/GenericSlider.svelte';
5
+ import * as pluginModule from '@kitschpatrol/tweakpane-plugin-inputs';
6
+ export let value;
7
+ export let wide = void 0;
8
+ let options;
9
+ $: options = {
10
+ view: 'stepper'
11
+ };
12
+ let ref;
13
+ function updateWide(wide2) {
14
+ const inputField = ref?.element.querySelector('div.tp-stepv_t');
15
+ const buttonContainer = ref?.element.querySelector('div.tp-stepv_s');
16
+ const buttons = buttonContainer?.querySelectorAll('button');
17
+ if (wide2) {
18
+ inputField?.style.setProperty('display', 'none');
19
+ buttonContainer?.style.setProperty('flex', '1');
20
+ for (const button of buttons ?? []) {
21
+ button.style.setProperty('flex', '1');
22
+ }
23
+ } else {
24
+ inputField?.style.removeProperty('display');
25
+ }
26
+ }
27
+ $: ref && wide !== void 0 && updateWide(wide);
28
+ </script>
29
+
30
+ <GenericSlider bind:value bind:ref on:change {options} plugin={pluginModule} {...$$restProps} />
@@ -0,0 +1,194 @@
1
+ import { SvelteComponent } from 'svelte';
2
+ import type { ValueChangeEvent } from '../utils.js';
3
+ import type { SliderInputBindingApi as GenericSliderRef } from 'tweakpane';
4
+ export type StepperChangeEvent = ValueChangeEvent<number>;
5
+ declare const __propDef: {
6
+ props: {
7
+ /**
8
+ * A `number` value to control.
9
+ * @bindable
10
+ * */
11
+ value: number;
12
+ } & Omit<
13
+ {
14
+ /**
15
+ * Minimum value.
16
+ *
17
+ * Specifying both a `min` and a `max` prop turns the control into a slider.
18
+ * @default `undefined`
19
+ */
20
+ min?: number;
21
+ /**
22
+ * Maximum value.
23
+ *
24
+ * Specifying both a `min` and a `max` prop turns the control into a slider.
25
+ * @default `undefined`
26
+ */
27
+ max?: number;
28
+ /**
29
+ * A function to customize the point value's string representation (e.g. rounding, etc.).
30
+ * @default `undefined` \
31
+ * Normal `.toString()` formatting.
32
+ */
33
+ format?: ((value: number) => string) | undefined;
34
+ /**
35
+ * The unit scale for key-based input for all dimensions (e.g. the up and down arrow keys).
36
+ * @default `1` \
37
+ * Or `stepValue` if defined.
38
+ */
39
+ keyScale?: number;
40
+ /**
41
+ * The unit scale for pointer-based input for all dimensions.
42
+ * @default `undefined` \
43
+ * [Dynamic based on magnitude of
44
+ * `value`](https://github.com/cocopon/tweakpane/blob/66dfbea04bfe9b7f031673c955ceda1f92356e75/packages/core/src/common/number/util.ts#L54).
45
+ */
46
+ pointerScale?: number;
47
+ /**
48
+ * The minimum step interval.
49
+ * @default `undefined` \
50
+ * No step constraint.
51
+ */
52
+ step?: number;
53
+ /**
54
+ * When `true`, expand the width of the control at the expense of the numeric input
55
+ * field.
56
+ * @default `false`
57
+ */
58
+ wide?: boolean;
59
+ } & {
60
+ /**
61
+ * A `number` value to control.
62
+ * @bindable
63
+ */
64
+ value: number;
65
+ } & Omit<
66
+ {
67
+ /**
68
+ * The binding's target object with values to manipulate.
69
+ * @bindable
70
+ */
71
+ object: import('@tweakpane/core').Bindable & Record<string, number>;
72
+ /** The key for the value in the target `object` that the control should manipulate. */
73
+ key: string;
74
+ /**
75
+ * Prevent interactivity and gray out the control.
76
+ * @default `false`
77
+ */
78
+ disabled?: boolean;
79
+ /**
80
+ * Text displayed next to control.
81
+ * @default `undefined`
82
+ */
83
+ label?: string | undefined;
84
+ /**
85
+ * Tweakpane's internal options object.
86
+ *
87
+ * See [`BindingParams`](https://tweakpane.github.io/docs/api/types/BindingParams.html).
88
+ *
89
+ * Valid types are contingent on the type of the value `key` points to in `object`.
90
+ *
91
+ * This is intended internal use, when implementing convenience components wrapping Binding's
92
+ * functionality. Options of interest are instead exposed as top-level props in _Svelte
93
+ * Tweakpane UI_.
94
+ * @default `undefined`
95
+ */
96
+ options?: import('tweakpane').NumberInputParams | undefined;
97
+ /**
98
+ * Custom color scheme.
99
+ *
100
+ * @default `undefined` \
101
+ * Inherits default Tweakpane theme equivalent to `ThemeUtils.presets.standard`, or the theme
102
+ * set with `setGlobalDefaultTheme()`.
103
+ */
104
+ theme?: import('..').Theme | undefined;
105
+ /**
106
+ * Reference to internal Tweakpane
107
+ * [`BindingApi`](https://tweakpane.github.io/docs/api/classes/_internal_.BindingApi.html) for
108
+ * this control.
109
+ *
110
+ * This property is exposed for advanced use cases only, such as when implementing convenience
111
+ * components wrapping `<Binding>`'s functionality.
112
+ *
113
+ * Direct manipulation of Tweakpane's internals can break _Svelte Tweakpane UI_ abstractions.
114
+ *
115
+ * @bindable
116
+ * @readonly
117
+ */
118
+ ref?: GenericSliderRef | undefined;
119
+ /**
120
+ * Imported Tweakpane `TpPluginBundle` (aliased as `Plugin`) module to automatically register in
121
+ * the `<Binding>`'s containing `<Pane>`.
122
+ *
123
+ * This property is exposed for advanced use cases only, such as when implementing convenience
124
+ * components wrapping `<Binding>`'s functionality in combination with a Tweakpane plugin.
125
+ *
126
+ * Direct manipulation of Tweakpane's internals can break _Svelte Tweakpane UI_ abstractions.
127
+ *
128
+ * @default `undefined`
129
+ */
130
+ plugin?: import('../utils.js').Plugin | undefined;
131
+ },
132
+ 'object' | 'key'
133
+ >,
134
+ 'ref' | 'options' | 'plugin' | 'amount'
135
+ >;
136
+ slots: {};
137
+ events: {
138
+ /**
139
+ * Fires when `value` changes.
140
+ *
141
+ * _This event is provided for advanced use cases. It's usually preferred to bind to the `value` prop instead._
142
+ *
143
+ * The `event.details` payload includes a copy of the value and an `origin` field to distinguish between user-interactive changes (`internal`)
144
+ * and changes resulting from programmatic manipulation of the `value` (`external`).
145
+ *
146
+ * @extends ValueChangeEvent
147
+ * @event
148
+ * */
149
+ change: StepperChangeEvent;
150
+ };
151
+ };
152
+ export type StepperProps = typeof __propDef.props;
153
+ export type StepperEvents = typeof __propDef.events;
154
+ export type StepperSlots = typeof __propDef.slots;
155
+ /**
156
+ * A control for simple incremental value changes.
157
+ *
158
+ * Similar in functionality to a `<Slider>`, but with nice big buttons to increment and decrement the value.
159
+ *
160
+ * Integrates the [Stepper](https://github.com/tallneil/tweakpane-plugin-inputs/blob/main/src/stepper/plugin.ts)
161
+ * control from [Neil Shankar's](https://tallneil.io/) ["Inputs for Tweakpane
162
+ * " plugin](https://github.com/tallneil/tweakpane-plugin-inputs).
163
+ *
164
+ * Usage outside of a `<Pane>` component will implicitly wrap the stepper in `<Pane position="inline">`.
165
+ *
166
+ * Note that _Svelte Tweakpane UI_ embeds a functionally identical [fork](https://github.com/kitschpatrol/tweakpane-plugin-inputs) of the plugin with build optimizations.
167
+ *
168
+ * @emits {StepperChangeEvent} change - When `value` changes. (This event is provided for advanced use cases. Prefer binding to `value`.)
169
+ *
170
+ * @example
171
+ * ```svelte
172
+ * <script lang="ts">
173
+ * import { Stepper } from 'svelte-tweakpane-ui';
174
+ * let angle = 45;
175
+ * </script>
176
+ *
177
+ * <Stepper bind:value={angle} label="Angle" step={45} />
178
+ *
179
+ * <div class="demo" style:--a="{angle}deg"></div>
180
+ *
181
+ * <style>
182
+ * div.demo {
183
+ * aspect-ratio: 1;
184
+ * width: 100%;
185
+ * background: linear-gradient(var(--a), magenta, orange);
186
+ * }
187
+ * </style>
188
+ * ```
189
+ *
190
+ * @sourceLink
191
+ * [Stepper.svelte](https://github.com/kitschpatrol/svelte-tweakpane-ui/blob/main/src/lib/control/Stepper.svelte)
192
+ */
193
+ export default class Stepper extends SvelteComponent<StepperProps, StepperEvents, StepperSlots> {}
194
+ export {};
@@ -44,17 +44,31 @@
44
44
  _ref?.dispose();
45
45
  });
46
46
  const dispatch = createEventDispatcher();
47
+ function safeCopy(value) {
48
+ if (value instanceof File) {
49
+ return new File([value], value.name, {
50
+ lastModified: value.lastModified,
51
+ type: value.type
52
+ });
53
+ }
54
+ if (BROWSER && value instanceof HTMLImageElement) {
55
+ const copy2 = new Image();
56
+ copy2.src = value.src;
57
+ return copy2;
58
+ }
59
+ return copy(value);
60
+ }
47
61
  let lastObject = object;
48
- let lastValue = copy(object[key]);
62
+ let lastValue = safeCopy(object[key]);
49
63
  let internalChange = false;
50
64
  function onBoundValueChange(object2) {
51
65
  if (lastObject !== object2) {
52
66
  internalChange = false;
53
67
  }
54
68
  if (!shallowEqual(object2[key], lastValue)) {
55
- lastValue = copy(object2[key]);
69
+ lastValue = safeCopy(object2[key]);
56
70
  dispatch('change', {
57
- value: copy(object2[key]),
71
+ value: safeCopy(object2[key]),
58
72
  origin: internalChange ? 'internal' : 'external'
59
73
  });
60
74
  if (!internalChange && _ref) {
@@ -71,7 +85,7 @@
71
85
  }
72
86
  function onTweakpaneChange() {
73
87
  internalChange = true;
74
- object[key] = copy(object[key]);
88
+ object[key] = safeCopy(object[key]);
75
89
  }
76
90
  $: DEV && enforceReadonly(_ref, ref, 'Binding', 'ref', true);
77
91
  $: options, $parentStore !== void 0 && index !== void 0 && create();
package/dist/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export {
18
18
  type CubicBezierValueTuple,
19
19
  default as CubicBezier
20
20
  } from './control/CubicBezier.svelte';
21
+ export { default as File, type FileChangeEvent, type FileValue } from './control/File.svelte';
21
22
  export { default as Image, type ImageChangeEvent, type ImageValue } from './control/Image.svelte';
22
23
  export {
23
24
  default as IntervalSlider,
@@ -74,6 +75,7 @@ export {
74
75
  type RotationQuaternionValueTuple
75
76
  } from './control/RotationQuaternion.svelte';
76
77
  export { default as Slider, type SliderChangeEvent } from './control/Slider.svelte';
78
+ export { default as Stepper, type StepperChangeEvent } from './control/Stepper.svelte';
77
79
  export { default as Text, type TextChangeEvent } from './control/Text.svelte';
78
80
  export { default as Textarea, type TextareaChangeEvent } from './control/Textarea.svelte';
79
81
  export { default as Wheel, type WheelChangeEvent } from './control/Wheel.svelte';
package/dist/index.js CHANGED
@@ -1,11 +1,9 @@
1
- // Components
2
1
  export { default as Button } from './control/Button.svelte';
3
- // Essentials (1st party plugins)
4
2
  export { default as ButtonGrid } from './control/ButtonGrid.svelte';
5
3
  export { default as Checkbox } from './control/Checkbox.svelte';
6
4
  export { default as Color } from './control/Color.svelte';
7
5
  export { default as CubicBezier } from './control/CubicBezier.svelte';
8
- // Additional plugins (3rd party / community)
6
+ export { default as File } from './control/File.svelte';
9
7
  export { default as Image } from './control/Image.svelte';
10
8
  export { default as IntervalSlider } from './control/IntervalSlider.svelte';
11
9
  export { default as List } from './control/List.svelte';
@@ -16,10 +14,10 @@ export { default as Ring } from './control/Ring.svelte';
16
14
  export { default as RotationEuler } from './control/RotationEuler.svelte';
17
15
  export { default as RotationQuaternion } from './control/RotationQuaternion.svelte';
18
16
  export { default as Slider } from './control/Slider.svelte';
17
+ export { default as Stepper } from './control/Stepper.svelte';
19
18
  export { default as Text } from './control/Text.svelte';
20
19
  export { default as Textarea } from './control/Textarea.svelte';
21
20
  export { default as Wheel } from './control/Wheel.svelte';
22
- // Core (tweakpane building blocks)
23
21
  export { default as Binding } from './core/Binding.svelte';
24
22
  export { default as Blade } from './core/Blade.svelte';
25
23
  export { default as Folder } from './core/Folder.svelte';
@@ -27,7 +25,6 @@ export { default as Pane } from './core/Pane.svelte';
27
25
  export { default as Separator } from './core/Separator.svelte';
28
26
  export { default as TabGroup } from './core/TabGroup.svelte';
29
27
  export { default as TabPage } from './core/TabPage.svelte';
30
- // Extra (svelte convenience components)
31
28
  export { default as AutoObject } from './extra/AutoObject.svelte';
32
29
  export { default as AutoValue } from './extra/AutoValue.svelte';
33
30
  export { default as Element } from './extra/Element.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelte-tweakpane-ui",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "type": "module",
5
5
  "description": "A Svelte component library wrapping UI elements from Tweakpane, plus some additional functionality for convenience and flexibility.",
6
6
  "repository": {
@@ -47,6 +47,10 @@
47
47
  "types": "./dist/control/CubicBezier.svelte.d.ts",
48
48
  "svelte": "./dist/control/CubicBezier.svelte"
49
49
  },
50
+ "./File.svelte": {
51
+ "types": "./dist/control/File.svelte.d.ts",
52
+ "svelte": "./dist/control/File.svelte"
53
+ },
50
54
  "./Image.svelte": {
51
55
  "types": "./dist/control/Image.svelte.d.ts",
52
56
  "svelte": "./dist/control/Image.svelte"
@@ -83,6 +87,10 @@
83
87
  "types": "./dist/control/Slider.svelte.d.ts",
84
88
  "svelte": "./dist/control/Slider.svelte"
85
89
  },
90
+ "./Stepper.svelte": {
91
+ "types": "./dist/control/Stepper.svelte.d.ts",
92
+ "svelte": "./dist/control/Stepper.svelte"
93
+ },
86
94
  "./Text.svelte": {
87
95
  "types": "./dist/control/Text.svelte.d.ts",
88
96
  "svelte": "./dist/control/Text.svelte"
@@ -187,49 +195,51 @@
187
195
  "dependencies": {
188
196
  "@kitschpatrol/tweakpane-plugin-camerakit": "0.3.1-beta.2",
189
197
  "@kitschpatrol/tweakpane-plugin-essentials": "0.2.2-beta.2",
198
+ "@kitschpatrol/tweakpane-plugin-file-import": "1.1.2-beta.1",
190
199
  "@kitschpatrol/tweakpane-plugin-image": "2.0.1-beta.4",
200
+ "@kitschpatrol/tweakpane-plugin-inputs": "1.0.4-beta.3",
191
201
  "@kitschpatrol/tweakpane-plugin-profiler": "0.4.2-beta.2",
192
202
  "@kitschpatrol/tweakpane-plugin-rotation": "0.2.1-beta.1",
193
203
  "@kitschpatrol/tweakpane-plugin-textarea": "2.0.1-beta.1",
194
204
  "@kitschpatrol/tweakpane-plugin-waveform": "1.0.4-beta.2",
195
- "@tweakpane/core": "2.0.4",
196
- "esm-env": "1.0.0",
197
- "fast-copy": "3.0.2",
198
- "fast-equals": "5.0.1",
199
- "nanoid": "5.0.7",
200
- "svelte-persisted-store": "0.11.0",
201
- "tweakpane": "4.0.4"
205
+ "@tweakpane/core": "2.0.5",
206
+ "esm-env": "^1.2.1",
207
+ "fast-copy": "^3.0.2",
208
+ "fast-equals": "^5.0.1",
209
+ "nanoid": "^5.0.9",
210
+ "svelte-persisted-store": "0.12.0",
211
+ "tweakpane": "4.0.5"
202
212
  },
203
213
  "devDependencies": {
204
- "@kitschpatrol/shared-config": "4.7.11",
205
- "@phenomnomnominal/tsquery": "6.1.3",
206
- "@playwright/test": "1.48.1",
207
- "@stkb/rewrap": "0.1.0",
208
- "@sveltejs/adapter-static": "3.0.5",
209
- "@sveltejs/kit": "2.7.2",
210
- "@sveltejs/package": "2.3.5",
211
- "@sveltejs/vite-plugin-svelte": "3.1.2",
212
- "@types/eslint": "8.56.12",
213
- "@types/fs-extra": "11.0.4",
214
- "@types/node": "18.19.57",
215
- "bumpp": "9.7.1",
216
- "eslint": "8.57.1",
217
- "fs-extra": "11.2.0",
218
- "glob": "11.0.0",
219
- "postcss-html": "1.7.0",
220
- "publint": "0.2.11",
221
- "read-package-up": "11.0.0",
222
- "remark-mdat": "0.7.3",
223
- "svelte": "4.2.19",
224
- "svelte-check": "4.0.5",
214
+ "@kitschpatrol/shared-config": "^4.7.12",
215
+ "@phenomnomnominal/tsquery": "^6.1.3",
216
+ "@playwright/test": "^1.49.1",
217
+ "@stkb/rewrap": "^0.1.0",
218
+ "@sveltejs/adapter-static": "^3.0.6",
219
+ "@sveltejs/kit": "^2.10.1",
220
+ "@sveltejs/package": "^2.3.7",
221
+ "@sveltejs/vite-plugin-svelte": "^3.1.2",
222
+ "@types/eslint": "^8.56.12",
223
+ "@types/fs-extra": "^11.0.4",
224
+ "@types/node": "^18.19.68",
225
+ "bumpp": "^9.9.0",
226
+ "eslint": "^8.57.1",
227
+ "fs-extra": "^11.2.0",
228
+ "glob": "^11.0.0",
229
+ "postcss-html": "^1.7.0",
230
+ "publint": "^0.2.12",
231
+ "read-package-up": "^11.0.0",
232
+ "remark-mdat": "^0.7.4",
233
+ "svelte": "^4.2.19",
234
+ "svelte-check": "^4.1.1",
225
235
  "svelte-language-server": "0.17.0",
226
- "svelte2tsx": "0.7.22",
227
- "ts-morph": "24.0.0",
228
- "tslib": "2.8.0",
229
- "tsx": "4.19.1",
230
- "typescript": "5.6.3",
231
- "vite": "5.4.9",
232
- "yaml": "2.6.0"
236
+ "svelte2tsx": "^0.7.30",
237
+ "ts-morph": "^24.0.0",
238
+ "tslib": "^2.8.1",
239
+ "tsx": "^4.19.2",
240
+ "typescript": "~5.6.3",
241
+ "vite": "^5.4.11",
242
+ "yaml": "^2.6.1"
233
243
  },
234
244
  "publishConfig": {
235
245
  "access": "public"
package/readme.md CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ## Overview
36
36
 
37
- 🎛️ **_Svelte Tweakpane UI_** wraps user-interface elements from the excellent [Tweakpane](https://tweakpane.github.io/docs/) library in a collection of <!-- component-count -->31<!-- /component-count --> idiomatic, reactive, type-safe, carefully-crafted, and obsessively-documented [Svelte](https://svelte.dev) components.
37
+ 🎛️ **_Svelte Tweakpane UI_** wraps user-interface elements from the excellent [Tweakpane](https://tweakpane.github.io/docs/) library in a collection of <!-- component-count -->33<!-- /component-count --> idiomatic, reactive, type-safe, carefully-crafted, and obsessively-documented [Svelte](https://svelte.dev) components.
38
38
 
39
39
  The library makes it easy to quickly and declaratively add knobs and dials to your projects using components that feel like they were made for Svelte. It also augments Tweakpane with a few [extra features](https://kitschpatrol.com/svelte-tweakpane-ui/docs/features) for your convenience and enjoyment.
40
40
 
@@ -102,6 +102,8 @@ npm install svelte-tweakpane-ui
102
102
  A color picker.
103
103
  - **[CubicBezier](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/cubicbezier)**\
104
104
  A control for editing a bezier curve. Ideal for tweaking animation easing values.
105
+ - **[File](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/file)**\
106
+ A file input control.
105
107
  - **[Image](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/image)**\
106
108
  An image input control.
107
109
  - **[IntervalSlider](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/intervalslider)**\
@@ -120,6 +122,8 @@ npm install svelte-tweakpane-ui
120
122
  Integrates the [quaternion rotation](https://github.com/0b5vr/tweakpane-plugin-rotation/blob/dev/src/RotationInputPluginQuaternion.ts) control from [0b5vr](https://0b5vr.com)'s [tweakpane-plugin-rotation](https://github.com/0b5vr/tweakpane-plugin-rotation).
121
123
  - **[Slider](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/slider)**\
122
124
  A slider component providing fine-grained control over numeric values.
125
+ - **[Stepper](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/stepper)**\
126
+ A control for simple incremental value changes.
123
127
  - **[Text](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/text)**\
124
128
  A text field, in the spirit of the HTML `<input type="text">` element.
125
129
  - **[Textarea](https://kitschpatrol.com/svelte-tweakpane-ui/docs/components/textarea)**\