signal-layers 0.0.6 → 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/FabMenu.jsx CHANGED
@@ -1,205 +1,112 @@
1
- import { Button } from "./Button";
2
- import { useState } from "react";
1
+ import { Button, createSignalUtils } from "./";
2
+ export function FabMenu(contract = {}) {
3
+ const { layer, data, state, classes, signals } = createSignalUtils(contract);
4
+ const { inputSignal, layerSignal, dataSignal, stateSignal } = signals;
5
+ let root, fab, actions, item;
6
+ root = {
7
+ base: layer("base", "root"),
8
+ position: layer("position", "root")
9
+ };
10
+ fab = {
11
+ base: layer("base", "fab"),
12
+ size: layer("size", "fab"),
13
+ visibility: layer("visibility", "fab")
14
+ };
15
+ actions = {
16
+ base: layer("base", "actions"),
17
+ layout: layer("layout", "actions"),
18
+ visibility: layer("visibility", "actions"),
19
+ animation: layer("animation", "actions")
20
+ };
21
+ item = {
22
+ base: layer("base", "item")
23
+ };
24
+ root.base("fixed z-50");
25
+ root.position("bottom-6 right-6");
26
+
27
+ fab.base("flex items-center justify-center z-10");
28
+
29
+ actions.base("absolute flex z-40");
30
+ actions.layout("flex flex-col-reverse items-center absolute gap-2 bottom-full right-1/2 translate-x-1/2 mb-3 w-auto min-w-[40px]");
31
+ actions.visibility("opacity-100 pointer-events-auto");
32
+ actions.animation("transition-all duration-300");
3
33
 
4
- export function FabMenu(input = {}) {
5
- /* ────────────────────────────────────────────────────────────────────────────
6
- * CONTRACT
7
- * ────────────────────────────────────────────────────────────────────────────
8
- * Signals:
9
- * React: reactHover, reactClick
10
- * Position: bottomRight, bottomLeft, topRight, topLeft
11
- * Layout: actionsTop, actionsBottom, actionsLeft, actionsRight
12
- * Size: sm, md, lg, xl
13
- * State: disabled
14
- *
15
- * Data:
16
- * icon Icon for the FAB button
17
- * actions Array of { icon, label, onClick } objects
18
- *
19
- * Defaults: bottomRight, md, reactClick
20
- *
21
- * Usage: <FabMenu icon="+" actions={[{icon:"", label:"Action 1", onClick:() => {}}]} />
22
- *
23
- * ──────────────────────────────────────────────────────────────────────────── */
34
+ item.base("flex items-center justify-center");
24
35
 
25
- const [inputSignal, layerSignal, dataSignal, stateSignal] = [{ ...input }, {}, {}, {}];
36
+ inputSignal.bottomRight && root.position("bottom-6 right-6");
37
+ inputSignal.bottomLeft && root.position("bottom-6 left-6");
38
+ inputSignal.topRight && root.position("top-6 right-6");
39
+ inputSignal.topLeft && root.position("top-6 left-6");
26
40
 
27
- /* ────────────────────────────────────────────────────────────────────────────
28
- * CONTRACT TOOLS
29
- * ──────────────────────────────────────────────────────────────────────────── */
30
- const layer = (name, scope = "root") => (className) =>
31
- (layerSignal[scope] ||= {},
32
- layerSignal[scope][name] ||= [],
33
- (layerSignal[scope][name][0] = className));
41
+ inputSignal.actionsTop && actions.layout("flex flex-col-reverse items-center absolute gap-2 bottom-full right-1/2 translate-x-1/2 mb-3 w-auto min-w-[40px]");
42
+ inputSignal.actionsBottom && actions.layout("flex flex-col-reverse items-center absolute gap-2 top-full right-1/2 translate-x-1/2 mb-3 w-auto min-w-[40px]");
43
+ inputSignal.actionsLeft && actions.layout("flex flex-col items-center absolute gap-2 right-full top-0 mr-2");
44
+ inputSignal.actionsRight && actions.layout("flex flex-col items-center absolute gap-2 left-full top-0 ml-2");
34
45
 
35
- const data = (name, key = name) =>
36
- inputSignal[key] && (dataSignal[name] = inputSignal[key]);
46
+ inputSignal.sm && fab.size("w-8 h-8");
47
+ inputSignal.md && fab.size("w-10 h-10");
48
+ inputSignal.lg && fab.size("w-12 h-12");
49
+ inputSignal.xl && fab.size("w-14 h-14");
37
50
 
38
- const classes = (layers = {}) =>
39
- Object.values(layers).map(l => l[0]).filter(Boolean).join(" ");
51
+ inputSignal.disabled && (fab.visibility("opacity-50 pointer-events-none"), actions.visibility("hidden"));
40
52
 
41
- const state = (name, priority = 0) => (
42
- (stateSignal._hooks ||= {})[name] ||= (() => { const [get, set] = useState(false); return ({ get, set }); })(),
43
- priority && (!stateSignal._priority || priority > stateSignal._priority) && (stateSignal[name] = stateSignal._hooks[name], stateSignal._priority = priority)
53
+ inputSignal.icon && data("icon");
54
+ inputSignal.actions && data("actions");
55
+
56
+ state("reactHover", 0);
57
+ state("reactClick", 0);
58
+ inputSignal.reactHover && state("reactHover", 1);
59
+ state("reactClick", 1);
60
+
61
+ const ActionButton = ({ action }) => (
62
+ <Button
63
+ circle
64
+ sm
65
+ ghost
66
+ onClick={() => {
67
+ action.onClick();
68
+ stateSignal.reactClick?.set &&
69
+ stateSignal.reactClick?.set(!stateSignal.reactClick.get);
70
+ }}
71
+ aria-label={action.label}
72
+ className={classes(layerSignal.item)}
73
+ >
74
+ <span>{action.icon}</span>
75
+ </Button>
76
+ );
77
+
78
+ return (
79
+ <div className={classes(layerSignal.root)}>
80
+
81
+ <div className={classes(layerSignal.actions)}>
82
+ {(stateSignal.reactHover?.get || stateSignal.reactClick?.get) &&
83
+ dataSignal.actions?.map((action, i) => (
84
+ <ActionButton key={i} action={action} />
85
+ ))}
86
+ </div>
87
+
88
+ <Button
89
+ circle
90
+ md
91
+ cta
92
+ aria-haspopup="true"
93
+ aria-expanded={stateSignal.reactClick?.get}
94
+ onClick={() =>
95
+ stateSignal.reactClick?.set &&
96
+ stateSignal.reactClick?.set(!stateSignal.reactClick.get)
97
+ }
98
+ onMouseEnter={
99
+ stateSignal.reactHover?.set &&
100
+ (() => stateSignal.reactHover.set(true))
101
+ }
102
+ onMouseLeave={
103
+ stateSignal.reactHover?.set &&
104
+ (() => stateSignal.reactHover.set(false))
105
+ }
106
+ className={classes(layerSignal.fab)}
107
+ >
108
+ {dataSignal.icon}
109
+ </Button>
110
+ </div>
44
111
  );
45
- /* ────────────────────────────────────────────────────────────────────────────
46
- * BASE LAYERS
47
- * ──────────────────────────────────────────────────────────────────────────── */
48
- let root, fab, actions, item;
49
- (() =>
50
- (
51
- root = {
52
- base: layer("base", "root"),
53
- position: layer("position", "root")
54
- }
55
- )
56
- )(),
57
- (() =>
58
- (
59
- fab = {
60
- base: layer("base", "fab"),
61
- size: layer("size", "fab"),
62
- visibility: layer("visibility", "fab")
63
- }
64
- )
65
- )(),
66
- (() =>
67
- (
68
- actions = {
69
- base: layer("base", "actions"),
70
- layout: layer("layout", "actions"),
71
- visibility: layer("visibility", "actions"),
72
- animation: layer("animation", "actions")
73
- }
74
- )
75
- )(),
76
- (() =>
77
- (
78
- item = { base: layer("base", "item") }
79
- )
80
- )(),
81
- /* ────────────────────────────────────────────────────────────────────────────
82
- * DEFAULTS
83
- * ──────────────────────────────────────────────────────────────────────────── */
84
- (() =>
85
- (
86
- root.base("fixed z-50"),
87
- root.position("bottom-6 right-6"),
88
- fab.base("flex items-center justify-center z-10"),
89
- actions.base("absolute flex z-40"),
90
- actions.layout("flex flex-col-reverse items-center absolute gap-2 bottom-full right-1/2 translate-x-1/2 mb-3 w-auto min-w-[40px]"),
91
- actions.visibility("opacity-100 pointer-events-auto"),
92
- actions.animation("transition-all duration-300"),
93
- item.base("flex items-center justify-center")
94
- )
95
- )(),
96
- /* ────────────────────────────────────────────────────────────────────────────
97
- * POSITION SIGNALS
98
- * ──────────────────────────────────────────────────────────────────────────── */
99
- (() =>
100
- (
101
- inputSignal.bottomRight && root.position("bottom-6 right-6"),
102
- inputSignal.bottomLeft && root.position("bottom-6 left-6"),
103
- inputSignal.topRight && root.position("top-6 right-6"),
104
- inputSignal.topLeft && root.position("top-6 left-6")
105
- )
106
- )(),
107
- /* ────────────────────────────────────────────────────────────────────────────
108
- * LAYOUT SIGNALS
109
- * ──────────────────────────────────────────────────────────────────────────── */
110
- (() =>
111
- (
112
- inputSignal.actionsTop && actions.layout("flex flex-col-reverse items-center absolute gap-2 bottom-full right-1/2 translate-x-1/2 mb-3 w-auto min-w-[40px]"),
113
- inputSignal.actionsBottom && actions.layout("flex flex-col-reverse items-center absolute gap-2 top-full right-1/2 translate-x-1/2 mb-3 w-auto min-w-[40px]"),
114
- inputSignal.actionsLeft && actions.layout("flex flex-col items-center absolute gap-2 right-full top-0 mr-2"),
115
- inputSignal.actionsRight && actions.layout("flex flex-col items-center absolute gap-2 left-full top-0 ml-2")
116
- )
117
- )(),
118
- /* ────────────────────────────────────────────────────────────────────────────
119
- * SIZE SIGNALS
120
- * ──────────────────────────────────────────────────────────────────────────── */
121
- (() =>
122
- (
123
- inputSignal.sm && fab.size("w-8 h-8"),
124
- inputSignal.md && fab.size("w-10 h-10"),
125
- inputSignal.lg && fab.size("w-12 h-12"),
126
- inputSignal.xl && fab.size("w-14 h-14")
127
- )
128
- )(),
129
- /* ────────────────────────────────────────────────────────────────────────────
130
- * VISIBILITY SIGNALS
131
- * ──────────────────────────────────────────────────────────────────────────── */
132
- (() =>
133
- (
134
- inputSignal.disabled && (
135
- fab.visibility("opacity-50 pointer-events-none"),
136
- actions.visibility("hidden")
137
- )
138
- )
139
- )(),
140
- /* ────────────────────────────────────────────────────────────────────────────
141
- * DATA & STATE
142
- * ──────────────────────────────────────────────────────────────────────────── */
143
- (() =>
144
- (
145
- inputSignal.icon && data("icon"),
146
- inputSignal.actions && data("actions"),
147
- state("reactHover", 0), inputSignal.reactHover && state("reactHover", 1),
148
- state("reactClick", 0), state("reactClick", 1)
149
- )
150
- )();
151
- /* ────────────────────────────────────────────────────────────────────────────
152
- * INTERNAL COMPONENTS
153
- * ──────────────────────────────────────────────────────────────────────────── */
154
- const ActionButton = ({ action }) => (
155
- <Button
156
- circle
157
- sm
158
- ghost
159
- onClick={() => {
160
- action.onClick();
161
- stateSignal.reactClick?.set && stateSignal.reactClick?.set(!stateSignal.reactClick.get);
162
- }}
163
- aria-label={action.label}
164
- className={classes(layerSignal.item)}
165
- >
166
- <span>{action.icon}</span>
167
- </Button>
168
- );
169
- /* ────────────────────────────────────────────────────────────
170
- * RENDER
171
- * ──────────────────────────────────────────────────────────── */
172
- return (
173
- <div
174
- className={classes(layerSignal.root)}
175
- >
176
- <div
177
- className={classes(layerSignal.actions)}
178
- >
179
- {
180
- (stateSignal.reactHover?.get || stateSignal.reactClick?.get)
181
- && dataSignal.actions?.map((action, i) => (
182
- <ActionButton
183
- key={i}
184
- action={action}
185
- />
186
- ))
187
- }
188
- </div>
189
-
190
- <Button
191
- circle
192
- md
193
- cta
194
- aria-haspopup="true"
195
- aria-expanded={stateSignal.reactClick?.get}
196
- onClick={() => stateSignal.reactClick?.set && stateSignal.reactClick?.set(!stateSignal.reactClick.get)}
197
- onMouseEnter={stateSignal.reactHover?.set && (() => stateSignal.reactHover.set(true))}
198
- onMouseLeave={stateSignal.reactHover?.set && (() => stateSignal.reactHover.set(false))}
199
- className={classes(layerSignal.fab)}
200
- >
201
- {dataSignal.icon}
202
- </Button>
203
- </div>
204
- );
205
- }
112
+ }
@@ -1,134 +1,52 @@
1
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
- * ──────────────────────────────────────────────────────────────────────────── */
2
+ const { layer, data, state, classes, signals } = createSignalUtils(contract);
3
+ const { inputSignal, layerSignal, dataSignal, stateSignal } = signals;
4
+ let progressbar;
5
+ progressbar = {
6
+ base: layer("base", "progressbar"),
7
+ size: layer("size", "progressbar"),
8
+ color: layer("color", "progressbar"),
9
+ shape: layer("shape", "progressbar"),
10
+ layout: layer("layout", "progressbar")
11
+ };
12
+ progressbar.base("[&::-webkit-progress-value]:transition-all [&::-webkit-progress-bar]:transition-all [&::-moz-progress-bar]:transition-all");
13
+ progressbar.shape("[&::-webkit-progress-value]:rounded-full [&::-webkit-progress-bar]:rounded-full [&::-moz-progress-bar]:rounded-full");
14
+ progressbar.color("[&::-webkit-progress-value]:bg-violet-400 [&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-violet-400");
15
+ progressbar.size("h-2 w-32");
16
+ progressbar.layout("block");
29
17
 
30
- const [inputSignal, layerSignal, dataSignal] = [{ ...contract }, {}, {}];
18
+ inputSignal.square && progressbar.shape(`[&::-webkit-progress-bar]:rounded-none [&::-webkit-progress-value]:rounded-none [&::-moz-progress-bar]:rounded-none`);
31
19
 
32
- /* ────────────────────────────────────────────────────────────────────────────
33
- * CONTRACT TOOLS
34
- * ──────────────────────────────────────────────────────────────────────────── */
20
+ inputSignal.primary && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-blue-500 [&::-webkit-progress-value]:bg-blue-500`);
21
+ inputSignal.success && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-green-500 [&::-webkit-progress-value]:bg-green-500`);
22
+ inputSignal.danger && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-red-500 [&::-webkit-progress-value]:bg-red-500`);
23
+ inputSignal.neutral && progressbar.color(`[&::-webkit-progress-bar]:bg-slate-300 [&::-moz-progress-bar]:bg-gray-500 [&::-webkit-progress-value]:bg-gray-500`);
24
+ inputSignal.transparent && progressbar.color(`[&::-webkit-progress-bar]:bg-transparent [&::-moz-progress-bar]:bg-transparent [&::-webkit-progress-value]:bg-transparent`);
35
25
 
36
- const layer = (name) => (className) =>
37
- (layerSignal[name] ||= [],
38
- (layerSignal[name][0] = className));
26
+ inputSignal.sm && progressbar.size("h-1 w-16");
27
+ inputSignal.md && progressbar.size("h-2 w-32");
28
+ inputSignal.lg && progressbar.size("h-3 w-48");
29
+ inputSignal.xl && progressbar.size("h-4 w-64");
30
+ inputSignal.responsive && progressbar.size("h-2 w-full");
39
31
 
40
- const data = (name, key = name) =>
41
- dataSignal[key] !== undefined &&
42
- (dataSignal[name] = dataSignal[key]);
32
+ inputSignal.inline && progressbar.layout("inline-block");
33
+ inputSignal.block && progressbar.layout("block");
34
+ inputSignal.centered && progressbar.layout("mx-auto");
35
+
36
+ inputSignal.value && data("value");
37
+ inputSignal.max && data("max");
38
+
39
+ const value =
40
+ dataSignal.value !== undefined
41
+ ? Math.min(100, Math.max(0, Number(dataSignal.value)))
42
+ : 0;
43
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
44
  return (
127
45
  <progress
128
46
  value={value}
129
47
  max={dataSignal.max ?? 100}
130
- className={Object.values(layerSignal)
131
- .map(l => l[0])
48
+ className={Object.values(layerSignal.progressbar)
49
+ .map((l) => l[0])
132
50
  .filter(Boolean)
133
51
  .join(" ")}
134
52
  />