@stanko/ctrls 0.1.9 → 0.3.0
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 +34 -0
- package/dist/ctrls/ctrl-boolean.d.ts +2 -0
- package/dist/ctrls/ctrl-boolean.js +2 -0
- package/dist/ctrls/ctrl-dual-range.d.ts +2 -0
- package/dist/ctrls/ctrl-dual-range.js +2 -0
- package/dist/ctrls/ctrl-easing.d.ts +2 -0
- package/dist/ctrls/ctrl-easing.js +2 -0
- package/dist/ctrls/ctrl-radio.d.ts +3 -1
- package/dist/ctrls/ctrl-radio.js +5 -3
- package/dist/ctrls/ctrl-range.d.ts +2 -0
- package/dist/ctrls/ctrl-range.js +2 -0
- package/dist/ctrls/ctrl-seed.d.ts +2 -0
- package/dist/ctrls/ctrl-seed.js +2 -0
- package/dist/ctrls/index.d.ts +38 -7
- package/dist/ctrls/index.js +76 -26
- package/dist/ctrls.css +53 -7
- package/dist/ctrls.css.map +1 -1
- package/dist/utils/alea.d.ts +1 -1
- package/dist/utils/alea.js +11 -9
- package/package.json +4 -2
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:
|
|
@@ -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,6 +2,8 @@ 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;
|
|
@@ -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,6 +8,9 @@ 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;
|
|
@@ -17,7 +20,6 @@ export declare class RadioCtrl implements Ctrl<string> {
|
|
|
17
20
|
items: Option[];
|
|
18
21
|
element: HTMLElement;
|
|
19
22
|
columns: 1 | 2 | 3 | 4 | 5;
|
|
20
|
-
id: string;
|
|
21
23
|
constructor(config: ConfigFor<"radio">, onChange: CtrlChangeHandler<string>, onInput: CtrlChangeHandler<string>);
|
|
22
24
|
parse: (string: string) => string;
|
|
23
25
|
getRandomValue: () => string;
|
package/dist/ctrls/ctrl-radio.js
CHANGED
|
@@ -23,8 +23,8 @@ 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", () => {
|
|
@@ -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;
|
package/dist/ctrls/ctrl-range.js
CHANGED
|
@@ -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;
|
package/dist/ctrls/ctrl-seed.js
CHANGED
package/dist/ctrls/index.d.ts
CHANGED
|
@@ -8,16 +8,20 @@ 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";
|
|
11
|
+
export type CtrlType = "boolean" | "range" | "radio" | "seed" | "easing" | "dual-range" | "group";
|
|
12
12
|
export type CtrlChangeHandler<T> = (name: string, value: T) => 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;
|
|
@@ -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: (name: string, value: unknown) => void, onInputControlHandler: (name: string, value: unknown) => 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(options: 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.id = toCamelCase(`${group}-${config.name}`);
|
|
44
|
+
}
|
|
45
|
+
const ControlComponent = controlMap[config.type];
|
|
46
|
+
const control = new ControlComponent(config, onChangeControlHandler, onInputControlHandler);
|
|
47
|
+
control.group = group;
|
|
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}`;
|
|
@@ -130,25 +179,15 @@ export class Ctrls {
|
|
|
130
179
|
const onInputControlHandler = (name, value) => {
|
|
131
180
|
this.onInput?.({ [name]: value });
|
|
132
181
|
};
|
|
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);
|
|
182
|
+
configs.map((config) => {
|
|
183
|
+
if (config.type === "group") {
|
|
184
|
+
config.controls.forEach((groupConfig) => {
|
|
185
|
+
this.registerControl(groupConfig, onChangeControlHandler, onInputControlHandler, toCamelCase(config.name));
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
this.registerControl(config, onChangeControlHandler, onInputControlHandler);
|
|
147
190
|
}
|
|
148
|
-
const ControlComponent = controlMap[config.type];
|
|
149
|
-
const control = new ControlComponent(config, onChangeControlHandler, onInputControlHandler);
|
|
150
|
-
this.controlsMap[control.name] = control;
|
|
151
|
-
return control;
|
|
152
191
|
});
|
|
153
192
|
this.element = this.buildUI();
|
|
154
193
|
if (this.options.storage === "hash") {
|
|
@@ -158,15 +197,26 @@ export class Ctrls {
|
|
|
158
197
|
this.options.parent.appendChild(this.element);
|
|
159
198
|
}
|
|
160
199
|
}
|
|
200
|
+
updateValuesObject(options, control) {
|
|
201
|
+
options[control.name] = control.value;
|
|
202
|
+
if (control.type === "easing") {
|
|
203
|
+
options[control.name + "Easing"] = BezierEasing(...control.value);
|
|
204
|
+
}
|
|
205
|
+
else if (control.type === "seed") {
|
|
206
|
+
options[control.name + "Rng"] = Alea(...control.value.split("-"));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
161
209
|
getValues() {
|
|
162
210
|
const options = {};
|
|
163
211
|
this.controls.forEach((control) => {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
212
|
+
if (control.group) {
|
|
213
|
+
if (!options[control.group]) {
|
|
214
|
+
options[control.group] = {};
|
|
215
|
+
}
|
|
216
|
+
this.updateValuesObject(options[control.group], control);
|
|
167
217
|
}
|
|
168
|
-
else
|
|
169
|
-
options
|
|
218
|
+
else {
|
|
219
|
+
this.updateValuesObject(options, control);
|
|
170
220
|
}
|
|
171
221
|
});
|
|
172
222
|
return options;
|
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,24 +313,37 @@
|
|
|
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
|
|
|
323
|
+
.ctrls__title {
|
|
324
|
+
border-block-end: 1px solid var(--ctrls-border);
|
|
325
|
+
}
|
|
326
|
+
|
|
320
327
|
.ctrls__controls {
|
|
321
328
|
display: grid;
|
|
322
|
-
|
|
323
|
-
padding: 0.5rem;
|
|
329
|
+
padding-top: 0.5rem;
|
|
324
330
|
overflow: auto;
|
|
325
331
|
scrollbar-width: thin;
|
|
326
332
|
scrollbar-color: var(--ctrls-scrollbar-thumb-bg) transparent;
|
|
327
333
|
}
|
|
328
334
|
|
|
335
|
+
.ctrls__controls > * {
|
|
336
|
+
margin-bottom: 0.5rem;
|
|
337
|
+
}
|
|
338
|
+
|
|
329
339
|
.ctrls--hidden .ctrls__controls {
|
|
330
340
|
display: none;
|
|
331
341
|
}
|
|
332
342
|
|
|
343
|
+
.ctrls--hidden .ctrls__title {
|
|
344
|
+
border-block-end: none;
|
|
345
|
+
}
|
|
346
|
+
|
|
333
347
|
/* ----- Buttons ----- */
|
|
334
348
|
.ctrls__btn {
|
|
335
349
|
background: none;
|
|
@@ -355,7 +369,8 @@
|
|
|
355
369
|
}
|
|
356
370
|
|
|
357
371
|
.ctrls__btn--lg {
|
|
358
|
-
margin-
|
|
372
|
+
margin-inline-start: calc(var(--ctrls-label-width) + 0.5rem);
|
|
373
|
+
margin-inline-end: 0.5rem;
|
|
359
374
|
padding: 0.5rem 1rem;
|
|
360
375
|
background: var(--ctrls-btn-bg);
|
|
361
376
|
border: 1px solid var(--ctrls-input-border);
|
|
@@ -375,8 +390,39 @@
|
|
|
375
390
|
opacity: 0;
|
|
376
391
|
}
|
|
377
392
|
|
|
378
|
-
/* -----
|
|
393
|
+
/* ----- Groups ----- */
|
|
394
|
+
.ctrls__group {
|
|
395
|
+
display: grid;
|
|
396
|
+
gap: 0.5rem;
|
|
397
|
+
padding-block-end: 0.5rem;
|
|
398
|
+
border-block: 1px solid var(--ctrls-border);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.ctrls__group .ctrls__control {
|
|
402
|
+
padding-inline-start: 1rem;
|
|
403
|
+
grid-template-columns: calc(var(--ctrls-label-width) - 0.5rem) minmax(0, 1fr);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.ctrls__group:has(+ .ctrls__group) {
|
|
407
|
+
border-block-end: none;
|
|
408
|
+
margin-block-end: 0;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.ctrls__group-title {
|
|
412
|
+
text-align: left;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.ctrls__group--hidden {
|
|
416
|
+
padding-block-end: 0;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.ctrls__group--hidden .ctrls__control {
|
|
420
|
+
display: none;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* ----- Controls ----- */
|
|
379
424
|
.ctrls__control {
|
|
425
|
+
padding-inline: 0.5rem;
|
|
380
426
|
display: grid;
|
|
381
427
|
grid-template-columns: var(--ctrls-label-width) minmax(0, 1fr);
|
|
382
428
|
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;;;AAIJ;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;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;;;AAGF;EACE;EACA;;;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
|
@@ -18,19 +18,21 @@ const getMash = () => {
|
|
|
18
18
|
};
|
|
19
19
|
return mash;
|
|
20
20
|
};
|
|
21
|
-
const Alea = (
|
|
22
|
-
|
|
21
|
+
const Alea = (...seeds) => {
|
|
22
|
+
const mash = getMash();
|
|
23
|
+
const s = [mash(" "), mash(" "), mash(" ")];
|
|
23
24
|
let c = 1;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
seeds.forEach((seed) => {
|
|
26
|
+
s.forEach((_, i) => {
|
|
27
|
+
s[i] -= mash(seed);
|
|
28
|
+
if (s[i] < 0) {
|
|
29
|
+
s[i] += 1;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
30
32
|
});
|
|
31
33
|
const random = () => {
|
|
32
34
|
const t = 2091639 * s[0] + c * 2.3283064365386963e-10; // 2^-32
|
|
33
|
-
c = t | 0;
|
|
35
|
+
c = t | 0; // quicker floor
|
|
34
36
|
s[0] = s[1];
|
|
35
37
|
s[1] = s[2];
|
|
36
38
|
s[2] = t - c;
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stanko/ctrls",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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",
|
|
@@ -24,7 +25,8 @@
|
|
|
24
25
|
"sass": "^1.92.1",
|
|
25
26
|
"simplex-noise": "^4.0.3",
|
|
26
27
|
"typescript": "~5.9.2",
|
|
27
|
-
"vite": "^7.1.5"
|
|
28
|
+
"vite": "^7.1.5",
|
|
29
|
+
"vitest": "^4.0.9"
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"@stanko/dual-range-input": "^1.0.1",
|