@stanko/ctrls 0.1.9 → 0.3.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.
- package/README.md +35 -1
- package/dist/ctrls/ctrl-boolean.d.ts +5 -3
- package/dist/ctrls/ctrl-boolean.js +4 -2
- package/dist/ctrls/ctrl-dual-range.d.ts +5 -3
- package/dist/ctrls/ctrl-dual-range.js +4 -2
- package/dist/ctrls/ctrl-easing.d.ts +5 -3
- package/dist/ctrls/ctrl-easing.js +8 -6
- package/dist/ctrls/ctrl-radio.d.ts +6 -4
- package/dist/ctrls/ctrl-radio.js +7 -5
- package/dist/ctrls/ctrl-range.d.ts +5 -3
- package/dist/ctrls/ctrl-range.js +4 -2
- package/dist/ctrls/ctrl-seed.d.ts +5 -3
- package/dist/ctrls/ctrl-seed.js +5 -3
- package/dist/ctrls/index.d.ts +40 -9
- package/dist/ctrls/index.js +94 -41
- package/dist/ctrls.css +46 -6
- package/dist/ctrls.css.map +1 -1
- package/dist/utils/alea.d.ts +1 -1
- package/dist/utils/alea.js +34 -25
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -332,6 +332,40 @@ Example:
|
|
|
332
332
|
|
|
333
333
|
</div>
|
|
334
334
|
|
|
335
|
+
### Group
|
|
336
|
+
|
|
337
|
+
Collapsible group of controls. All values are going to be nested in an object using the group's name.
|
|
338
|
+
|
|
339
|
+
```ts
|
|
340
|
+
{
|
|
341
|
+
// Mandatory
|
|
342
|
+
controls: ConfigItem[]
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Example:
|
|
347
|
+
|
|
348
|
+
<div class="example">
|
|
349
|
+
|
|
350
|
+
```json
|
|
351
|
+
{
|
|
352
|
+
"type": "group",
|
|
353
|
+
"name": "color",
|
|
354
|
+
"controls": [
|
|
355
|
+
{
|
|
356
|
+
"type": "boolean",
|
|
357
|
+
"name": "monochrome"
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
"type": "easing",
|
|
361
|
+
"name": "distribution"
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
</div>
|
|
368
|
+
|
|
335
369
|
## Theming
|
|
336
370
|
|
|
337
371
|
Ctrls uses CSS variables for theming. There are many you can adjust, but I recommend starting with these four:
|
|
@@ -389,7 +423,7 @@ Thank you for stopping by! If you end up using Ctrls, please let me know, I woul
|
|
|
389
423
|
* [x] On input event / handler
|
|
390
424
|
* [x] Revisit naming `controls` vs `options` vs `values`
|
|
391
425
|
* [x] Remove lucide as a dependency, swap with local SVGs
|
|
392
|
-
* [x] Add
|
|
426
|
+
* [x] Add Alea PRNG instead of seedrandom
|
|
393
427
|
* [x] Storage options - none
|
|
394
428
|
* [x] Fix get random value float point error in dual range (modulo with floats)
|
|
395
429
|
* [x] Easing - fix handles jumping to previous positions after selecting preset (no storage)
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { Ctrl, CtrlType, CtrlChangeHandler, CtrlConfig } from ".";
|
|
2
2
|
export declare class BooleanCtrl implements Ctrl<boolean> {
|
|
3
3
|
type: CtrlType;
|
|
4
|
+
id: string;
|
|
5
|
+
group?: string;
|
|
4
6
|
name: string;
|
|
5
7
|
label: string;
|
|
6
8
|
value: boolean;
|
|
7
9
|
isRandomizationDisabled: boolean;
|
|
8
|
-
onChange: CtrlChangeHandler
|
|
9
|
-
onInput: CtrlChangeHandler
|
|
10
|
+
onChange: CtrlChangeHandler;
|
|
11
|
+
onInput: CtrlChangeHandler;
|
|
10
12
|
element: HTMLElement;
|
|
11
13
|
input: HTMLInputElement;
|
|
12
|
-
constructor(config: CtrlConfig<boolean>, onChange: CtrlChangeHandler
|
|
14
|
+
constructor(config: CtrlConfig<boolean>, onChange: CtrlChangeHandler, onInput: CtrlChangeHandler);
|
|
13
15
|
parse: (string: string) => string is "true";
|
|
14
16
|
getRandomValue: () => boolean;
|
|
15
17
|
getDefaultValue: () => boolean;
|
|
@@ -25,11 +25,11 @@ export class BooleanCtrl {
|
|
|
25
25
|
input.checked = this.value;
|
|
26
26
|
input.addEventListener("change", () => {
|
|
27
27
|
this.value = input.checked;
|
|
28
|
-
this.onChange(this
|
|
28
|
+
this.onChange(this);
|
|
29
29
|
});
|
|
30
30
|
input.addEventListener("input", () => {
|
|
31
31
|
this.value = input.checked;
|
|
32
|
-
this.onInput(this
|
|
32
|
+
this.onInput(this);
|
|
33
33
|
});
|
|
34
34
|
const checkmark = document.createElement("span");
|
|
35
35
|
checkmark.classList.add("ctrls__boolean-checkmark");
|
|
@@ -56,6 +56,8 @@ export class BooleanCtrl {
|
|
|
56
56
|
};
|
|
57
57
|
this.type = "boolean";
|
|
58
58
|
this.name = config.name;
|
|
59
|
+
this.id = config.id || config.name;
|
|
60
|
+
this.group = config.group || "";
|
|
59
61
|
this.label = config.label || config.name;
|
|
60
62
|
this.value =
|
|
61
63
|
config.defaultValue === undefined
|
|
@@ -11,12 +11,14 @@ export type DualRangeValue = {
|
|
|
11
11
|
};
|
|
12
12
|
export declare class DualRangeCtrl implements Ctrl<DualRangeValue> {
|
|
13
13
|
type: CtrlType;
|
|
14
|
+
id: string;
|
|
15
|
+
group?: string;
|
|
14
16
|
name: string;
|
|
15
17
|
label: string;
|
|
16
18
|
value: DualRangeValue;
|
|
17
19
|
isRandomizationDisabled: boolean;
|
|
18
|
-
onChange: CtrlChangeHandler
|
|
19
|
-
onInput: CtrlChangeHandler
|
|
20
|
+
onChange: CtrlChangeHandler;
|
|
21
|
+
onInput: CtrlChangeHandler;
|
|
20
22
|
min: number;
|
|
21
23
|
max: number;
|
|
22
24
|
step: number;
|
|
@@ -25,7 +27,7 @@ export declare class DualRangeCtrl implements Ctrl<DualRangeValue> {
|
|
|
25
27
|
maxInput: HTMLInputElement;
|
|
26
28
|
dualRange: DualRangeInput;
|
|
27
29
|
valueSpan: HTMLSpanElement;
|
|
28
|
-
constructor(config: ConfigFor<"dual-range">, onChange: CtrlChangeHandler
|
|
30
|
+
constructor(config: ConfigFor<"dual-range">, onChange: CtrlChangeHandler, onInput: CtrlChangeHandler);
|
|
29
31
|
parse: (string: string) => {
|
|
30
32
|
min: number;
|
|
31
33
|
max: number;
|
|
@@ -36,7 +36,7 @@ export class DualRangeCtrl {
|
|
|
36
36
|
max: parseFloat(maxInput.value),
|
|
37
37
|
};
|
|
38
38
|
this.update(this.value);
|
|
39
|
-
this.onChange(this
|
|
39
|
+
this.onChange(this);
|
|
40
40
|
};
|
|
41
41
|
const inputHandler = () => {
|
|
42
42
|
this.value = {
|
|
@@ -44,7 +44,7 @@ export class DualRangeCtrl {
|
|
|
44
44
|
max: parseFloat(maxInput.value),
|
|
45
45
|
};
|
|
46
46
|
this.update(this.value);
|
|
47
|
-
this.onInput(this
|
|
47
|
+
this.onInput(this);
|
|
48
48
|
};
|
|
49
49
|
const minInput = document.createElement("input");
|
|
50
50
|
minInput.setAttribute("type", "range");
|
|
@@ -101,6 +101,8 @@ export class DualRangeCtrl {
|
|
|
101
101
|
this.dualRange.update();
|
|
102
102
|
};
|
|
103
103
|
this.name = config.name;
|
|
104
|
+
this.id = config.id || config.name;
|
|
105
|
+
this.group = config.group || "";
|
|
104
106
|
this.label = config.label || config.name;
|
|
105
107
|
this.min = config.min;
|
|
106
108
|
this.max = config.max;
|
|
@@ -2,12 +2,14 @@ import type { Ctrl, CtrlChangeHandler, CtrlType, ConfigFor } from ".";
|
|
|
2
2
|
export type Easing = [number, number, number, number];
|
|
3
3
|
export declare class EasingCtrl implements Ctrl<Easing> {
|
|
4
4
|
type: CtrlType;
|
|
5
|
+
id: string;
|
|
6
|
+
group?: string;
|
|
5
7
|
name: string;
|
|
6
8
|
label: string;
|
|
7
9
|
value: Easing;
|
|
8
10
|
isRandomizationDisabled: boolean;
|
|
9
|
-
onChange: CtrlChangeHandler
|
|
10
|
-
onInput: CtrlChangeHandler
|
|
11
|
+
onChange: CtrlChangeHandler;
|
|
12
|
+
onInput: CtrlChangeHandler;
|
|
11
13
|
element: HTMLElement;
|
|
12
14
|
ticks: SVGLineElement[];
|
|
13
15
|
control: HTMLDivElement;
|
|
@@ -15,7 +17,7 @@ export declare class EasingCtrl implements Ctrl<Easing> {
|
|
|
15
17
|
lines: SVGLineElement[];
|
|
16
18
|
path: SVGPathElement;
|
|
17
19
|
presets: Record<string, Easing>;
|
|
18
|
-
constructor(config: ConfigFor<"easing">, onChange: CtrlChangeHandler
|
|
20
|
+
constructor(config: ConfigFor<"easing">, onChange: CtrlChangeHandler, onInput: CtrlChangeHandler);
|
|
19
21
|
parse: (string: string) => Easing;
|
|
20
22
|
getRandomValue: () => Easing;
|
|
21
23
|
getDefaultValue: () => Easing;
|
|
@@ -117,14 +117,14 @@ export class EasingCtrl {
|
|
|
117
117
|
dragging = false;
|
|
118
118
|
const newValue = getNewValue(e.clientX, e.clientY);
|
|
119
119
|
this.value = newValue;
|
|
120
|
-
this.onChange(this
|
|
120
|
+
this.onChange(this);
|
|
121
121
|
this.update();
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
124
|
document.addEventListener("mousemove", (e) => {
|
|
125
125
|
if (dragging) {
|
|
126
126
|
const newValue = getNewValue(e.clientX, e.clientY);
|
|
127
|
-
this.onInput(this
|
|
127
|
+
this.onInput(this);
|
|
128
128
|
this.updateUI(newValue);
|
|
129
129
|
}
|
|
130
130
|
});
|
|
@@ -145,7 +145,7 @@ export class EasingCtrl {
|
|
|
145
145
|
if (dragging) {
|
|
146
146
|
e.preventDefault();
|
|
147
147
|
const newValue = getNewValue(e.touches[0].clientX, e.touches[0].clientY);
|
|
148
|
-
this.onInput(this
|
|
148
|
+
this.onInput(this);
|
|
149
149
|
this.updateUI(newValue);
|
|
150
150
|
}
|
|
151
151
|
});
|
|
@@ -155,7 +155,7 @@ export class EasingCtrl {
|
|
|
155
155
|
dragging = false;
|
|
156
156
|
const newValue = getNewValue(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
|
|
157
157
|
this.value = newValue;
|
|
158
|
-
this.onChange(this
|
|
158
|
+
this.onChange(this);
|
|
159
159
|
this.update();
|
|
160
160
|
}
|
|
161
161
|
});
|
|
@@ -175,7 +175,7 @@ export class EasingCtrl {
|
|
|
175
175
|
// Cap x values
|
|
176
176
|
newValue[index] = Math.max(Math.min(newValue[index], 1), 0);
|
|
177
177
|
this.value = newValue;
|
|
178
|
-
this.onChange(this
|
|
178
|
+
this.onChange(this);
|
|
179
179
|
this.update();
|
|
180
180
|
}
|
|
181
181
|
});
|
|
@@ -192,7 +192,7 @@ export class EasingCtrl {
|
|
|
192
192
|
button.textContent = key.toLowerCase().replace("ease_", "");
|
|
193
193
|
button.addEventListener("click", () => {
|
|
194
194
|
this.value = this.presets[key];
|
|
195
|
-
this.onChange(this
|
|
195
|
+
this.onChange(this);
|
|
196
196
|
this.update();
|
|
197
197
|
});
|
|
198
198
|
presetButtons.appendChild(button);
|
|
@@ -270,6 +270,8 @@ export class EasingCtrl {
|
|
|
270
270
|
this.element.setAttribute("data-value", value.join(","));
|
|
271
271
|
};
|
|
272
272
|
this.name = config.name;
|
|
273
|
+
this.id = config.id || config.name;
|
|
274
|
+
this.group = config.group || "";
|
|
273
275
|
this.label = config.label || config.name;
|
|
274
276
|
this.value =
|
|
275
277
|
config.defaultValue === undefined
|
|
@@ -8,17 +8,19 @@ export type RadioControlOptions = {
|
|
|
8
8
|
};
|
|
9
9
|
export declare class RadioCtrl implements Ctrl<string> {
|
|
10
10
|
type: CtrlType;
|
|
11
|
+
htmlId: string;
|
|
12
|
+
id: string;
|
|
13
|
+
group?: string;
|
|
11
14
|
name: string;
|
|
12
15
|
label: string;
|
|
13
16
|
value: string;
|
|
14
17
|
isRandomizationDisabled: boolean;
|
|
15
|
-
onChange: CtrlChangeHandler
|
|
16
|
-
onInput: CtrlChangeHandler
|
|
18
|
+
onChange: CtrlChangeHandler;
|
|
19
|
+
onInput: CtrlChangeHandler;
|
|
17
20
|
items: Option[];
|
|
18
21
|
element: HTMLElement;
|
|
19
22
|
columns: 1 | 2 | 3 | 4 | 5;
|
|
20
|
-
|
|
21
|
-
constructor(config: ConfigFor<"radio">, onChange: CtrlChangeHandler<string>, onInput: CtrlChangeHandler<string>);
|
|
23
|
+
constructor(config: ConfigFor<"radio">, onChange: CtrlChangeHandler, onInput: CtrlChangeHandler);
|
|
22
24
|
parse: (string: string) => string;
|
|
23
25
|
getRandomValue: () => string;
|
|
24
26
|
getDefaultValue: () => string;
|
package/dist/ctrls/ctrl-radio.js
CHANGED
|
@@ -23,17 +23,17 @@ export class RadioCtrl {
|
|
|
23
23
|
const inputs = items.map((item) => {
|
|
24
24
|
const input = document.createElement("input");
|
|
25
25
|
input.setAttribute("type", "radio");
|
|
26
|
-
input.setAttribute("name", this.
|
|
27
|
-
input.setAttribute("id", `${this.
|
|
26
|
+
input.setAttribute("name", this.htmlId);
|
|
27
|
+
input.setAttribute("id", `${this.htmlId}-${toKebabCase(item.value)}`);
|
|
28
28
|
input.setAttribute("value", item.value);
|
|
29
29
|
input.checked = item.value === value;
|
|
30
30
|
input.addEventListener("change", () => {
|
|
31
31
|
this.value = this.parse(input.value);
|
|
32
|
-
this.onChange(this
|
|
32
|
+
this.onChange(this);
|
|
33
33
|
});
|
|
34
34
|
input.addEventListener("input", () => {
|
|
35
35
|
this.value = this.parse(input.value);
|
|
36
|
-
this.onInput(this
|
|
36
|
+
this.onInput(this);
|
|
37
37
|
});
|
|
38
38
|
const label = document.createElement("span");
|
|
39
39
|
label.textContent = item.label;
|
|
@@ -77,8 +77,10 @@ export class RadioCtrl {
|
|
|
77
77
|
});
|
|
78
78
|
this.columns = config.columns || 3;
|
|
79
79
|
this.name = config.name;
|
|
80
|
+
this.id = config.id || config.name;
|
|
81
|
+
this.group = config.group || "";
|
|
80
82
|
this.label = config.label || config.name;
|
|
81
|
-
this.
|
|
83
|
+
this.htmlId = `ctrls__${toKebabCase(config.name)}-${getRandomString()}`;
|
|
82
84
|
const defaultValue = this.items.find((item) => item.value === config.defaultValue);
|
|
83
85
|
this.value = defaultValue?.value || this.getDefaultValue();
|
|
84
86
|
this.isRandomizationDisabled = config.isRandomizationDisabled || false;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import type { Ctrl, CtrlChangeHandler, CtrlType, ConfigFor } from ".";
|
|
2
2
|
export declare class RangeCtrl implements Ctrl<number> {
|
|
3
3
|
type: CtrlType;
|
|
4
|
+
id: string;
|
|
5
|
+
group?: string;
|
|
4
6
|
name: string;
|
|
5
7
|
label: string;
|
|
6
8
|
value: number;
|
|
7
9
|
isRandomizationDisabled: boolean;
|
|
8
|
-
onChange: CtrlChangeHandler
|
|
9
|
-
onInput: CtrlChangeHandler
|
|
10
|
+
onChange: CtrlChangeHandler;
|
|
11
|
+
onInput: CtrlChangeHandler;
|
|
10
12
|
min: number;
|
|
11
13
|
max: number;
|
|
12
14
|
step: number;
|
|
13
15
|
element: HTMLElement;
|
|
14
16
|
input: HTMLInputElement;
|
|
15
17
|
valueSpan: HTMLSpanElement;
|
|
16
|
-
constructor(config: ConfigFor<"range">, onChange: CtrlChangeHandler
|
|
18
|
+
constructor(config: ConfigFor<"range">, onChange: CtrlChangeHandler, onInput: CtrlChangeHandler);
|
|
17
19
|
parse: (string: string) => number;
|
|
18
20
|
getRandomValue: () => number;
|
|
19
21
|
getDefaultValue: () => number;
|
package/dist/ctrls/ctrl-range.js
CHANGED
|
@@ -33,12 +33,12 @@ export class RangeCtrl {
|
|
|
33
33
|
input.addEventListener("input", () => {
|
|
34
34
|
this.value = this.parse(input.value);
|
|
35
35
|
this.update(this.value);
|
|
36
|
-
this.onInput(this
|
|
36
|
+
this.onInput(this);
|
|
37
37
|
});
|
|
38
38
|
input.addEventListener("change", () => {
|
|
39
39
|
this.value = this.parse(input.value);
|
|
40
40
|
this.update(this.value);
|
|
41
|
-
this.onChange(this
|
|
41
|
+
this.onChange(this);
|
|
42
42
|
});
|
|
43
43
|
input.addEventListener("input", () => {
|
|
44
44
|
const value = this.parse(input.value);
|
|
@@ -73,6 +73,8 @@ export class RangeCtrl {
|
|
|
73
73
|
this.element.style.setProperty("--gradient-position", `${percentage.toFixed(2)}%`);
|
|
74
74
|
};
|
|
75
75
|
this.name = config.name;
|
|
76
|
+
this.id = config.id || config.name;
|
|
77
|
+
this.group = config.group || "";
|
|
76
78
|
this.label = config.label || config.name;
|
|
77
79
|
this.isRandomizationDisabled = config.isRandomizationDisabled || false;
|
|
78
80
|
this.onChange = onChange;
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import type { Ctrl, CtrlChangeHandler, CtrlConfig, CtrlType } from ".";
|
|
2
2
|
export declare class SeedCtrl implements Ctrl<string> {
|
|
3
3
|
type: CtrlType;
|
|
4
|
+
id: string;
|
|
5
|
+
group?: string;
|
|
4
6
|
name: string;
|
|
5
7
|
label: string;
|
|
6
8
|
value: string;
|
|
7
9
|
isRandomizationDisabled: boolean;
|
|
8
|
-
onChange: CtrlChangeHandler
|
|
9
|
-
onInput: CtrlChangeHandler
|
|
10
|
+
onChange: CtrlChangeHandler;
|
|
11
|
+
onInput: CtrlChangeHandler;
|
|
10
12
|
element: HTMLElement;
|
|
11
13
|
input: HTMLInputElement;
|
|
12
|
-
constructor(config: CtrlConfig<string>, onChange: CtrlChangeHandler
|
|
14
|
+
constructor(config: CtrlConfig<string>, onChange: CtrlChangeHandler, onInput: CtrlChangeHandler);
|
|
13
15
|
parse: (string: string) => string;
|
|
14
16
|
getRandomValue: () => string;
|
|
15
17
|
getDefaultValue: () => string;
|
package/dist/ctrls/ctrl-seed.js
CHANGED
|
@@ -30,11 +30,11 @@ export class SeedCtrl {
|
|
|
30
30
|
input.setAttribute("name", id);
|
|
31
31
|
input.addEventListener("change", () => {
|
|
32
32
|
this.value = this.parse(input.value);
|
|
33
|
-
this.onChange(this
|
|
33
|
+
this.onChange(this);
|
|
34
34
|
});
|
|
35
35
|
input.addEventListener("input", () => {
|
|
36
36
|
this.value = this.parse(input.value);
|
|
37
|
-
this.onInput(this
|
|
37
|
+
this.onInput(this);
|
|
38
38
|
});
|
|
39
39
|
const reload = document.createElement("button");
|
|
40
40
|
reload.innerHTML = refreshIcon;
|
|
@@ -42,7 +42,7 @@ export class SeedCtrl {
|
|
|
42
42
|
reload.addEventListener("click", () => {
|
|
43
43
|
this.value = this.getRandomValue();
|
|
44
44
|
this.update();
|
|
45
|
-
this.onChange(this
|
|
45
|
+
this.onChange(this);
|
|
46
46
|
});
|
|
47
47
|
const right = document.createElement("div");
|
|
48
48
|
right.classList.add("ctrls__control-right");
|
|
@@ -66,6 +66,8 @@ export class SeedCtrl {
|
|
|
66
66
|
this.input.value = value;
|
|
67
67
|
};
|
|
68
68
|
this.name = config.name;
|
|
69
|
+
this.id = config.id || config.name;
|
|
70
|
+
this.group = config.group || "";
|
|
69
71
|
this.label = config.label || config.name;
|
|
70
72
|
this.value =
|
|
71
73
|
config.defaultValue === undefined
|
package/dist/ctrls/index.d.ts
CHANGED
|
@@ -8,21 +8,25 @@ import { SeedCtrl } from "./ctrl-seed";
|
|
|
8
8
|
export interface PRNG {
|
|
9
9
|
(): number;
|
|
10
10
|
}
|
|
11
|
-
export type CtrlType = "boolean" | "range" | "radio" | "seed" | "easing" | "dual-range";
|
|
12
|
-
export type CtrlChangeHandler
|
|
11
|
+
export type CtrlType = "boolean" | "range" | "radio" | "seed" | "easing" | "dual-range" | "group";
|
|
12
|
+
export type CtrlChangeHandler = (control: CtrlComponent) => void;
|
|
13
13
|
export type CtrlConfig<T = unknown> = {
|
|
14
14
|
type: CtrlType;
|
|
15
|
+
id?: string;
|
|
15
16
|
name: string;
|
|
17
|
+
group?: string;
|
|
16
18
|
label?: string;
|
|
17
19
|
defaultValue?: T;
|
|
18
20
|
isRandomizationDisabled?: boolean;
|
|
19
21
|
};
|
|
20
22
|
export interface Ctrl<T> {
|
|
23
|
+
id: string;
|
|
24
|
+
group?: string;
|
|
21
25
|
name: string;
|
|
22
26
|
label: string;
|
|
23
27
|
type: CtrlType;
|
|
24
28
|
isRandomizationDisabled: boolean;
|
|
25
|
-
onChange: CtrlChangeHandler
|
|
29
|
+
onChange: CtrlChangeHandler;
|
|
26
30
|
parse: (value: string) => T;
|
|
27
31
|
getRandomValue: () => T;
|
|
28
32
|
getDefaultValue: () => T;
|
|
@@ -59,23 +63,44 @@ export interface CtrlTypeMap {
|
|
|
59
63
|
max: number;
|
|
60
64
|
step?: number;
|
|
61
65
|
};
|
|
66
|
+
group: {
|
|
67
|
+
value: Record<string, unknown>;
|
|
68
|
+
controls: readonly ConfigItem[];
|
|
69
|
+
isRandomizationDisabled?: boolean;
|
|
70
|
+
};
|
|
62
71
|
}
|
|
63
72
|
export type TypedControlConfig = {
|
|
64
73
|
[K in CtrlType]: {
|
|
65
74
|
type: K;
|
|
75
|
+
id?: string;
|
|
66
76
|
name: string;
|
|
77
|
+
group?: string;
|
|
67
78
|
label?: string;
|
|
68
79
|
defaultValue?: CtrlTypeMap[K]["value"];
|
|
69
80
|
isRandomizationDisabled?: boolean;
|
|
70
81
|
} & Omit<CtrlTypeMap[K], "value">;
|
|
71
82
|
}[CtrlType];
|
|
83
|
+
export type GroupConfig = {
|
|
84
|
+
type: "group";
|
|
85
|
+
name: string;
|
|
86
|
+
label?: string;
|
|
87
|
+
controls: readonly TypedControlConfig[];
|
|
88
|
+
isRandomizationDisabled?: boolean;
|
|
89
|
+
};
|
|
90
|
+
export type ConfigItem = TypedControlConfig | GroupConfig;
|
|
72
91
|
export type ConfigFor<T extends CtrlType> = Extract<TypedControlConfig, {
|
|
73
92
|
type: T;
|
|
74
93
|
}>;
|
|
75
|
-
type ExtractValues<Configs extends readonly
|
|
76
|
-
[C in Configs[number]
|
|
94
|
+
type ExtractValues<Configs extends readonly ConfigItem[]> = {
|
|
95
|
+
[C in Extract<Configs[number], {
|
|
96
|
+
type: Exclude<CtrlType, "group">;
|
|
97
|
+
}> as C["name"]]: CtrlTypeMap[C["type"]]["value"];
|
|
98
|
+
} & {
|
|
99
|
+
[C in Extract<Configs[number], {
|
|
100
|
+
type: "group";
|
|
101
|
+
}> as C["name"]]: OptionsMap<C["controls"]>;
|
|
77
102
|
};
|
|
78
|
-
type DerivedProps<Configs extends readonly
|
|
103
|
+
type DerivedProps<Configs extends readonly ConfigItem[]> = {
|
|
79
104
|
[C in Extract<Configs[number], {
|
|
80
105
|
type: "easing";
|
|
81
106
|
}> as `${C["name"]}Easing`]: ReturnType<typeof BezierEasing>;
|
|
@@ -83,8 +108,12 @@ type DerivedProps<Configs extends readonly TypedControlConfig[]> = {
|
|
|
83
108
|
[C in Extract<Configs[number], {
|
|
84
109
|
type: "seed";
|
|
85
110
|
}> as `${C["name"]}Rng`]: PRNG;
|
|
111
|
+
} & {
|
|
112
|
+
[C in Extract<Configs[number], {
|
|
113
|
+
type: "group";
|
|
114
|
+
}> as C["name"]]: DerivedProps<C["controls"]>;
|
|
86
115
|
};
|
|
87
|
-
type OptionsMap<Configs extends readonly
|
|
116
|
+
type OptionsMap<Configs extends readonly ConfigItem[]> = ExtractValues<Configs> & DerivedProps<Configs>;
|
|
88
117
|
type ControlsOptions = {
|
|
89
118
|
showRandomizeButton?: boolean;
|
|
90
119
|
storage?: "hash" | "none";
|
|
@@ -93,20 +122,22 @@ type ControlsOptions = {
|
|
|
93
122
|
title?: string;
|
|
94
123
|
};
|
|
95
124
|
type CtrlComponent = BooleanCtrl | RangeCtrl | RadioCtrl | SeedCtrl | EasingCtrl | DualRangeCtrl;
|
|
96
|
-
export declare class Ctrls<Configs extends readonly
|
|
125
|
+
export declare class Ctrls<Configs extends readonly ConfigItem[]> {
|
|
97
126
|
options: ControlsOptions;
|
|
98
127
|
controls: CtrlComponent[];
|
|
99
128
|
controlsMap: Record<string, CtrlComponent>;
|
|
100
129
|
element: HTMLDivElement;
|
|
101
130
|
onChange?: (updatedValues: Partial<ReturnType<typeof this.getValues>>) => void;
|
|
102
131
|
onInput?: (updatedValues: Partial<ReturnType<typeof this.getValues>>) => void;
|
|
103
|
-
constructor(
|
|
132
|
+
constructor(configs: Configs, options?: ControlsOptions);
|
|
133
|
+
registerControl: (config: TypedControlConfig, onChangeControlHandler: (control: CtrlComponent) => void, onInputControlHandler: (control: CtrlComponent) => void, group?: string) => void;
|
|
104
134
|
buildUI: () => HTMLDivElement;
|
|
105
135
|
toggleVisibility: () => void;
|
|
106
136
|
addHashListeners: () => void;
|
|
107
137
|
getHash: () => string;
|
|
108
138
|
setHash: () => void;
|
|
109
139
|
updateFromHash: () => void;
|
|
140
|
+
updateValuesObject(values: any, control: CtrlComponent): void;
|
|
110
141
|
getValues(): OptionsMap<Configs>;
|
|
111
142
|
randomize: () => void;
|
|
112
143
|
}
|
package/dist/ctrls/index.js
CHANGED
|
@@ -17,16 +17,65 @@ const controlMap = {
|
|
|
17
17
|
"dual-range": DualRangeCtrl,
|
|
18
18
|
};
|
|
19
19
|
export class Ctrls {
|
|
20
|
-
constructor(
|
|
20
|
+
constructor(configs, options) {
|
|
21
|
+
this.controls = [];
|
|
21
22
|
this.controlsMap = {};
|
|
23
|
+
this.registerControl = (config, onChangeControlHandler, onInputControlHandler, group = "") => {
|
|
24
|
+
// To make typescript happy
|
|
25
|
+
if (config.type === "group") {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// TODO
|
|
29
|
+
// Again, document as it is my personal preference
|
|
30
|
+
if (!config.label) {
|
|
31
|
+
config.label = toSpaceCase(config.name);
|
|
32
|
+
}
|
|
33
|
+
// TODO
|
|
34
|
+
// Document this behaviour
|
|
35
|
+
// This might counter-intuitive for some people,
|
|
36
|
+
// but it is my personal preference to have properties named in camel case
|
|
37
|
+
// when using them in code
|
|
38
|
+
//
|
|
39
|
+
// However, they are going to be converted to kebab case when used in the hash,
|
|
40
|
+
// because it is nicer that URL be all lowercase
|
|
41
|
+
config.name = toCamelCase(config.name);
|
|
42
|
+
if (group) {
|
|
43
|
+
config.group = group;
|
|
44
|
+
config.id = toCamelCase(`${group}-${config.name}`);
|
|
45
|
+
}
|
|
46
|
+
const ControlComponent = controlMap[config.type];
|
|
47
|
+
const control = new ControlComponent(config, onChangeControlHandler, onInputControlHandler);
|
|
48
|
+
this.controlsMap[control.id] = control;
|
|
49
|
+
this.controls.push(control);
|
|
50
|
+
};
|
|
22
51
|
this.buildUI = () => {
|
|
23
52
|
const element = document.createElement("div");
|
|
24
53
|
element.classList.add("ctrls");
|
|
25
54
|
element.classList.add(`ctrls--${this.options.theme}-theme`);
|
|
26
55
|
const controlsContainer = document.createElement("div");
|
|
27
56
|
controlsContainer.classList.add("ctrls__controls");
|
|
57
|
+
let group = "";
|
|
58
|
+
let groupElement;
|
|
28
59
|
this.controls.forEach((control) => {
|
|
29
|
-
|
|
60
|
+
if (control.group) {
|
|
61
|
+
if (control.group !== group) {
|
|
62
|
+
group = control.group;
|
|
63
|
+
groupElement = document.createElement("div");
|
|
64
|
+
groupElement.classList.add("ctrls__group");
|
|
65
|
+
const groupTitle = document.createElement("button");
|
|
66
|
+
groupTitle.classList.add("ctrls__group-title");
|
|
67
|
+
groupTitle.innerText = control.group;
|
|
68
|
+
groupTitle.addEventListener("click", () => {
|
|
69
|
+
groupTitle.parentElement?.classList.toggle("ctrls__group--hidden");
|
|
70
|
+
});
|
|
71
|
+
groupElement.append(groupTitle);
|
|
72
|
+
controlsContainer.appendChild(groupElement);
|
|
73
|
+
}
|
|
74
|
+
groupElement.append(control.element);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
controlsContainer.appendChild(control.element);
|
|
78
|
+
}
|
|
30
79
|
});
|
|
31
80
|
if (this.options.showRandomizeButton) {
|
|
32
81
|
const randomizeButton = document.createElement("button");
|
|
@@ -59,7 +108,7 @@ export class Ctrls {
|
|
|
59
108
|
this.getHash = () => {
|
|
60
109
|
const values = this.controls
|
|
61
110
|
.map((control) => {
|
|
62
|
-
return `${toKebabCase(control.
|
|
111
|
+
return `${toKebabCase(control.id)}:${control.valueToString()}`;
|
|
63
112
|
})
|
|
64
113
|
.join("/");
|
|
65
114
|
return `#/${values}`;
|
|
@@ -73,22 +122,22 @@ export class Ctrls {
|
|
|
73
122
|
const items = [];
|
|
74
123
|
pairs.forEach((pair) => {
|
|
75
124
|
const [kebabCaseName, value] = pair.split(":");
|
|
76
|
-
const
|
|
77
|
-
const control = this.controlsMap[
|
|
125
|
+
const id = toCamelCase(kebabCaseName);
|
|
126
|
+
const control = this.controlsMap[id];
|
|
78
127
|
if (control) {
|
|
79
128
|
const parsed = control.parse(value);
|
|
80
129
|
items.push({
|
|
81
|
-
|
|
130
|
+
id,
|
|
82
131
|
value: parsed,
|
|
83
132
|
});
|
|
84
133
|
}
|
|
85
134
|
});
|
|
86
135
|
const updatedValues = {};
|
|
87
136
|
items.forEach((item) => {
|
|
88
|
-
const {
|
|
89
|
-
const control = this.controlsMap[
|
|
137
|
+
const { id, value } = item;
|
|
138
|
+
const control = this.controlsMap[id];
|
|
90
139
|
if (control && JSON.stringify(value) !== JSON.stringify(control.value)) {
|
|
91
|
-
updatedValues
|
|
140
|
+
this.updateValuesObject(updatedValues, control);
|
|
92
141
|
control.update(value);
|
|
93
142
|
}
|
|
94
143
|
});
|
|
@@ -105,7 +154,7 @@ export class Ctrls {
|
|
|
105
154
|
}
|
|
106
155
|
control.value = control.getRandomValue();
|
|
107
156
|
control.update(control.value);
|
|
108
|
-
updatedValues
|
|
157
|
+
this.updateValuesObject(updatedValues, control);
|
|
109
158
|
});
|
|
110
159
|
if (Object.keys(updatedValues).length > 0) {
|
|
111
160
|
this.onChange?.(updatedValues);
|
|
@@ -121,34 +170,28 @@ export class Ctrls {
|
|
|
121
170
|
theme: "system",
|
|
122
171
|
...options,
|
|
123
172
|
};
|
|
124
|
-
const onChangeControlHandler = (
|
|
125
|
-
|
|
173
|
+
const onChangeControlHandler = (control) => {
|
|
174
|
+
const updatedValues = {};
|
|
175
|
+
this.updateValuesObject(updatedValues, control);
|
|
176
|
+
this.onChange?.(updatedValues);
|
|
126
177
|
if (this.options.storage === "hash") {
|
|
127
178
|
this.setHash();
|
|
128
179
|
}
|
|
129
180
|
};
|
|
130
|
-
const onInputControlHandler = (
|
|
131
|
-
|
|
181
|
+
const onInputControlHandler = (control) => {
|
|
182
|
+
const updatedValues = {};
|
|
183
|
+
this.updateValuesObject(updatedValues, control);
|
|
184
|
+
this.onInput?.(updatedValues);
|
|
132
185
|
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// because it is nicer that URL be all lowercase
|
|
142
|
-
config.name = toCamelCase(config.name);
|
|
143
|
-
// TODO
|
|
144
|
-
// Again, document as it is my personal preference
|
|
145
|
-
if (!config.label) {
|
|
146
|
-
config.label = toSpaceCase(config.name);
|
|
186
|
+
configs.map((config) => {
|
|
187
|
+
if (config.type === "group") {
|
|
188
|
+
config.controls.forEach((groupConfig) => {
|
|
189
|
+
this.registerControl(groupConfig, onChangeControlHandler, onInputControlHandler, toCamelCase(config.name));
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
this.registerControl(config, onChangeControlHandler, onInputControlHandler);
|
|
147
194
|
}
|
|
148
|
-
const ControlComponent = controlMap[config.type];
|
|
149
|
-
const control = new ControlComponent(config, onChangeControlHandler, onInputControlHandler);
|
|
150
|
-
this.controlsMap[control.name] = control;
|
|
151
|
-
return control;
|
|
152
195
|
});
|
|
153
196
|
this.element = this.buildUI();
|
|
154
197
|
if (this.options.storage === "hash") {
|
|
@@ -158,17 +201,27 @@ export class Ctrls {
|
|
|
158
201
|
this.options.parent.appendChild(this.element);
|
|
159
202
|
}
|
|
160
203
|
}
|
|
204
|
+
updateValuesObject(values, control) {
|
|
205
|
+
let objectToUpdate = values;
|
|
206
|
+
if (control.group) {
|
|
207
|
+
if (!values[control.group]) {
|
|
208
|
+
values[control.group] = {};
|
|
209
|
+
}
|
|
210
|
+
objectToUpdate = values[control.group];
|
|
211
|
+
}
|
|
212
|
+
objectToUpdate[control.name] = control.value;
|
|
213
|
+
if (control.type === "easing") {
|
|
214
|
+
objectToUpdate[control.name + "Easing"] = BezierEasing(...control.value);
|
|
215
|
+
}
|
|
216
|
+
else if (control.type === "seed") {
|
|
217
|
+
objectToUpdate[control.name + "Rng"] = Alea(...control.value.split("-"));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
161
220
|
getValues() {
|
|
162
|
-
const
|
|
221
|
+
const values = {};
|
|
163
222
|
this.controls.forEach((control) => {
|
|
164
|
-
|
|
165
|
-
if (control.type === "easing") {
|
|
166
|
-
options[control.name + "Easing"] = BezierEasing(...control.value);
|
|
167
|
-
}
|
|
168
|
-
else if (control.type === "seed") {
|
|
169
|
-
options[control.name + "Rng"] = Alea(control.value);
|
|
170
|
-
}
|
|
223
|
+
this.updateValuesObject(values, control);
|
|
171
224
|
});
|
|
172
|
-
return
|
|
225
|
+
return values;
|
|
173
226
|
}
|
|
174
227
|
}
|
package/dist/ctrls.css
CHANGED
|
@@ -170,8 +170,8 @@
|
|
|
170
170
|
ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, Consolas,
|
|
171
171
|
"DejaVu Sans Mono", monospace;
|
|
172
172
|
--ctrls-radius: 4px;
|
|
173
|
-
--ctrls-label-width:
|
|
174
|
-
--ctrls-width:
|
|
173
|
+
--ctrls-label-width: 7rem;
|
|
174
|
+
--ctrls-width: 24rem;
|
|
175
175
|
--ctrls-font-size: 0.75rem;
|
|
176
176
|
--ctrls-value-font-size: 0.6875rem;
|
|
177
177
|
--ctrls-c: 0.25;
|
|
@@ -301,6 +301,7 @@
|
|
|
301
301
|
overflow: visible;
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
.ctrls__group-title,
|
|
304
305
|
.ctrls__title {
|
|
305
306
|
background: none;
|
|
306
307
|
border: none;
|
|
@@ -312,15 +313,17 @@
|
|
|
312
313
|
cursor: pointer;
|
|
313
314
|
transition: color 300ms, background-color 300ms;
|
|
314
315
|
}
|
|
315
|
-
.
|
|
316
|
+
.ctrls__group-title:focus-visible, .ctrls__group-title:hover,
|
|
317
|
+
.ctrls__title:focus-visible,
|
|
318
|
+
.ctrls__title:hover {
|
|
316
319
|
color: var(--ctrls-theme);
|
|
317
320
|
background: var(--ctrls-btn-hover-bg);
|
|
318
321
|
}
|
|
319
322
|
|
|
320
323
|
.ctrls__controls {
|
|
321
324
|
display: grid;
|
|
325
|
+
padding-block: 0.5rem;
|
|
322
326
|
gap: 0.5rem;
|
|
323
|
-
padding: 0.5rem;
|
|
324
327
|
overflow: auto;
|
|
325
328
|
scrollbar-width: thin;
|
|
326
329
|
scrollbar-color: var(--ctrls-scrollbar-thumb-bg) transparent;
|
|
@@ -330,6 +333,10 @@
|
|
|
330
333
|
display: none;
|
|
331
334
|
}
|
|
332
335
|
|
|
336
|
+
.ctrls--hidden .ctrls__title {
|
|
337
|
+
border-block-end: none;
|
|
338
|
+
}
|
|
339
|
+
|
|
333
340
|
/* ----- Buttons ----- */
|
|
334
341
|
.ctrls__btn {
|
|
335
342
|
background: none;
|
|
@@ -355,7 +362,8 @@
|
|
|
355
362
|
}
|
|
356
363
|
|
|
357
364
|
.ctrls__btn--lg {
|
|
358
|
-
margin-
|
|
365
|
+
margin-inline-start: calc(var(--ctrls-label-width) + 0.5rem);
|
|
366
|
+
margin-inline-end: 0.5rem;
|
|
359
367
|
padding: 0.5rem 1rem;
|
|
360
368
|
background: var(--ctrls-btn-bg);
|
|
361
369
|
border: 1px solid var(--ctrls-input-border);
|
|
@@ -375,8 +383,40 @@
|
|
|
375
383
|
opacity: 0;
|
|
376
384
|
}
|
|
377
385
|
|
|
378
|
-
/* -----
|
|
386
|
+
/* ----- Groups ----- */
|
|
387
|
+
.ctrls__group {
|
|
388
|
+
margin: 0 0.5rem;
|
|
389
|
+
display: grid;
|
|
390
|
+
gap: 0.5rem;
|
|
391
|
+
padding-block-end: 0.5rem;
|
|
392
|
+
border: 1px solid var(--ctrls-border);
|
|
393
|
+
border-radius: var(--ctrls-radius);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.ctrls__group .ctrls__control {
|
|
397
|
+
grid-template-columns: calc(var(--ctrls-label-width) - 0.5rem) minmax(0, 1fr);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.ctrls__group:has(+ .ctrls__group) {
|
|
401
|
+
border-block-end: none;
|
|
402
|
+
margin-block-end: 0;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.ctrls__group-title {
|
|
406
|
+
text-align: left;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.ctrls__group--hidden {
|
|
410
|
+
padding-block-end: 0;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.ctrls__group--hidden .ctrls__control {
|
|
414
|
+
display: none;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* ----- Controls ----- */
|
|
379
418
|
.ctrls__control {
|
|
419
|
+
padding-inline: 0.5rem;
|
|
380
420
|
display: grid;
|
|
381
421
|
grid-template-columns: var(--ctrls-label-width) minmax(0, 1fr);
|
|
382
422
|
align-items: center;
|
package/dist/ctrls.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../node_modules/@stanko/dual-range-input/dist/index.css","../src/scss/_ctrls.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;;AC3IF;EACE;AAAA;AAAA;EAGA;EACA;EACA;EACA;EACA;EAGA;EACA;EAEA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EAGA;EAGA;EACA;EACA;EAGA;EACA;EAGA;EAGA;EAEA;EACA;EAEA;EACA;EAGA;EAGA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EAGA;EACA;EAEA;EAEA;EACA;EAEA;EAGA;EAEA;EACA;;;AA+CF;EACE;IA3CA;IAEA;IACA;IAGA;IACA;IACA;IACA;IACA;IACA;IAGA;IAGA;IACA;IACA;IAGA;IACA;IAGA;IAGA;IAEA;IAGA;IAGA;AAAA;AAAA;;;AAWF;EAhDE;EAEA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EAGA;EAGA;EACA;EACA;EAGA;EACA;EAGA;EAGA;EAEA;EAGA;EAGA;AAAA;AAAA;;;AAiBF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;EAGE;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../node_modules/@stanko/dual-range-input/dist/index.css","../src/scss/_ctrls.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;;AC3IF;EACE;AAAA;AAAA;EAGA;EACA;EACA;EACA;EACA;EAGA;EACA;EAEA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EAGA;EAGA;EACA;EACA;EAGA;EACA;EAGA;EAGA;EAEA;EACA;EAEA;EACA;EAGA;EAGA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EAGA;EACA;EAEA;EAEA;EACA;EAEA;EAGA;EAEA;EACA;;;AA+CF;EACE;IA3CA;IAEA;IACA;IAGA;IACA;IACA;IACA;IACA;IACA;IAGA;IAGA;IACA;IACA;IAGA;IACA;IAGA;IAGA;IAEA;IAGA;IAGA;AAAA;AAAA;;;AAWF;EAhDE;EAEA;EACA;EAGA;EACA;EACA;EACA;EACA;EACA;EAGA;EAGA;EACA;EACA;EAGA;EACA;EAGA;EAGA;EAEA;EAGA;EAGA;AAAA;AAAA;;;AAiBF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;EAGE;EACA;EACA;;AAGF;AAAA;EAEE;EACA;EACA;;AAGF;EACE;EACA;;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YACE;;AAGF;AAAA;AAAA;EAEE;EACA;;;AAOJ;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAGA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YACE;;AAGF;EAEE;;AAGF;EACE;EACA;EACA,YACE;;AAIJ;EACE;;;AAKJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EAEE;EACA;;;AAOA;EACE;;AAGF;AAAA;AAAA;EAEE;;;AAKN;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAEA;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAKA;AAAA;EACE;EACA;EACA;;;AAIJ;AAEA;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;;AAKJ;EACE;EACA;EACA;;;AAIF;EACE;;;AAIF;EACE;;;AAIF;EACE;EACA;;;AAGF;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAmCJ;EA5BE;EACA;EACA;EACA;EACA;EAEA;;;AA0BF;EAhCE;EACA;EACA;EACA;EACA;EAEA;;;AA8BF;EAtBE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAkBA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAKF;EAvDE;EACA;EACA;EACA;EACA;EAEA;;;AAqDF;EA7CE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAwCA;EACA;;;AAGF;EACE;EACA;;;AAGF;AAEA;EAGE;;AAEA;EACE;;AANJ;EASE;EACA;EAEA;EACA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;;AAEA;EACE;;;AAIJ;AAEA;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YACE;EAEF;EACA;EACA;;AAEA;EAEE;EACA;;;AAIJ;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;AAEA;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;;;AAIJ;EACE;EACA;;AAIE;EACE;;;AAKN;AAEA;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;AAEA;AAAA;EAEE;EACA;EACA;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YACE;;AAGF;EAEE;EACA;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAGF;EAEE;;AAEA;EACE;EACA","file":"ctrls.css"}
|
package/dist/utils/alea.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const Alea: (
|
|
1
|
+
declare const Alea: (...seeds: string[]) => (() => number);
|
|
2
2
|
export default Alea;
|
package/dist/utils/alea.js
CHANGED
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
// Algorithm by Johannes Baagøe
|
|
2
2
|
// Slim TypeScript version of this implementation:
|
|
3
3
|
// https://github.com/coverslide/node-alea
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
const Alea = (...seeds) => {
|
|
5
|
+
const getMash = () => {
|
|
6
|
+
let n = 0xefc8249d;
|
|
7
|
+
const mash = (seed) => {
|
|
8
|
+
for (let i = 0; i < seed.length; i++) {
|
|
9
|
+
n += seed.charCodeAt(i);
|
|
10
|
+
let h = 0.02519603282416938 * n;
|
|
11
|
+
n = h >>> 0;
|
|
12
|
+
h -= n;
|
|
13
|
+
h *= n;
|
|
14
|
+
n = h >>> 0;
|
|
15
|
+
h -= n;
|
|
16
|
+
n += h * 0x100000000; // 2^32
|
|
17
|
+
}
|
|
18
|
+
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
|
|
19
|
+
};
|
|
20
|
+
return mash;
|
|
18
21
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const getRandomSeeds = () => {
|
|
23
|
+
return [
|
|
24
|
+
Math.random().toString(36).slice(2),
|
|
25
|
+
Math.random().toString(36).slice(2),
|
|
26
|
+
Math.random().toString(36).slice(2),
|
|
27
|
+
];
|
|
28
|
+
};
|
|
29
|
+
const mash = getMash();
|
|
30
|
+
const s = [mash(" "), mash(" "), mash(" ")];
|
|
23
31
|
let c = 1;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
(seeds || getRandomSeeds()).forEach((seed) => {
|
|
33
|
+
s.forEach((_, i) => {
|
|
34
|
+
s[i] -= mash(seed);
|
|
35
|
+
if (s[i] < 0) {
|
|
36
|
+
s[i] += 1;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
30
39
|
});
|
|
31
40
|
const random = () => {
|
|
32
41
|
const t = 2091639 * s[0] + c * 2.3283064365386963e-10; // 2^-32
|
|
33
|
-
c = t | 0;
|
|
42
|
+
c = t | 0; // quicker floor
|
|
34
43
|
s[0] = s[1];
|
|
35
44
|
s[1] = s[2];
|
|
36
45
|
s[2] = t - c;
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stanko/ctrls",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
+
"test": "vitest",
|
|
6
7
|
"start": "npm run parse-markdown && vite",
|
|
7
8
|
"build": "npm run parse-markdown && tsc && rm -rf ./docs && vite build && touch ./docs/.nojekyll",
|
|
8
9
|
"build-css": "sass ./src/scss/index.scss ./dist/ctrls.css",
|
|
@@ -19,12 +20,13 @@
|
|
|
19
20
|
],
|
|
20
21
|
"devDependencies": {
|
|
21
22
|
"highlight.js": "^11.11.1",
|
|
22
|
-
"marked": "^
|
|
23
|
-
"marked-highlight": "^2.2.
|
|
24
|
-
"sass": "^1.
|
|
23
|
+
"marked": "^17.0.0",
|
|
24
|
+
"marked-highlight": "^2.2.3",
|
|
25
|
+
"sass": "^1.94.0",
|
|
25
26
|
"simplex-noise": "^4.0.3",
|
|
26
|
-
"typescript": "~5.9.
|
|
27
|
-
"vite": "^7.
|
|
27
|
+
"typescript": "~5.9.3",
|
|
28
|
+
"vite": "^7.2.2",
|
|
29
|
+
"vitest": "^4.0.10"
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"@stanko/dual-range-input": "^1.0.1",
|