signal-layers 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ export function ProgressBar(contract = {}) {
2
+ /* ────────────────────────────────────────────────────────────────────────────
3
+ * CONTRACT
4
+ * ────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * ProgressBar - Visual progress indicator with customizable styling
7
+ *
8
+ * Foundation: HTML progress element with enhanced styling
9
+ *
10
+ * Signals:
11
+ * Size: xs, sm, md, lg, xl, responsive
12
+ * Color: primary, success, danger, neutral, transparent
13
+ * Layout: inline, block, centered
14
+ * Shape: square
15
+ *
16
+ * Data:
17
+ * value - Progress value (0-100)
18
+ * max - Maximum progress value
19
+ *
20
+ * Defaults: md, block, primary
21
+ *
22
+ * Usage:
23
+ * <ProgressBar value={50} />
24
+ * <ProgressBar value={75} success lg />
25
+ * <ProgressBar value={30} danger transparent />
26
+ *
27
+ *
28
+ * ──────────────────────────────────────────────────────────────────────────── */
29
+
30
+ const [inputSignal, layerSignal, dataSignal] = [{ ...contract }, {}, {}];
31
+
32
+ /* ────────────────────────────────────────────────────────────────────────────
33
+ * CONTRACT TOOLS
34
+ * ──────────────────────────────────────────────────────────────────────────── */
35
+
36
+ const layer = (name) => (className) =>
37
+ (layerSignal[name] ||= [],
38
+ (layerSignal[name][0] = className));
39
+
40
+ const data = (name, key = name) =>
41
+ dataSignal[key] !== undefined &&
42
+ (dataSignal[name] = dataSignal[key]);
43
+
44
+ /* ────────────────────────────────────────────────────────────────────────────
45
+ * LAYERS
46
+ * ──────────────────────────────────────────────────────────────────────────── */
47
+ let progressbar;
48
+ (() =>
49
+ (
50
+ progressbar = {
51
+ base: layer("base", "progressbar"),
52
+ size: layer("size", "progressbar"),
53
+ color: layer("color", "progressbar"),
54
+ shape: layer("shape", "progressbar"),
55
+ layout: layer("layout", "progressbar")
56
+ }
57
+ )
58
+ )(),
59
+ /* ────────────────────────────────────────────────────────────────────────────
60
+ * DEFAULTS
61
+ * ──────────────────────────────────────────────────────────────────────────── */
62
+ (() =>
63
+ (
64
+ progressbar.base("[&::-webkit-progress-value]:transition-all [&::-webkit-progress-bar]:transition-all [&::-moz-progress-bar]:transition-all" ),
65
+ progressbar.shape("[&::-webkit-progress-value]:rounded-full [&::-webkit-progress-bar]:rounded-full [&::-moz-progress-bar]:rounded-full" ),
66
+ progressbar.color("[&::-webkit-progress-value]:bg-violet-400 [&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-violet-400" ),
67
+ progressbar.size("h-2 w-32"),
68
+ progressbar.layout("block")
69
+ )
70
+ )(),
71
+ /* ────────────────────────────────────────────────────────────────────────────
72
+ * SHAPE SIGNALS
73
+ * ──────────────────────────────────────────────────────────────────────────── */
74
+ (() =>
75
+ (
76
+ inputSignal.square && progressbar.shape(`[&::-webkit-progress-bar]:rounded-none [&::-webkit-progress-value]:rounded-none [&::-moz-progress-bar]:rounded-none`)
77
+ )
78
+ )(),
79
+ /* ────────────────────────────────────────────────────────────────────────────
80
+ * COLOR SIGNALS
81
+ * ──────────────────────────────────────────────────────────────────────────── */
82
+ (() =>
83
+ (
84
+ inputSignal.primary && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-blue-500 [&::-webkit-progress-value]:bg-blue-500`),
85
+ inputSignal.success && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-green-500 [&::-webkit-progress-value]:bg-green-500`),
86
+ inputSignal.danger && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-red-500 [&::-webkit-progress-value]:bg-red-500`),
87
+ inputSignal.neutral && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-gray-500 [&::-webkit-progress-value]:bg-gray-500`),
88
+ inputSignal.transparent && progressbar.color(`[&::-webkit-progress-bar]:bg-transparent [&::-moz-progress-bar]:bg-transparent [&::-webkit-progress-value]:bg-transparent`)
89
+ )
90
+ )(),
91
+ /* ────────────────────────────────────────────────────────────────────────────
92
+ * SIZE SIGNALS
93
+ * ──────────────────────────────────────────────────────────────────────────── */
94
+ (() =>
95
+ (
96
+ inputSignal.sm && progressbar.size("h-1 w-16"),
97
+ inputSignal.md && progressbar.size("h-2 w-32"),
98
+ inputSignal.lg && progressbar.size("h-3 w-48"),
99
+ inputSignal.xl && progressbar.size("h-4 w-64"),
100
+ inputSignal.responsive && progressbar.size("h-2 w-full")
101
+ )
102
+ )(),
103
+ /* ────────────────────────────────────────────────────────────────────────────
104
+ * LAYOUT SIGNALS
105
+ * ──────────────────────────────────────────────────────────────────────────── */
106
+ (() =>
107
+ (
108
+ inputSignal.inline && progressbar.layout("inline-block"),
109
+ inputSignal.block && progressbar.layout("block"),
110
+ inputSignal.centered && progressbar.layout("mx-auto")
111
+ )
112
+ )(),
113
+ /* ────────────────────────────────────────────────────────────────────────────
114
+ * DATA
115
+ * ──────────────────────────────────────────────────────────────────────────── */
116
+ (() =>
117
+ (
118
+ inputSignal.value && data("value"),
119
+ inputSignal.max && data("max")
120
+ )
121
+ )();
122
+ const value = dataSignal.value !== undefined ? Math.min(100, Math.max(0, Number(dataSignal.value))) : 0;
123
+ /* ────────────────────────────────────────────────────────────────────────────
124
+ * RENDER
125
+ * ──────────────────────────────────────────────────────────────────────────── */
126
+ return (
127
+ <progress
128
+ value={value}
129
+ max={dataSignal.max ?? 100}
130
+ className={Object.values(layerSignal)
131
+ .map(l => l[0])
132
+ .filter(Boolean)
133
+ .join(" ")}
134
+ />
135
+ );
136
+ }
package/src/Slider.jsx ADDED
@@ -0,0 +1,306 @@
1
+ import { useState } from "react";
2
+
3
+ export function Slider(contract = {}) {
4
+ /* ────────────────────────────────────────────────────────────────────────────
5
+ * CONTRACT
6
+ * ────────────────────────────────────────────────────────────────────────────
7
+ *
8
+ * Slider - Continuous range input with value bubble on interaction
9
+ *
10
+ * Foundation: Native HTML range input with dynamic value bubble
11
+ *
12
+ * Signals:
13
+ * Size: xs, sm, md, lg, xl
14
+ * Color: primary, neutral
15
+ * Shape: square
16
+ * Layout: inline, block, centered
17
+ * State: disabled
18
+ * Bubble Size: bubbleXs, bubbleSm, bubbleMd, bubbleLg, bubbleXl
19
+ * Bubble Shape: bubbleSquare, bubblePill, bubbleFlat, bubbleSharp
20
+ *
21
+ * Data:
22
+ * defaultValue - Initial value (uncontrolled)
23
+ * min - Minimum value
24
+ * max - Maximum value
25
+ * step - Step interval
26
+ * onChange - Change handler
27
+ * ariaLabel - Accessibility label
28
+ *
29
+ * Defaults: md, primary, block
30
+ *
31
+ * Usage:
32
+ * <Slider defaultValue={40} onChange={v => {}} />
33
+ * <Slider lg primary min={0} max={200} />
34
+ * <Slider sm neutral square bubbleLg />
35
+ *
36
+ *
37
+ * ────────────────────────────────────────────────────────────────────────────
38
+ */
39
+
40
+ const [inputSignal, layerSignal, dataSignal, stateSignal] = [
41
+ { ...contract },
42
+ {},
43
+ {},
44
+ {}
45
+ ];
46
+
47
+ /* ────────────────────────────────────────────────────────────────────────────
48
+ * CONTRACT TOOLS
49
+ * ──────────────────────────────────────────────────────────────────────────── */
50
+
51
+ const layer = (name, scope = "card") => (className) =>
52
+ (layerSignal[scope] ||= {},
53
+ layerSignal[scope][name] ||= [],
54
+ (layerSignal[scope][name][0] = className));
55
+
56
+ const data = (name, key = name) =>
57
+ inputSignal[key] !== undefined && (dataSignal[name] = inputSignal[key]);
58
+
59
+ const state = (name, priority = 0, initial = 0) => (
60
+ (stateSignal._hooks ||= {})[name] ||= (() => { const [get, set] = useState(initial); return ({ get, set }); })(),
61
+ priority && (!stateSignal._priority || priority > stateSignal._priority) && (stateSignal[name] = stateSignal._hooks[name], stateSignal._priority = priority)
62
+ );
63
+
64
+ const classes = (layers = {}) =>
65
+ Object.values(layers).map(l => l[0]).filter(Boolean).join(" ");
66
+
67
+ /* ────────────────────────────────────────────────────────────────────────────
68
+ * BASE LAYERS
69
+ * ──────────────────────────────────────────────────────────────────────────── */
70
+
71
+ let container, slider, text, bubble;
72
+
73
+ (() =>
74
+ (
75
+ container = {
76
+ base:layer("base", "container")
77
+ }
78
+ )
79
+ )();
80
+
81
+ (() =>
82
+ (
83
+ slider = {
84
+ base: layer("base", "slider"),
85
+ track: layer("track", "slider"),
86
+ thumb: layer("thumb", "slider"),
87
+ size: layer("size", "slider"),
88
+ color: layer("color", "slider"),
89
+ shape: layer("shape", "slider"),
90
+ layout: layer("layout", "slider"),
91
+ interaction: layer("interaction", "slider")
92
+ }
93
+ )
94
+ )();
95
+
96
+ (() =>
97
+ (
98
+ text = {
99
+ base: layer("text-base", "text"),
100
+ position: layer("position", "text"),
101
+ bubble: layer("bubble", "text"),
102
+ color: layer("color", "text")
103
+ }
104
+ )
105
+ )();
106
+
107
+ (() =>
108
+ (
109
+ bubble = {
110
+ base: layer("base", "bubble"),
111
+ color: layer("color", "bubble"),
112
+ size: layer("size", "bubble"),
113
+ shape: layer("shape", "bubble")
114
+ }
115
+ )
116
+ )();
117
+
118
+ /* ────────────────────────────────────────────────────────────────────────────
119
+ * DEFAULTS
120
+ * ──────────────────────────────────────────────────────────────────────────── */
121
+
122
+ (() =>
123
+ (
124
+ container.base( "inline-flex items-center select-none relative"),
125
+ slider.base("appearance-none outline-none focus:outline-none transition-all duration-200"),
126
+ slider.track("[&::-webkit-slider-runnable-track]:h-2 [&::-webkit-slider-runnable-track]:rounded-full [&::-moz-range-track]:h-2 [&::-moz-range-track]:rounded-full"),
127
+ slider.thumb("[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:-mt-1 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:rounded-full"),
128
+ slider.color("[&::-webkit-slider-runnable-track]:bg-gray-900 [&::-moz-range-track]:bg-gray-900"),
129
+ slider.interaction("cursor-pointer"),
130
+ slider.shape("rounded-full"),
131
+ slider.layout("block"),
132
+ slider.size("w-48"),
133
+ text.base("text-gray-800 font-light font-mono text-xs absolute -top-8"),
134
+ text.position("left-(--thumb-position) -translate-x-1/2 ml-(--thumb-margin)"),
135
+ text.color("text-gray-800"),
136
+ bubble.base("inline-block"),
137
+ bubble.color("bg-white"),
138
+ bubble.size("px-2 py-1"),
139
+ bubble.shape("rounded shadow-md")
140
+ )
141
+ )();
142
+
143
+ /* ────────────────────────────────────────────────────────────────────────────
144
+ * SLIDER SIZE SIGNALS
145
+ * ──────────────────────────────────────────────────────────────────────────── */
146
+
147
+ (() =>
148
+ (
149
+ inputSignal.xs && slider.size("w-24"),
150
+ inputSignal.sm && slider.size("w-32"),
151
+ inputSignal.md && slider.size("w-48"),
152
+ inputSignal.lg && slider.size("w-64"),
153
+ inputSignal.xl && slider.size("w-80")
154
+ )
155
+ )();
156
+
157
+ /* ────────────────────────────────────────────────────────────────────────────
158
+ * BUBBLE SIZE SIGNALS
159
+ * ──────────────────────────────────────────────────────────────────────────── */
160
+
161
+ (() =>
162
+ (
163
+ inputSignal.bubbleXs && bubble.size("px-1 py-0.5 text-2xs"),
164
+ inputSignal.bubbleSm && bubble.size("px-1.5 py-0.5 text-xs"),
165
+ inputSignal.bubbleMd && bubble.size("px-2 py-1 text-xs"),
166
+ inputSignal.bubbleLg && bubble.size("px-3 py-1.5 text-sm"),
167
+ inputSignal.bubbleXl && bubble.size("px-4 py-2 text-base")
168
+ )
169
+ )();
170
+
171
+ /* ────────────────────────────────────────────────────────────────────────────
172
+ * BUBBLE SHAPE SIGNALS
173
+ * ──────────────────────────────────────────────────────────────────────────── */
174
+
175
+ (() =>
176
+ (
177
+ inputSignal.bubbleSquare &&
178
+ bubble.shape("rounded-none shadow-sm"),
179
+ inputSignal.bubblePill &&
180
+ bubble.shape("rounded-full shadow-lg"),
181
+ inputSignal.bubbleFlat &&
182
+ bubble.shape("rounded shadow-none"),
183
+ inputSignal.bubbleSharp &&
184
+ bubble.shape("rounded-xl shadow-2xl")
185
+ )
186
+ )();
187
+
188
+ /* ────────────────────────────────────────────────────────────────────────────
189
+ * COLOR SIGNALS
190
+ * ──────────────────────────────────────────────────────────────────────────── */
191
+
192
+ (() =>
193
+ (
194
+ inputSignal.primary && (
195
+ slider.color("[&::-webkit-slider-runnable-track]:bg-blue-500 [&::-moz-range-track]:bg-blue-500"),
196
+ bubble.color("bg-blue-600"),
197
+ text.color("text-white")
198
+ ),
199
+ inputSignal.neutral && (
200
+ slider.color("[&::-webkit-slider-runnable-track]:bg-gray-400 [&::-moz-range-track]:bg-gray-400"),
201
+ bubble.color("bg-gray-500"),
202
+ text.color("text-white")
203
+ )
204
+ )
205
+ )();
206
+
207
+ /* ────────────────────────────────────────────────────────────────────────────
208
+ * SHAPE SIGNALS
209
+ * ──────────────────────────────────────────────────────────────────────────── */
210
+
211
+ (() =>
212
+ (
213
+ inputSignal.square &&
214
+ slider.shape("[&::-webkit-slider-runnable-track]:rounded-none [&::-webkit-slider-thumb]:rounded-none [&::-moz-range-track]:rounded-none [&::-moz-range-thumb]:rounded-none")
215
+ )
216
+ )();
217
+
218
+ /* ────────────────────────────────────────────────────────────────────────────
219
+ * LAYOUT SIGNALS
220
+ * ──────────────────────────────────────────────────────────────────────────── */
221
+
222
+ (() =>
223
+ (
224
+ inputSignal.inline && slider.layout("inline-block"),
225
+ inputSignal.block && slider.layout("block"),
226
+ inputSignal.centered && slider.layout("mx-auto")
227
+ )
228
+ )();
229
+
230
+ /* ────────────────────────────────────────────────────────────────────────────
231
+ * INTERACTION SIGNALS
232
+ * ──────────────────────────────────────────────────────────────────────────── */
233
+
234
+ (() =>
235
+ (
236
+ inputSignal.disabled &&
237
+ slider.interaction("opacity-50 pointer-events-none")
238
+ )
239
+ )();
240
+
241
+ /* ────────────────────────────────────────────────────────────────────────────
242
+ * DATA & STATE
243
+ * ──────────────────────────────────────────────────────────────────────────── */
244
+
245
+ (() =>
246
+ (
247
+ inputSignal.min && data("min"),
248
+ inputSignal.max && data("max"),
249
+ inputSignal.step && data("step"),
250
+ inputSignal.ariaLabel && data("ariaLabel"),
251
+ inputSignal.onChange && data("onChange"),
252
+ inputSignal.defaultValue && data("defaultValue"),
253
+ state("isSliding", 1, false),
254
+ state("value", 2, dataSignal.defaultValue ?? 0)
255
+ )
256
+ )();
257
+
258
+ const thumbVars = () => {
259
+ const percentage = ((stateSignal.value.get - (dataSignal.min ?? 0)) /
260
+ ((dataSignal.max ?? 100) - (dataSignal.min ?? 0))) * 100;
261
+ return {
262
+ position: `${percentage}%`,
263
+ margin: `${Math.max(0, 5 - (percentage * 5 / 100))}px`
264
+ };
265
+ }
266
+ /* ────────────────────────────────────────────────────────────────────────────
267
+ * RENDER
268
+ * ──────────────────────────────────────────────────────────────────────────── */
269
+
270
+ return (
271
+ <div className={classes(layerSignal.container)}>
272
+ <input
273
+ type="range"
274
+ min={dataSignal.min ?? 0}
275
+ max={dataSignal.max ?? 100}
276
+ step={dataSignal.step ?? 1}
277
+ value={stateSignal.value.get}
278
+ aria-label={dataSignal.ariaLabel}
279
+ disabled={inputSignal.disabled}
280
+ onChange={(e) => {
281
+ const v = Number(e.target.value);
282
+ stateSignal.value?.set && stateSignal.value.set(v);
283
+ dataSignal.onChange?.(v);
284
+ }}
285
+ onMouseUp={() => stateSignal.isSliding?.set && stateSignal.isSliding.set(false)}
286
+ onMouseDown={() => stateSignal.isSliding?.set && stateSignal.isSliding.set(true)}
287
+ onTouchStart={() => stateSignal.isSliding?.set && stateSignal.isSliding.set(true)}
288
+ onTouchEnd={() => stateSignal.isSliding?.set && stateSignal.isSliding.set(false)}
289
+ className={classes(layerSignal.slider)}
290
+ />
291
+ {stateSignal.isSliding?.get && (
292
+ <div
293
+ className={classes(layerSignal.text)}
294
+ style={{
295
+ "--thumb-position": thumbVars().position,
296
+ "--thumb-margin": thumbVars().margin
297
+ }}
298
+ >
299
+ <div className={classes(layerSignal.bubble)}>
300
+ {Math.round(stateSignal.value.get)}</div>
301
+ </div>
302
+ )}
303
+
304
+ </div>
305
+ );
306
+ }
@@ -0,0 +1,155 @@
1
+ export function Spinner(contract = {}) {
2
+ /* ────────────────────────────────────────────────────────────────────────────
3
+ * CONTRACT
4
+ * ────────────────────────────────────────────────────────────────────────────
5
+ *
6
+ * Spinner - Animated loading indicator with customizable appearance
7
+ *
8
+ * Foundation: CSS animated div with spinning border
9
+ *
10
+ * Signals:
11
+ * Size: xs, sm, md, lg, xl
12
+ * Color: light, primary, danger
13
+ * Border: thin, thick
14
+ * Animation: spinSlow, spinFast, pause
15
+ * Layout: inline, block, centered
16
+ * Visibility: transparent
17
+ *
18
+ * Data:
19
+ * ariaLabel - Accessibility label for screen readers
20
+ *
21
+ * Defaults: md, inline, spin
22
+ *
23
+ * Usage:
24
+ * <Spinner />
25
+ * <Spinner lg primary />
26
+ * <Spinner sm light transparent />
27
+ *
28
+ * ──────────────────────────────────────────────────────────────────────────── */
29
+
30
+ const [inputSignal, layerSignal, dataSignal] = [{ ...contract }, {}, {}];
31
+
32
+ /* ────────────────────────────────────────────────────────────────────────────
33
+ * CONTRACT TOOLS
34
+ * ──────────────────────────────────────────────────────────────────────────── */
35
+
36
+ const layer = (name) => (className) =>
37
+ (layerSignal[name] ||= [],
38
+ (layerSignal[name][0] = className));
39
+
40
+ const data = (name, key = name) =>
41
+ inputSignal[key] && (dataSignal[name] = inputSignal[key]);
42
+
43
+ const classes = (layers = {}) =>
44
+ Object.values(layers).map(l => l[0]).filter(Boolean).join(" ");
45
+
46
+ /* ────────────────────────────────────────────────────────────────────────────
47
+ * BASE LAYERS
48
+ * ──────────────────────────────────────────────────────────────────────────── */
49
+ let spinner;
50
+ (() =>
51
+ (
52
+ spinner = {
53
+ layout:layer("layout"),
54
+ size:layer("size"),
55
+ color:layer("color"),
56
+ border:layer("border"),
57
+ animation:layer("animation"),
58
+ visibility:layer("visibility")
59
+ }
60
+ )
61
+ )(),
62
+ /* ────────────────────────────────────────────────────────────────────────────
63
+ * DEFAULTS
64
+ * ──────────────────────────────────────────────────────────────────────────── */
65
+ (() =>
66
+ (
67
+ layout("inline-flex items-center justify-center"),
68
+ color("border-black/20 border-t-black"),
69
+ border("border-2 rounded-full"),
70
+ animation("animate-spin"),
71
+ visibility("opacity-100"),
72
+ size("w-6 h-6")
73
+ )
74
+ )(),
75
+ /* ────────────────────────────────────────────────────────────────────────────
76
+ * SIZE SIGNALS
77
+ * ──────────────────────────────────────────────────────────────────────────── */
78
+ (() =>
79
+ (
80
+ inputSignal.xs && size("w-3 h-3 border"),
81
+ inputSignal.sm && size("w-4 h-4 border-2"),
82
+ inputSignal.md && size("w-6 h-6 border-2"),
83
+ inputSignal.lg && size("w-8 h-8 border-4"),
84
+ inputSignal.xl && size("w-12 h-12 border-4")
85
+ )
86
+ )(),
87
+ /* ────────────────────────────────────────────────────────────────────────────
88
+ * COLOR SIGNALS
89
+ * ──────────────────────────────────────────────────────────────────────────── */
90
+ (() =>
91
+ (
92
+
93
+ inputSignal.light && color("border-white/30 border-t-white"),
94
+ inputSignal.primary && color("border-blue-600/30 border-t-blue-500"),
95
+ inputSignal.danger && color("border-red-600/30 border-t-red-500")
96
+ )
97
+ )(),
98
+ /* ────────────────────────────────────────────────────────────────────────────
99
+ * BORDER SIGNALS
100
+ * ──────────────────────────────────────────────────────────────────────────── */
101
+ (() =>
102
+ (
103
+
104
+ inputSignal.thin && border("border rounded-full"),
105
+ inputSignal.thick && border("border-4 rounded-full")
106
+ )
107
+ )(),
108
+ /* ────────────────────────────────────────────────────────────────────────────
109
+ * ANIMATION SIGNALS
110
+ * ──────────────────────────────────────────────────────────────────────────── */
111
+ (() =>
112
+ (
113
+
114
+ inputSignal.spinSlow && animation("animate-spin [animation-duration:1.5s]"),
115
+ inputSignal.spinFast && animation("animate-spin [animation-duration:.6s]"),
116
+ inputSignal.pause && animation("animate-spin [animation-play-state:paused]")
117
+ )
118
+ )(),
119
+ /* ────────────────────────────────────────────────────────────────────────────
120
+ * LAYOUT SIGNALS
121
+ * ──────────────────────────────────────────────────────────────────────────── */
122
+ (() =>
123
+ (
124
+ inputSignal.inline && layout("inline-flex"),
125
+ inputSignal.block && layout("block mx-auto"),
126
+ inputSignal.centered && layout("flex mx-auto")
127
+ )
128
+ )(),
129
+ /* ────────────────────────────────────────────────────────────────────────────
130
+ * VISIBILITY SIGNALS
131
+ * ──────────────────────────────────────────────────────────────────────────── */
132
+ (() =>
133
+ (
134
+ inputSignal.transparent && visibility("opacity-25")
135
+ )
136
+ )(),
137
+ /* ────────────────────────────────────────────────────────────────────────────
138
+ * DATA
139
+ * ──────────────────────────────────────────────────────────────────────────── */
140
+ (() =>
141
+ (
142
+ inputSignal.ariaLabel && data("ariaLabel")
143
+ )
144
+ )();
145
+ /* ────────────────────────────────────────────────────────────────────────────
146
+ * RENDER
147
+ * ──────────────────────────────────────────────────────────────────────────── */
148
+ return (
149
+ <div
150
+ role="status"
151
+ aria-label={dataSignal.ariaLabel || "loading"}
152
+ className={classes(layerSignal)}
153
+ />
154
+ );
155
+ }