signal-layers 0.0.5 → 0.1.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/src/Button.jsx CHANGED
@@ -1,296 +1,328 @@
1
- import { useState, useRef } from "react";
1
+ import { useRef } from "react";
2
+ import { createSignalUtils } from "./";
2
3
  export function Button(contract = {}) {
3
- /* ────────────────────────────────────────────────────────────────────────────
4
- * CONTRACT
5
- * ────────────────────────────────────────────────────────────────────────────
6
- * Button - Interactive button element with multiple visual styles
7
- *
8
- * Foundation: Semantic HTML button with layered styling system
9
- *
10
- * Signals:
11
- * Style: cta, neumorphism, ghost
12
- * Color: red, green, blue
13
- * Size: xs, sm, md, lg, xl
14
- * Shape: square, rounded, pill, circle
15
- * Layout: block, inline, center
16
- * Shadow: innerShadow, noShadow
17
- * Border: border, noBorder, roundedBorder, pillBorder, circleBorder
18
- * Hover: hoverEnlarge, hoverShrink, hoverLift, hoverFade, hoverBorder, hoverNone
19
- * Active: activeShrink, activeRipple, activeExplode, activeSlide, activeNone
20
- * Ripple: ripple
21
- *
22
- * Data:
23
- * children - Button content
24
- * disabled - Disable interaction
25
- * onClick - Click handler
26
- * type - Button type attribute
27
- * aria-label - Accessibility label
28
- * aria-haspopup - ARIA haspopup attribute
29
- * aria-expanded - ARIA expanded state
30
- * onMouseEnter - Mouse enter handler
31
- * onMouseLeave - Mouse leave handler
32
- *
33
- * Defaults: md, inline, hoverEnlarge, activeShrink
34
- *
35
- * Usage:
36
- * <Button onClick={() => {}}>Click me</Button>
37
- * <Button cta blue lg>Primary Action</Button>
38
- * <Button ghost hoverLift>Secondary</Button>
39
- *
40
- *
41
- * ────────────────────────────────────────────────────────────────────────────
42
- * CONTRACT TOOLS
43
- * ──────────────────────────────────────────────────────────────────────────── */
4
+ const { layer, data, state, classes, signals } = createSignalUtils(contract);
5
+ const { inputSignal, layerSignal, dataSignal, stateSignal } = signals;
6
+ let btn, ripple;
7
+ btn = {
8
+ size: layer("size", "btn"),
9
+ border: layer("border", "btn"),
10
+ shape: layer("shape", "btn"),
11
+ color: layer("color", "btn"),
12
+ shadow: layer("shadow", "btn"),
13
+ text: layer("text", "btn"),
14
+ textLayer: layer("textLayer", "btn"),
15
+ textWeight: layer("textWeight", "btn"),
16
+ textSize: layer("textSize", "btn"),
17
+ textTransform: layer("textTransform", "btn"),
18
+ textDecoration: layer("textDecoration", "btn"),
19
+ hover: layer("hover", "btn"),
20
+ active: layer("active", "btn"),
21
+ focus: layer("focus", "btn"),
22
+ layout: layer("layout", "btn"),
23
+ animation: layer("animation", "btn"),
24
+ rippleBase: layer("ripple", "btn")
25
+ };
26
+ ripple = {
27
+ base: layer("base", "ripple"),
28
+ animation: layer("animation", "ripple")
29
+ };
30
+ btn.size("px-4 py-2");
31
+ btn.border("border-0");
32
+ btn.shape("rounded-xs");
33
+ btn.color("bg-gray-800 text-white");
34
+ btn.shadow("shadow-xs shadow-black/50");
35
+ btn.text("text-xs font-light font-sans");
36
+ btn.textLayer("text-inherit");
37
+ btn.textWeight("font-medium");
38
+ btn.textSize("text-sm");
39
+ btn.textTransform("normal-case");
40
+ btn.textDecoration("no-underline");
41
+ btn.hover("hover:scale-105 hover:shadow-md");
42
+ btn.active("active:scale-90 active:shadow-md");
43
+ btn.layout("flex items-center justify-center");
44
+ btn.animation("transition-all duration-300 cursor-pointer");
45
+ btn.focus("focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2");
46
+ btn.rippleBase("relative overflow-hidden");
47
+ ripple.base("absolute bg-white/30 rounded-full pointer-events-none");
48
+ ripple.animation("-translate-x-1/2 -translate-y-1/2 transition-all");
44
49
 
45
- const [inputSignal, layerSignal, dataSignal, stateSignal] = [{ ...contract }, {}, {}, {}];
46
-
47
- const layer = (name, scope = "btn") => (className) =>
48
- (layerSignal[scope] ||= {},
49
- layerSignal[scope][name] ||= [],
50
- (layerSignal[scope][name][0] = className));
51
-
52
- const data = (name, key = name) =>
53
- inputSignal[key] && (dataSignal[name] = inputSignal[key]);
54
50
 
55
- const state = (name, priority = 0, initial = false) => (
56
- (stateSignal._hooks ||= {})[name] ||= (() => {
57
- const [get, set] = useState(initial);
58
- return { get, set };
59
- })(),
60
- priority &&
61
- (!stateSignal._priority || priority > stateSignal._priority) &&
62
- (stateSignal[name] = stateSignal._hooks[name],
63
- stateSignal._priority = priority)
64
- );
51
+ inputSignal.primary && (
52
+ btn.color("bg-blue-600 text-white"),
53
+ btn.shadow("shadow-sm shadow-blue-500/40"),
54
+ btn.hover("hover:bg-blue-700 hover:shadow-md"),
55
+ btn.active("active:bg-blue-800")
56
+ );
57
+ inputSignal.secondary && (
58
+ btn.color("bg-gray-200 text-gray-800 border border-gray-300"),
59
+ btn.hover("hover:bg-gray-300 hover:border-gray-400"),
60
+ btn.active("active:bg-gray-400")
61
+ );
62
+ inputSignal.outline && (
63
+ btn.color("bg-transparent border-2 border-blue-600 text-blue-600"),
64
+ btn.hover("hover:bg-blue-600 hover:text-white"),
65
+ btn.active("active:bg-blue-700")
66
+ );
67
+ inputSignal.link && (
68
+ btn.color("text-blue-600 underline-offset-4"),
69
+ btn.hover("hover:underline"),
70
+ btn.active("active:text-blue-800")
71
+ );
72
+ inputSignal.danger && (
73
+ btn.color("bg-red-600 text-white"),
74
+ btn.shadow("shadow-sm shadow-red-500/40"),
75
+ btn.hover("hover:bg-red-700"),
76
+ btn.active("active:bg-red-800")
77
+ );
78
+ inputSignal.warning && (
79
+ btn.color("bg-yellow-500 text-gray-900"),
80
+ btn.shadow("shadow-sm shadow-yellow-500/40"),
81
+ btn.hover("hover:bg-yellow-600"),
82
+ btn.active("active:bg-yellow-700")
83
+ );
84
+ inputSignal.success && (
85
+ btn.color("bg-green-600 text-white"),
86
+ btn.shadow("shadow-sm shadow-green-500/40"),
87
+ btn.hover("hover:bg-green-700"),
88
+ btn.active("active:bg-green-800")
89
+ );
90
+ inputSignal.cta && (
91
+ btn.color("bg-blue-600/95 text-white"),
92
+ btn.shadow("shadow-xs shadow-blue-500/40"),
93
+ btn.hover("hover:scale-105 hover:shadow-md"),
94
+ btn.active("active:scale-90 active:shadow-md")
95
+ );
96
+ inputSignal.neumorphism && (
97
+ btn.shape("rounded-md"),
98
+ btn.shadow("shadow-inner"),
99
+ btn.color("bg-linear-to-r from-gray-300 to-gray-200 text-gray-800"),
100
+ btn.hover("hover:scale-105 hover:bg-white/50 hover:shadow-inner hover:shadow-gray-500/30"),
101
+ btn.active("active:scale-95 active:bg-white/50 active:shadow-inner active:shadow-gray-500/30"),
102
+ btn.animation("transition-all duration-700 cursor-pointer")
103
+ );
104
+ inputSignal.ghost && (
105
+ btn.shape("rounded-full"),
106
+ btn.shadow("shadow-none"),
107
+ btn.color("bg-transparent text-gray-800"),
108
+ btn.hover("hover:scale-110 hover:bg-gray-100/50 hover:text-gray-900"),
109
+ btn.active("active:scale-95 active:bg-gray-100/75 active:text-gray-900"),
110
+ btn.animation("transition-all duration-500 cursor-pointer")
111
+ );
112
+
113
+ inputSignal.red && btn.color("bg-red-600 text-white");
114
+ inputSignal.green && btn.color("bg-green-500 text-white");
115
+ inputSignal.blue && btn.color("bg-blue-500/90 text-neutral-100");
65
116
 
66
- const classes = (layers = {}) =>
67
- Object.values(layers).map(l => l[0]).filter(Boolean).join(" ");
117
+ inputSignal.square && btn.shape("rounded-none");
118
+ inputSignal.rounded && btn.shape("rounded-lg");
119
+ inputSignal.pill && btn.shape("rounded-full");
120
+ inputSignal.circle && btn.shape("rounded-full aspect-square p-0");
121
+
122
+ inputSignal.sizeXs && (
123
+ btn.size("px-2 py-1"),
124
+ btn.textSize("text-xs")
125
+ );
126
+ inputSignal.sizeSm && (
127
+ btn.size("px-3 py-1.5"),
128
+ btn.textSize("text-sm")
129
+ );
130
+ inputSignal.sizeMd && (
131
+ btn.size("px-4 py-2"),
132
+ btn.textSize("text-base")
133
+ );
134
+ inputSignal.sizeLg && (
135
+ btn.size("px-6 py-3"),
136
+ btn.textSize("text-lg")
137
+ );
138
+ inputSignal.sizeXl && (
139
+ btn.size("px-8 py-4"),
140
+ btn.textSize("text-xl")
141
+ );
142
+ inputSignal.size2xl && (
143
+ btn.size("px-10 py-5"),
144
+ btn.textSize("text-2xl")
145
+ );
146
+ inputSignal.wAuto && btn.size("w-auto");
147
+ inputSignal.wFull && btn.size("w-full");
148
+ inputSignal.wFit && btn.size("w-fit");
149
+ inputSignal.wMin && btn.size("w-min");
150
+ inputSignal.wMax && btn.size("w-max");
151
+ inputSignal.hAuto && btn.size("h-auto");
152
+ inputSignal.hFull && btn.size("h-full");
153
+ inputSignal.hFit && btn.size("h-fit");
154
+ inputSignal.hMin && btn.size("h-min");
155
+ inputSignal.hMax && btn.size("h-max");
68
156
 
69
- const rippleCounterRef = useRef(0);
70
- const handleRippleClick = (e) => {
71
- const rect = e.currentTarget.getBoundingClientRect();
72
- stateSignal.ripples?.set([...(stateSignal.ripples?.get || []), {
73
- id: ++rippleCounterRef.current,
74
- x: e.clientX - rect.left,
75
- y: e.clientY - rect.top
76
- }]);
77
- };
157
+ inputSignal.block && btn.layout("w-full");
158
+ inputSignal.inline && btn.layout("inline-flex");
159
+ inputSignal.center && btn.layout("mx-auto");
78
160
 
79
- /* ────────────────────────────────────────────────────────────────────────────
80
- * BASE LAYER
81
- * ──────────────────────────────────────────────────────────────────────────── */
82
- let btn, ripple;
83
- (() =>
84
- (
85
- btn = {
86
- size:layer("size"),
87
- border:layer("border"),
88
- shape:layer("shape"),
89
- color:layer("color"),
90
- shadow:layer("shadow"),
91
- text:layer("text"),
92
- hover:layer("hover"),
93
- active:layer("active"),
94
- layout:layer("layout"),
95
- animation:layer("animation"),
96
- rippleBase: layer("ripple")
97
- },
98
- ripple = {
99
- base: layer("base", "ripple"),
100
- animation: layer("animation", "ripple")
101
- }
102
- )
103
- )(),
104
- /* ────────────────────────────────────────────────────────────────────────────
105
- * DEFAULT
106
- * ──────────────────────────────────────────────────────────────────────────── */
107
- (() =>
108
- (
109
- btn.size("px-4 py-2"),
110
- btn.border("border-0"),
111
- btn.shape("rounded-xs"),
112
- btn.color("bg-gray-800 text-white"),
113
- btn.shadow("shadow-xs shadow-black/50"),
114
- btn.text("text-xs font-light font-sans"),
115
- btn.hover("hover:scale-105 hover:shadow-md"),
116
- btn.active("active:scale-90 active:shadow-md"),
117
- btn.layout("flex items-center justify-center"),
118
- btn.animation("transition-all duration-300 cursor-pointer"),
119
- btn.rippleBase("relative overflow-hidden"),
120
- ripple.base("absolute bg-white/30 rounded-full pointer-events-none"),
121
- ripple.animation("-translate-x-1/2 -translate-y-1/2 transition-all")
122
- )
123
- )();
124
- /* ────────────────────────────────────────────────────────────────────────────
125
- * CALL TO ACTION COMPOSITE SIGNAL
126
- * ──────────────────────────────────────────────────────────────────────────── */
127
- (()=>
128
- (
129
- inputSignal.cta && (
130
- btn.color("bg-blue-600/95 text-white"),
131
- btn.shadow("shadow-xs shadow-blue-500/40"),
132
- btn.hover("hover:scale-105 hover:shadow-md"),
133
- btn.active("active:scale-90 active:shadow-md")
134
- )
135
- )
136
- )(),
137
- /* ────────────────────────────────────────────────────────────────────────────
138
- * NEUMORPHIC COMPOSITE SIGNAL
139
- * ──────────────────────────────────────────────────────────────────────────── */
140
- (()=>
141
- (
142
- inputSignal.neumorphism && (
143
- btn.shape("rounded-md"),
144
- btn.shadow("shadow-inner"),
145
- btn.color("bg-linear-to-r from-gray-300 to-gray-200 text-gray-800"),
146
- btn.hover("hover:scale-105 hover:bg-white/50 hover:shadow-inner hover:shadow-gray-500/30"),
147
- btn.active("active:scale-95 active:bg-white/50 active:shadow-inner active:shadow-gray-500/30"),
148
- btn.animation("transition-all duration-700 cursor-pointer")
149
- )
150
- )
151
- )(),
152
- /* ────────────────────────────────────────────────────────────────────────────
153
- * GHOST COMPOSITE SIGNAL
154
- * ──────────────────────────────────────────────────────────────────────────── */
155
- (()=>
156
- (
157
- inputSignal.ghost && (
158
- btn.shape("rounded-full"),
159
- btn.shadow("shadow-none"),
160
- btn.color("bg-transparent text-gray-800"),
161
- btn.hover("hover:scale-110 hover:bg-gray-100/50 hover:text-gray-900"),
162
- btn.active("active:scale-95 active:bg-gray-100/75 active:text-gray-900"),
163
- btn.animation("transition-all duration-500 cursor-pointer")
164
- )
165
- )
166
- )(),
167
- /* ────────────────────────────────────────────────────────────────────────────
168
- * COLOR SIGNALS
169
- * ──────────────────────────────────────────────────────────────────────────── */
170
- (()=>
171
- (
172
- inputSignal.red && btn.color("bg-red-600 text-white"),
173
- inputSignal.green && btn.color("bg-green-500 text-white"),
174
- inputSignal.blue && btn.color("bg-blue-500/90 text-neutral-100")
175
- )
176
- )(),
177
- /* ────────────────────────────────────────────────────────────────────────────
178
- * SIZE SIGNALS
179
- * ──────────────────────────────────────────────────────────────────────────── */
180
- (()=>
181
- (
182
- inputSignal.xs && btn.size("px-2 py-1 text-xs"),
183
- inputSignal.sm && btn.size("px-3 py-1.5 text-sm"),
184
- inputSignal.md && btn.size("px-4 py-2 text-base"),
185
- inputSignal.lg && btn.size("px-6 py-3 text-lg"),
186
- inputSignal.xl && btn.size("px-8 py-4 text-xl")
187
- )
188
- )(),
189
- /* ────────────────────────────────────────────────────────────────────────────
190
- * SHAPE SIGNALS
191
- * ──────────────────────────────────────────────────────────────────────────── */
192
- (()=>
193
- (
194
- inputSignal.square && btn.shape("rounded-none"),
195
- inputSignal.rounded && btn.shape("rounded-lg"),
196
- inputSignal.pill && btn.shape("rounded-full"),
197
- inputSignal.circle && btn.shape("rounded-full aspect-square p-0")
198
- )
199
- )(),
200
- /* ────────────────────────────────────────────────────────────────────────────
201
- * LAYOUT SIGNALS
202
- * ──────────────────────────────────────────────────────────────────────────── */
203
- (()=>
204
- (
205
- inputSignal.block && btn.layout("w-full"),
206
- inputSignal.inline && btn.layout("inline-flex"),
207
- inputSignal.center && btn.layout("mx-auto")
208
- )
209
- )(),
210
- /* ────────────────────────────────────────────────────────────────────────────
211
- * SHADOW SIGNALS
212
- * ──────────────────────────────────────────────────────────────────────────── */
213
- (()=>(
214
- (
215
- inputSignal.innerShadow && btn.shadow("shadow-inner"),
216
- inputSignal.noShadow && btn.shadow("shadow-none")
217
- )
218
- ))(),
219
- /* ────────────────────────────────────────────────────────────────────────────
220
- * BORDER SIGNALS
221
- * ──────────────────────────────────────────────────────────────────────────── */
222
- (()=>
223
- (
224
- inputSignal.border && btn.border("border border-gray-800"),
225
- inputSignal.noBorder && btn.border("border-none"),
226
- inputSignal.roundedBorder && btn.border("rounded-lg"),
227
- inputSignal.pillBorder && btn.border("rounded-full"),
228
- inputSignal.circleBorder && btn.border("rounded-full aspect-square p-0")
229
- )
230
- )(),
231
- /* ────────────────────────────────────────────────────────────────────────────
232
- * HOVER SIGNALS
233
- * ──────────────────────────────────────────────────────────────────────────── */
234
- (()=>(
235
- (
236
- inputSignal.hoverEnlarge && btn.hover("hover:scale-105"),
237
- inputSignal.hoverShrink && btn.hover("hover:scale-95"),
238
- inputSignal.hoverLift && btn.hover("hover:-translate-y-0.5"),
239
- inputSignal.hoverFade && btn.hover("hover:opacity-40"),
240
- inputSignal.hoverBorder && btn.hover("hover:border hover:border-black"),
241
- inputSignal.hoverNone && btn.hover("hover:scale-100 hover:opacity-100")
242
- )
243
- )(),
244
- /* ────────────────────────────────────────────────────────────────────────────
245
- * ACTIVE SIGNALS
246
- * ──────────────────────────────────────────────────────────────────────────── */
247
- (()=>
248
- (
249
- inputSignal.activeShrink && btn.active("active:scale-95 transition-transform"),
250
- inputSignal.activeRipple && btn.active("active:ring-4 active:ring-black active:scale-90"),
251
- inputSignal.activeExplode && btn.active("active:scale-110 active:ring-8 active:ring-black"),
252
- inputSignal.activeSlide && btn.active("active:translate-x-0.5"),
253
- inputSignal.activeNone && btn.active("active:scale-100 active:opacity-100")
254
- )
255
- )(),
256
- /* ────────────────────────────────────────────────────────────────────────────
257
- * DATA
258
- * ──────────────────────────────────────────────────────────────────────────── */
259
- (()=>
260
- (
261
- inputSignal.children && data("children"),
262
- inputSignal.disabled && data("disabled"),
263
- inputSignal.onClick && data("onClick"),
264
- inputSignal.type && data("type"),
265
- inputSignal["aria-label"] && data("aria-label"),
266
- inputSignal["aria-haspopup"] && data("aria-haspopup"),
267
- inputSignal["aria-expanded"] && data("aria-expanded"),
268
- inputSignal.onMouseEnter && data("onMouseEnter"),
269
- inputSignal.onMouseLeave && data("onMouseLeave"),
270
- state("ripples", 0, []), inputSignal.ripple && data("ripple") && state("ripples", 1, [])
271
- )
272
- ))();
161
+ inputSignal.innerShadow && btn.shadow("shadow-inner");
162
+ inputSignal.noShadow && btn.shadow("shadow-none");
273
163
 
274
- const rippleStyles = ` @keyframes ripple {
275
- 0% { width: 0; height: 0; opacity: 0.5; }
276
- 100% { width: 400px; height: 400px; opacity: 0; }
277
- }
278
- `;
279
- /* ────────────────────────────────────────────────────────────────────────────
280
- * RENDER
281
- * ──────────────────────────────────────────────────────────────────────────── */
164
+ inputSignal.border && btn.border("border border-gray-800");
165
+ inputSignal.noBorder && btn.border("border-none");
166
+ inputSignal.roundedBorder && btn.border("rounded-lg");
167
+ inputSignal.pillBorder && btn.border("rounded-full");
168
+ inputSignal.circleBorder && btn.border("rounded-full aspect-square p-0");
169
+
170
+ inputSignal.radiusNone && btn.shape("rounded-none");
171
+ inputSignal.radiusSm && btn.shape("rounded-sm");
172
+ inputSignal.radius && btn.shape("rounded");
173
+ inputSignal.radiusMd && btn.shape("rounded-md");
174
+ inputSignal.radiusLg && btn.shape("rounded-lg");
175
+ inputSignal.radiusXl && btn.shape("rounded-xl");
176
+ inputSignal.radius2xl && btn.shape("rounded-2xl");
177
+ inputSignal.radius3xl && btn.shape("rounded-3xl");
178
+ inputSignal.radiusFull && btn.shape("rounded-full");
179
+ inputSignal.aspectSquare && btn.shape("aspect-square");
180
+ inputSignal.aspectVideo && btn.shape("aspect-video");
181
+ inputSignal.aspect4_3 && btn.shape("aspect-4/3");
182
+ inputSignal.aspect16_9 && btn.shape("aspect-video");
183
+
184
+ inputSignal.hoverEnlarge && btn.hover("hover:scale-105");
185
+ inputSignal.hoverShrink && btn.hover("hover:scale-95");
186
+ inputSignal.hoverLift && btn.hover("hover:-translate-y-0.5");
187
+ inputSignal.hoverFade && btn.hover("hover:opacity-40");
188
+ inputSignal.hoverBorder && btn.hover("hover:border hover:border-black");
189
+ inputSignal.hoverNone && btn.hover("hover:scale-100 hover:opacity-100");
190
+ inputSignal.hoverGlow && btn.hover("hover:shadow-lg hover:shadow-blue-500/50");
191
+ inputSignal.hoverRotate && btn.hover("hover:rotate-3");
192
+ inputSignal.hoverScale && btn.hover("hover:scale-110");
193
+ inputSignal.hoverBounce && btn.hover("hover:animate-bounce");
194
+ inputSignal.hoverPulse && btn.hover("hover:animate-pulse");
195
+ inputSignal.hoverSpin && btn.hover("hover:animate-spin");
196
+ inputSignal.hoverPing && btn.hover("hover:animate-ping");
197
+
198
+ inputSignal.activeShrink &&
199
+ btn.active("active:scale-95 transition-transform");
200
+ inputSignal.activeRipple &&
201
+ btn.active("active:ring-4 active:ring-black active:scale-90");
202
+ inputSignal.activeExplode &&
203
+ btn.active("active:scale-110 active:ring-8 active:ring-black");
204
+ inputSignal.activeSlide && btn.active("active:translate-x-0.5");
205
+ inputSignal.activeNone && btn.active("active:scale-100 active:opacity-100");
206
+ inputSignal.activeGlow && btn.active("active:shadow-lg active:shadow-blue-500/50");
207
+ inputSignal.activeRotate && btn.active("active:rotate-3");
208
+ inputSignal.activeBounce && btn.active("active:animate-bounce");
209
+ inputSignal.activePulse && btn.active("active:animate-pulse");
210
+
211
+ inputSignal.focusRing && btn.focus("focus:ring-4 focus:ring-blue-500");
212
+ inputSignal.focusGlow && btn.focus("focus:shadow-lg focus:shadow-blue-500/50");
213
+ inputSignal.focusScale && btn.focus("focus:scale-105");
214
+ inputSignal.focusNone && btn.focus("focus:outline-none");
215
+
216
+ inputSignal.bold && btn.textWeight("font-bold");
217
+ inputSignal.semibold && btn.textWeight("font-semibold");
218
+ inputSignal.light && btn.textWeight("font-light");
219
+ inputSignal.thin && btn.textWeight("font-thin");
220
+
221
+ inputSignal.uppercase && btn.textTransform("uppercase");
222
+ inputSignal.lowercase && btn.textTransform("lowercase");
223
+ inputSignal.capitalize && btn.textTransform("capitalize");
224
+
225
+ inputSignal.underline && btn.textDecoration("underline");
226
+ inputSignal.lineThrough && btn.textDecoration("line-through");
227
+ inputSignal.overline && btn.textDecoration("overline");
228
+
229
+ inputSignal.textXs && btn.textSize("text-xs");
230
+ inputSignal.textSm && btn.textSize("text-sm");
231
+ inputSignal.textBase && btn.textSize("text-base");
232
+ inputSignal.textLg && btn.textSize("text-lg");
233
+ inputSignal.textXl && btn.textSize("text-xl");
234
+ inputSignal.text2xl && btn.textSize("text-2xl");
235
+
236
+ inputSignal.textLeft && btn.textLayer("text-left");
237
+ inputSignal.textCenter && btn.textLayer("text-center");
238
+ inputSignal.textRight && btn.textLayer("text-right");
239
+ inputSignal.textJustify && btn.textLayer("text-justify");
240
+
241
+ inputSignal.animateSpin && btn.animation("animate-spin");
242
+ inputSignal.animatePulse && btn.animation("animate-pulse");
243
+ inputSignal.animateBounce && btn.animation("animate-bounce");
244
+ inputSignal.animatePing && btn.animation("animate-ping");
245
+ inputSignal.transitionNone && btn.animation("transition-none");
246
+ inputSignal.transitionFast && btn.animation("transition-all duration-150");
247
+ inputSignal.transitionSlow && btn.animation("transition-all duration-700");
248
+ inputSignal.transitionAll && btn.animation("transition-all duration-300 ease-in-out");
249
+ inputSignal.transitionColors && btn.animation("transition-colors duration-300");
250
+ inputSignal.transitionTransform && btn.animation("transition-transform duration-300");
251
+ inputSignal.transitionOpacity && btn.animation("transition-opacity duration-300");
252
+ inputSignal.transitionNone && btn.animation("transition-none");
253
+
254
+ inputSignal.loading && (
255
+ btn.color("opacity-75 cursor-not-allowed"),
256
+ data("disabled")
257
+ );
258
+ inputSignal.disabled && (
259
+ btn.color("opacity-50 cursor-not-allowed"),
260
+ data("disabled")
261
+ );
262
+ inputSignal.selected && btn.color("ring-2 ring-blue-500 ring-offset-2");
263
+ inputSignal.pressed && btn.active("scale-95");
264
+
265
+ inputSignal.relative && btn.layout("relative");
266
+ inputSignal.absolute && btn.layout("absolute");
267
+ inputSignal.fixed && btn.layout("fixed");
268
+ inputSignal.sticky && btn.layout("sticky");
269
+ inputSignal.m0 && btn.layout("m-0");
270
+ inputSignal.m1 && btn.layout("m-1");
271
+ inputSignal.m2 && btn.layout("m-2");
272
+ inputSignal.m4 && btn.layout("m-4");
273
+ inputSignal.m8 && btn.layout("m-8");
274
+ inputSignal.p0 && btn.layout("p-0");
275
+ inputSignal.p1 && btn.layout("p-1");
276
+ inputSignal.p2 && btn.layout("p-2");
277
+ inputSignal.p4 && btn.layout("p-4");
278
+ inputSignal.p8 && btn.layout("p-8");
279
+ inputSignal.flexRow && btn.layout("flex-row");
280
+ inputSignal.flexCol && btn.layout("flex-col");
281
+ inputSignal.flexWrap && btn.layout("flex-wrap");
282
+ inputSignal.flexNowrap && btn.layout("flex-nowrap");
283
+ inputSignal.gap1 && btn.layout("gap-1");
284
+ inputSignal.gap2 && btn.layout("gap-2");
285
+ inputSignal.gap4 && btn.layout("gap-4");
286
+ inputSignal.gap8 && btn.layout("gap-8");
287
+
288
+ inputSignal.children && data("children");
289
+ inputSignal.onClick && data("onClick");
290
+ inputSignal.type && data("type");
291
+ inputSignal["aria-label"] && data("aria-label");
292
+ inputSignal["aria-haspopup"] && data("aria-haspopup");
293
+ inputSignal["aria-expanded"] && data("aria-expanded");
294
+ inputSignal.onMouseEnter && data("onMouseEnter");
295
+ inputSignal.onMouseLeave && data("onMouseLeave");
296
+
297
+ state("ripples", 0, []);
298
+ inputSignal.ripple && data("ripple") && state("ripples", 1, []);
299
+
300
+ const rippleCounterRef = useRef(0);
301
+ const handleRippleClick = (e) => {
302
+ const rect = e.currentTarget.getBoundingClientRect();
303
+ stateSignal.ripples?.set([
304
+ ...(stateSignal.ripples?.get || []),
305
+ {
306
+ id: ++rippleCounterRef.current,
307
+ x: e.clientX - rect.left,
308
+ y: e.clientY - rect.top,
309
+ },
310
+ ]);
311
+ };
312
+ const rippleStyles = ` @keyframes ripple {
313
+ 0% { width: 0; height: 0; opacity: 0.5; }
314
+ 100% { width: 400px; height: 400px; opacity: 0; }
315
+ }`;
282
316
 
283
317
  return (
284
318
  <>
285
- <style>
286
- {rippleStyles}
287
- </style>
319
+ <style>{rippleStyles}</style>
288
320
 
289
- <button
321
+ <button
290
322
  type={dataSignal.type}
291
323
  onClick={(e) => {
292
- dataSignal.onClick?.(e);
293
- dataSignal.ripple && handleRippleClick(e);
324
+ dataSignal.onClick?.(e);
325
+ dataSignal.ripple && handleRippleClick(e);
294
326
  }}
295
327
  disabled={dataSignal.disabled}
296
328
  aria-label={dataSignal["aria-label"]}
@@ -299,24 +331,22 @@ export function Button(contract = {}) {
299
331
  onMouseEnter={dataSignal.onMouseEnter}
300
332
  onMouseLeave={dataSignal.onMouseLeave}
301
333
  className={classes(layerSignal.btn)}
302
- >
303
-
334
+ >
304
335
  {dataSignal.children}
305
336
 
306
- {dataSignal.ripple && (stateSignal.ripples?.get || [] ).map(ripple => (
337
+ {dataSignal.ripple &&
338
+ (stateSignal.ripples?.get || []).map((ripple) => (
307
339
  <span
308
- key={ripple.id}
309
- className={classes(layerSignal.ripple)}
310
- style={{
311
- left: ripple.x,
312
- top: ripple.y,
313
- animation: 'ripple 2s ease-out'
314
- }}
340
+ key={ripple.id}
341
+ className={classes(layerSignal.ripple)}
342
+ style={{
343
+ left: ripple.x,
344
+ top: ripple.y,
345
+ animation: "ripple 2s ease-out",
346
+ }}
315
347
  />
316
- ))}
317
-
318
- </button>
348
+ ))}
349
+ </button>
319
350
  </>
320
351
  );
321
- }
322
-
352
+ }