@bunnix/components 0.10.1 → 0.10.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnix/components",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "Bunnix components: a set of bunnix ready components for modern web apps.",
5
5
  "keywords": [
6
6
  "bunnix",
@@ -5,6 +5,8 @@
5
5
  display: flex;
6
6
  flex-direction: row;
7
7
  align-items: center;
8
+ flex-shrink: 0;
9
+ flex-wrap: nowrap;
8
10
  justify-content: center;
9
11
  cursor: pointer;
10
12
  border-radius: var(--radius-md);
@@ -68,6 +70,7 @@
68
70
  align-items: center;
69
71
  cursor: pointer;
70
72
  border-radius: var(--radius-md);
73
+ padding: 0;
71
74
  user-select: none;
72
75
  -webkit-user-select: none;
73
76
  background: none;
@@ -0,0 +1,69 @@
1
+ .safe-insets {
2
+ padding-left: env(safe-area-inset-left);
3
+ padding-right: env(safe-area-inset-right);
4
+ padding-top: env(safe-area-inset-top);
5
+ padding-bottom: env(safe-area-inset-bottom);
6
+ }
7
+
8
+ @media (max-width: 720px) {
9
+ :root {
10
+ --font-size-xlarge: 20px;
11
+ --font-size-large: 18px;
12
+ --font-size-default: 16px;
13
+ --font-size-small: 14px;
14
+ --font-size-xsmall: 12px;
15
+
16
+ --padding-sm: 10px;
17
+ --padding-md: 12px;
18
+ --padding-lg: 16px;
19
+ }
20
+
21
+ .grid {
22
+ display: flex;
23
+ flex-direction: column;
24
+ }
25
+
26
+ .table {
27
+ display: flex;
28
+ flex-direction: column;
29
+ }
30
+
31
+ .table thead {
32
+ display: none;
33
+ }
34
+
35
+ .table tbody tr {
36
+ display: flex;
37
+ flex-direction: column;
38
+ border-bottom: 1px solid var(--color-border-primary);
39
+ }
40
+
41
+ .table tbody tr:last-child {
42
+ border-bottom: none;
43
+ }
44
+
45
+ .table tbody td {
46
+ display: grid;
47
+ grid-template-columns: minmax(0.1fr, 0.3fr) 0.5fr;
48
+ border-bottom: none;
49
+ padding-top: var(--padding-md);
50
+ padding-bottom: 0;
51
+ row-gap: 0;
52
+ }
53
+
54
+ .table tbody td:last-child {
55
+ padding-bottom: var(--padding-md);
56
+ }
57
+
58
+ .table tbody td[data-label]::before {
59
+ content: attr(data-label) ": ";
60
+ font-weight: var(--font-weight-heavier);
61
+ font-size: var(--font-size-default);
62
+ color: var(--color-fg-tertiary);
63
+ }
64
+
65
+ .checkbox {
66
+ width: 28px;
67
+ height: 28px;
68
+ }
69
+ }
package/src/core/core.css CHANGED
@@ -61,7 +61,7 @@
61
61
  --font-size-large: 16px;
62
62
  --font-size-default: 14px;
63
63
  --font-size-small: 12px;
64
- --font-size-xmall: 10px;
64
+ --font-size-xsmall: 10px;
65
65
  --font-weight-default: 400;
66
66
  --font-weight-heavy: 500;
67
67
  --font-weight-heavier: 600;
@@ -397,6 +397,27 @@ body {
397
397
  flex-shrink: 0;
398
398
  }
399
399
 
400
+ /* Layout positioning */
401
+ .sticky-top {
402
+ position: sticky;
403
+ top: 0;
404
+ }
405
+
406
+ .sticky-bottom {
407
+ position: sticky;
408
+ bottom: 0;
409
+ }
410
+
411
+ .fixed-top {
412
+ position: fixed;
413
+ top: 0;
414
+ }
415
+
416
+ .fixed-bottom {
417
+ position: fixed;
418
+ bottom: 0;
419
+ }
420
+
400
421
  /* Spacing: padding */
401
422
  .padding-none,
402
423
  [padding="none"] {
@@ -610,6 +631,10 @@ body {
610
631
  }
611
632
 
612
633
  /* Effects */
634
+ .drop-shadow {
635
+ filter: drop-shadow(0px 1px 2px #00000033);
636
+ }
637
+
613
638
  @media (prefers-color-scheme: dark) {
614
639
  .dark-color-inverted,
615
640
  [dark-color="inverted"],
@@ -618,3 +643,6 @@ body {
618
643
  filter: brightness(0) invert(1);
619
644
  }
620
645
  }
646
+
647
+ /* Mobile overrides - must be imported last to properly override desktop values */
648
+ @import "./core-mobile.css";
@@ -34,6 +34,10 @@
34
34
  place-content: center;
35
35
  margin: 0;
36
36
  flex-shrink: 0;
37
+ width: 22px;
38
+ height: 22px;
39
+ aspect-ratio: 1 / 1;
40
+ object-fit: contain;
37
41
  }
38
42
 
39
43
  .checkbox:checked {
@@ -233,7 +233,7 @@ export const TextInput = withNormalizedArgs((props, ...children) =>
233
233
  export const Select = withNormalizedArgs((props, ...children) =>
234
234
  withExtractedStyles((finalProps, ...children) =>
235
235
  SelectCore(finalProps, ...children),
236
- )({ height: 32, textSize: "1rem", ...props }, ...children),
236
+ )({ minHeight: 32, textSize: "1rem", ...props }, ...children),
237
237
  );
238
238
 
239
239
  /**
@@ -255,5 +255,5 @@ export const Select = withNormalizedArgs((props, ...children) =>
255
255
  export const CheckBox = withNormalizedArgs((props, ...children) =>
256
256
  withExtractedStyles((finalProps, ...children) =>
257
257
  CheckBoxCore(finalProps, ...children),
258
- )({ size: 20, textSize: "1rem", ...props }, ...children),
258
+ )({ textSize: "1rem", ...props }, ...children),
259
259
  );
@@ -18,3 +18,18 @@
18
18
  flex-grow: 1;
19
19
  flex-shrink: 1;
20
20
  }
21
+
22
+ /* Grid Base Styles */
23
+ .grid {
24
+ --grid-gap: var(--gap-md);
25
+ display: grid;
26
+ column-gap: var(--grid-gap);
27
+ row-gap: var(--grid-gap);
28
+ }
29
+
30
+ .grid.flow {
31
+ display: flex;
32
+ flex-wrap: wrap;
33
+ flex-direction: row;
34
+ gap: var(--grid-gap);
35
+ }
@@ -101,28 +101,16 @@ export const Spacer = withNormalizedArgs((props = {}, ...children) => {
101
101
  const GridCore = (props, ...children) => {
102
102
  let layout = props.layout ?? "fixed";
103
103
  let columns = props.columns ?? [];
104
- let gap = props.gridGap ?? "var(--gap-md)";
104
+ let gap = props.gridGap;
105
105
 
106
106
  delete props.layout;
107
107
  delete props.columns;
108
108
  delete props.gridGap;
109
109
 
110
- gap = (typeof gap === "number") ? `${gap}px` : gap;
110
+ let style = {};
111
111
 
112
- let style = {
113
- display: "grid",
114
- "column-gap": gap,
115
- "row-gap": gap,
116
- };
117
-
118
- if (layout === "flow") {
119
- style = {
120
- ...style,
121
- "display": "flex",
122
- "flex-wrap": "wrap",
123
- "flex-direction": "row",
124
- "gap": gap
125
- }
112
+ if (gap !== undefined) {
113
+ style["--grid-gap"] = (typeof gap === "number") ? `${gap}px` : gap;
126
114
  }
127
115
 
128
116
  if (layout === "fixed") {
@@ -132,14 +120,13 @@ const GridCore = (props, ...children) => {
132
120
  return col.size ?? "1fr";
133
121
  }).join(" ");
134
122
 
135
- style = {
136
- ...style,
137
- "grid-template-columns": columnsTemplate,
123
+ if (columnsTemplate) {
124
+ style["grid-template-columns"] = columnsTemplate;
138
125
  }
139
126
  }
140
127
 
141
128
  return div(
142
- { ...props, class: `grid ${props.class ?? ""}`, style },
129
+ { ...props, class: `grid ${layout} ${props.class ?? ""}`, style },
143
130
  children
144
131
  )
145
132
  };
package/src/core/menu.css CHANGED
@@ -7,9 +7,6 @@
7
7
 
8
8
  .menu-items {
9
9
  position: absolute;
10
- top: 100%;
11
- left: 0;
12
- margin-top: 4px;
13
10
  background-color: var(--color-bg-primary);
14
11
  border: 1px solid var(--color-border-primary);
15
12
  border-radius: var(--radius-md);
@@ -19,6 +16,31 @@
19
16
  padding: 4px;
20
17
  }
21
18
 
19
+ /* Anchor positions */
20
+ .menu-items--bottomLeft {
21
+ top: 100%;
22
+ left: 0;
23
+ margin-top: 4px;
24
+ }
25
+
26
+ .menu-items--bottomRight {
27
+ top: 100%;
28
+ right: 0;
29
+ margin-top: 4px;
30
+ }
31
+
32
+ .menu-items--topLeft {
33
+ bottom: 100%;
34
+ left: 0;
35
+ margin-bottom: 4px;
36
+ }
37
+
38
+ .menu-items--topRight {
39
+ bottom: 100%;
40
+ right: 0;
41
+ margin-bottom: 4px;
42
+ }
43
+
22
44
  .menu-divider {
23
45
  height: 1px;
24
46
  background-color: var(--color-border-primary);
package/src/core/menu.mjs CHANGED
@@ -34,8 +34,12 @@ const MenuCore = (props, ...children) => {
34
34
  // Resolve trigger
35
35
  let trigger = props.trigger || "Menu";
36
36
 
37
+ // Resolve anchor position
38
+ let anchor = props.anchor || "bottomLeft";
39
+
37
40
  delete props.items;
38
41
  delete props.trigger;
42
+ delete props.anchor;
39
43
 
40
44
  // Click outside handler
41
45
  useEffect(() => {
@@ -79,7 +83,7 @@ const MenuCore = (props, ...children) => {
79
83
  // Menu
80
84
  Show(isOpen, (open) =>
81
85
  open && div(
82
- { class: "menu-items" },
86
+ { class: `menu-items menu-items--${anchor}` },
83
87
  Column(
84
88
  { gap: 0 },
85
89
  ...items.map((item) => {
@@ -117,6 +121,7 @@ const MenuCore = (props, ...children) => {
117
121
  * @param {Function} [props.items[].action] - Optional action to run on click
118
122
  * @param {boolean} [props.items[].divider] - If true, renders a divider
119
123
  * @param {*} [props.trigger] - Trigger element or function that receives {isOpen, toggle}
124
+ * @param {string} [props.anchor="bottomLeft"] - Menu anchor position: "topLeft" | "topRight" | "bottomLeft" | "bottomRight"
120
125
  * @param {string} [props.class] - Additional CSS classes
121
126
  * @param {...*} children - Children elements
122
127
  * @returns {*} Menu component
@@ -9,13 +9,13 @@
9
9
 
10
10
  .table thead td,
11
11
  .table th {
12
- font-weight: var(--font-weight-heavy);
13
- padding: var(--padding-sm) var(--padding-md);
12
+ font-weight: var(--font-weight-heavier);
13
+ padding: calc(var(--padding-md) * 0.75) var(--padding-md);
14
14
  border-bottom: 1px solid var(--color-border-primary);
15
15
  }
16
16
 
17
17
  .table td {
18
- padding: var(--padding-sm) var(--padding-md);
18
+ padding: calc(var(--padding-md) * 0.5) var(--padding-md);
19
19
  border-bottom: 1px solid var(--color-border-primary);
20
20
  }
21
21
 
@@ -32,7 +32,7 @@ const TableCore = withNormalizedArgs((props, ...children) => {
32
32
  headers.map((h) => {
33
33
  if (!h.key) return td("");
34
34
  if (!(h.key in r)) return td("");
35
- return td(r[h.key]);
35
+ return td({ "data-label": h.content }, r[h.key]);
36
36
  }),
37
37
  ),
38
38
  );
@@ -52,7 +52,7 @@ const TableCore = withNormalizedArgs((props, ...children) => {
52
52
  // Apply default Table props at export
53
53
  /**
54
54
  * Data table component with column headers and row rendering.
55
- *
55
+ *
56
56
  * @param {Object} props - Component props
57
57
  * @param {Array<{content: *, key: string, size?: number}>} props.headers - Column definitions with content, key for data mapping, and optional size
58
58
  * @param {Array<Object>} props.rows - Array of data objects to render as rows (keys should match header keys)
@@ -60,7 +60,7 @@ const TableCore = withNormalizedArgs((props, ...children) => {
60
60
  * @param {string} [props.class] - Additional CSS classes
61
61
  * @param {...*} children - Child elements
62
62
  * @returns {*} Table component
63
- *
63
+ *
64
64
  * @example
65
65
  * Table({
66
66
  * headers: [
@@ -161,6 +161,70 @@ export function withExtractedStyles(fn) {
161
161
  delete finalProps.marginY;
162
162
  }
163
163
 
164
+ if ("marginLeft" in props) {
165
+ style.marginLeft =
166
+ typeof props.marginLeft === "number"
167
+ ? `${props.marginLeft}px`
168
+ : props.marginLeft;
169
+ delete finalProps.marginLeft;
170
+ }
171
+
172
+ if ("marginRight" in props) {
173
+ style.marginRight =
174
+ typeof props.marginRight === "number"
175
+ ? `${props.marginRight}px`
176
+ : props.marginRight;
177
+ delete finalProps.marginRight;
178
+ }
179
+
180
+ if ("marginTop" in props) {
181
+ style.marginTop =
182
+ typeof props.marginTop === "number"
183
+ ? `${props.marginTop}px`
184
+ : props.marginTop;
185
+ delete finalProps.marginTop;
186
+ }
187
+
188
+ if ("marginBottom" in props) {
189
+ style.marginBottom =
190
+ typeof props.marginBottom === "number"
191
+ ? `${props.marginBottom}px`
192
+ : props.marginBottom;
193
+ delete finalProps.marginBottom;
194
+ }
195
+
196
+ if ("top" in props) {
197
+ style.top =
198
+ typeof props.top === "number"
199
+ ? `${props.top}px`
200
+ : props.top;
201
+ delete finalProps.top;
202
+ }
203
+
204
+ if ("bottom" in props) {
205
+ style.bottom =
206
+ typeof props.bottom === "number"
207
+ ? `${props.bottom}px`
208
+ : props.bottom;
209
+ delete finalProps.bottom;
210
+ }
211
+
212
+ if ("left" in props) {
213
+ style.left =
214
+ typeof props.left === "number"
215
+ ? `${props.left}px`
216
+ : props.left;
217
+ delete finalProps.left;
218
+ }
219
+
220
+ if ("right" in props) {
221
+ style.right =
222
+ typeof props.right === "number"
223
+ ? `${props.right}px`
224
+ : props.right;
225
+ delete finalProps.right;
226
+ }
227
+
164
228
  if ("flexGrow" in props) {
165
229
  style.flexGrow = props.flexGrow;
166
230
  delete finalProps.flexGrow;