@jsenv/navi 0.7.3 → 0.9.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.
@@ -0,0 +1,514 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>useLayoutStyle Demo - Text</title>
6
+ <style>
7
+ .demo-container {
8
+ margin-bottom: 30px;
9
+ padding: 20px;
10
+ background: #f8f9fa;
11
+ border-radius: 8px;
12
+ }
13
+
14
+ .demo-box {
15
+ min-width: 60px;
16
+ padding: 10px;
17
+ text-align: center;
18
+ background: #e3f2fd;
19
+ border: 1px solid #90caf9;
20
+ border-radius: 4px;
21
+ }
22
+
23
+ .visual-container {
24
+ margin: 10px 0;
25
+ padding: 10px;
26
+ background: #fff3e0;
27
+ border: 2px dashed #ff9800;
28
+ }
29
+
30
+ .text-showcase {
31
+ margin: 10px 0;
32
+ padding: 15px;
33
+ background: #f5f5f5;
34
+ border: 1px solid #ddd;
35
+ border-radius: 4px;
36
+ }
37
+
38
+ h3 {
39
+ margin-top: 0;
40
+ }
41
+
42
+ /* Helper to visualize the container boundaries */
43
+ .bounded {
44
+ width: 400px;
45
+ height: 100px;
46
+ padding: 10px;
47
+ background: #f0f4ff;
48
+ border: 1px solid #6366f1;
49
+ }
50
+
51
+ /* Helper to show text boundaries */
52
+ .text-boundary {
53
+ display: inline-block;
54
+ border: 1px dashed #999;
55
+ }
56
+ </style>
57
+ </head>
58
+ <body>
59
+ <div id="app"></div>
60
+ <script type="module" jsenv-type="module/jsx">
61
+ /* eslint-disable no-unused-vars */
62
+ import { render } from "preact";
63
+ import { FlexRow, FlexColumn, Text, Button } from "@jsenv/navi";
64
+
65
+ const DemoBox = ({ children, ...props }) => (
66
+ <div className="demo-box" {...props}>
67
+ {children}
68
+ </div>
69
+ );
70
+
71
+ const Demo = () => {
72
+ return (
73
+ <div style={{ padding: "20px", fontFamily: "system-ui, sans-serif" }}>
74
+ <h1>useLayoutStyle Demo - Text Elements</h1>
75
+ <p>
76
+ Testing spacing (margin/padding), alignment, and expand behavior
77
+ specifically with Text components in various layout contexts.
78
+ </p>
79
+
80
+ <div className="demo-container">
81
+ <h3>1. Text with Expand in FlexRow</h3>
82
+ <div className="visual-container">
83
+ <FlexRow gap="10px">
84
+ <DemoBox>Start</DemoBox>
85
+ <Text
86
+ expand
87
+ style={{ background: "#e8f5e8", padding: "10px" }}
88
+ >
89
+ This text expands to fill available horizontal space
90
+ </Text>
91
+ <DemoBox>End</DemoBox>
92
+ </FlexRow>
93
+ </div>
94
+ </div>
95
+
96
+ <div className="demo-container">
97
+ <h3>2. Text with Expand in FlexColumn</h3>
98
+ <div className="visual-container">
99
+ <FlexColumn gap="10px" style={{ height: "200px" }}>
100
+ <Text style={{ background: "#fff3e0", padding: "10px" }}>
101
+ Fixed text at top
102
+ </Text>
103
+ <Text
104
+ expand
105
+ style={{ background: "#e8f5e8", padding: "10px" }}
106
+ >
107
+ This text expands vertically to fill available space
108
+ </Text>
109
+ <Text style={{ background: "#fff3e0", padding: "10px" }}>
110
+ Fixed text at bottom
111
+ </Text>
112
+ </FlexColumn>
113
+ </div>
114
+ </div>
115
+
116
+ <div className="demo-container">
117
+ <h3>3. Text with Expand outside Flex context</h3>
118
+ <div className="visual-container">
119
+ <div
120
+ style={{
121
+ width: "300px",
122
+ border: "1px solid #ccc",
123
+ padding: "10px",
124
+ }}
125
+ >
126
+ <Text style={{ background: "#fff3e0", padding: "5px" }}>
127
+ Normal text width
128
+ </Text>
129
+ <br />
130
+ <br />
131
+ <Text
132
+ expand
133
+ style={{ background: "#e8f5e8", padding: "5px" }}
134
+ >
135
+ Expanding text (should take full width)
136
+ </Text>
137
+ </div>
138
+ </div>
139
+ </div>
140
+
141
+ <div className="demo-container">
142
+ <h3>4. Text Spacing - Margins</h3>
143
+ <div className="visual-container">
144
+ <div className="text-showcase">
145
+ <Text
146
+ margin="15px"
147
+ style={{ background: "#e3f2fd", padding: "5px" }}
148
+ >
149
+ Text with margin all sides
150
+ </Text>
151
+ <br />
152
+ <Text
153
+ marginX="25px"
154
+ style={{ background: "#f3e5f5", padding: "5px" }}
155
+ >
156
+ Text with marginX only
157
+ </Text>
158
+ <br />
159
+ <Text
160
+ marginY="20px"
161
+ style={{ background: "#e8f5e8", padding: "5px" }}
162
+ >
163
+ Text with marginY only
164
+ </Text>
165
+ <br />
166
+ <Text
167
+ marginLeft="40px"
168
+ style={{ background: "#fff3e0", padding: "5px" }}
169
+ >
170
+ Text with marginLeft only
171
+ </Text>
172
+ </div>
173
+ </div>
174
+ </div>
175
+
176
+ <div className="demo-container">
177
+ <h3>5. Text Spacing - Padding</h3>
178
+ <div className="visual-container">
179
+ <div className="text-showcase">
180
+ <Text
181
+ style={{
182
+ background: "#e3f2fd",
183
+ border: "1px solid #90caf9",
184
+ }}
185
+ >
186
+ Default padding
187
+ </Text>
188
+ <br />
189
+ <br />
190
+ <Text
191
+ padding="20px"
192
+ style={{
193
+ background: "#f3e5f5",
194
+ border: "1px solid #ce93d8",
195
+ }}
196
+ >
197
+ Extra padding all sides
198
+ </Text>
199
+ <br />
200
+ <br />
201
+ <Text
202
+ paddingX="30px"
203
+ style={{
204
+ background: "#e8f5e8",
205
+ border: "1px solid #81c784",
206
+ }}
207
+ >
208
+ PaddingX only
209
+ </Text>
210
+ <br />
211
+ <br />
212
+ <Text
213
+ paddingY="15px"
214
+ style={{
215
+ background: "#fff3e0",
216
+ border: "1px solid #ffb74d",
217
+ }}
218
+ >
219
+ PaddingY only
220
+ </Text>
221
+ </div>
222
+ </div>
223
+ </div>
224
+
225
+ <div className="demo-container">
226
+ <h3>6. Text AlignX Test: FlexRow + alignX="end"</h3>
227
+ <div className="visual-container">
228
+ <div className="bounded">
229
+ <FlexRow gap="10px" style={{ height: "100%" }}>
230
+ <Text style={{ background: "#e3f2fd", padding: "5px" }}>
231
+ Default position
232
+ </Text>
233
+ <Text
234
+ alignX="end"
235
+ style={{ background: "#f3e5f5", padding: "5px" }}
236
+ >
237
+ Pushed to end
238
+ </Text>
239
+ </FlexRow>
240
+ </div>
241
+ </div>
242
+ </div>
243
+
244
+ <div className="demo-container">
245
+ <h3>7. Text Labels and Content Layout</h3>
246
+ <div className="visual-container">
247
+ <FlexColumn gap="15px">
248
+ <FlexRow gap="10px">
249
+ <Text style={{ minWidth: "80px", fontWeight: "bold" }}>
250
+ Name:
251
+ </Text>
252
+ <Text
253
+ expand
254
+ style={{ background: "#f5f5f5", padding: "8px" }}
255
+ >
256
+ John Doe
257
+ </Text>
258
+ </FlexRow>
259
+
260
+ <FlexRow gap="10px">
261
+ <Text style={{ minWidth: "80px", fontWeight: "bold" }}>
262
+ Email:
263
+ </Text>
264
+ <Text
265
+ expand
266
+ style={{ background: "#f5f5f5", padding: "8px" }}
267
+ >
268
+ john.doe@example.com
269
+ </Text>
270
+ <Button>Edit</Button>
271
+ </FlexRow>
272
+
273
+ <FlexRow gap="10px">
274
+ <Text style={{ minWidth: "80px", fontWeight: "bold" }}>
275
+ Status:
276
+ </Text>
277
+ <Text
278
+ expand
279
+ style={{
280
+ background: "#e8f5e8",
281
+ padding: "8px",
282
+ color: "#2e7d32",
283
+ }}
284
+ >
285
+ Active
286
+ </Text>
287
+ </FlexRow>
288
+ </FlexColumn>
289
+ </div>
290
+ </div>
291
+
292
+ <div className="demo-container">
293
+ <h3>8. Text in Card Layout</h3>
294
+ <div className="visual-container">
295
+ <div
296
+ style={{
297
+ border: "1px solid #ddd",
298
+ borderRadius: "8px",
299
+ padding: "20px",
300
+ width: "350px",
301
+ }}
302
+ >
303
+ <FlexRow gap="15px" style={{ marginBottom: "15px" }}>
304
+ <Text style={{ fontSize: "20px", fontWeight: "bold" }}>
305
+ Article Title
306
+ </Text>
307
+ <Text
308
+ alignX="end"
309
+ style={{ fontSize: "12px", color: "#666" }}
310
+ >
311
+ 2 min read
312
+ </Text>
313
+ </FlexRow>
314
+
315
+ <Text
316
+ style={{
317
+ lineHeight: "1.6",
318
+ color: "#555",
319
+ marginBottom: "15px",
320
+ }}
321
+ >
322
+ This is the article content that can expand and flow
323
+ naturally. It demonstrates how text components work with
324
+ layout props.
325
+ </Text>
326
+
327
+ <FlexRow gap="10px">
328
+ <Text style={{ fontSize: "12px", color: "#999" }}>
329
+ Published: Oct 29, 2025
330
+ </Text>
331
+ <Text
332
+ alignX="end"
333
+ style={{ fontSize: "12px", color: "#666" }}
334
+ >
335
+ By Author Name
336
+ </Text>
337
+ </FlexRow>
338
+ </div>
339
+ </div>
340
+ </div>
341
+
342
+ <div className="demo-container">
343
+ <h3>9. Text Navigation and Breadcrumbs</h3>
344
+ <div className="visual-container">
345
+ <FlexColumn gap="15px">
346
+ <FlexRow gap="10px">
347
+ <Text style={{ fontWeight: "bold" }}>Home</Text>
348
+ <Text>/</Text>
349
+ <Text style={{ fontWeight: "bold" }}>Products</Text>
350
+ <Text>/</Text>
351
+ <Text>Current Page</Text>
352
+ <Text
353
+ alignX="end"
354
+ style={{ fontSize: "12px", color: "#666" }}
355
+ >
356
+ Help
357
+ </Text>
358
+ </FlexRow>
359
+
360
+ <FlexRow gap="15px">
361
+ <Text
362
+ expand
363
+ style={{ fontSize: "18px", fontWeight: "bold" }}
364
+ >
365
+ Page Title
366
+ </Text>
367
+ <Text alignX="end" style={{ fontSize: "12px" }}>
368
+ Last updated: Today
369
+ </Text>
370
+ </FlexRow>
371
+ </FlexColumn>
372
+ </div>
373
+ </div>
374
+
375
+ <div className="demo-container">
376
+ <h3>10. Text with Mixed Alignment</h3>
377
+ <div className="visual-container">
378
+ <FlexColumn gap="10px">
379
+ <FlexRow gap="10px">
380
+ <Text style={{ background: "#e3f2fd", padding: "8px" }}>
381
+ Default (start)
382
+ </Text>
383
+ <Text
384
+ alignX="center"
385
+ style={{ background: "#f3e5f5", padding: "8px" }}
386
+ >
387
+ Centered
388
+ </Text>
389
+ <Text
390
+ alignX="end"
391
+ style={{ background: "#e8f5e8", padding: "8px" }}
392
+ >
393
+ End aligned
394
+ </Text>
395
+ </FlexRow>
396
+
397
+ <FlexRow gap="10px">
398
+ <Text
399
+ expand
400
+ style={{ background: "#fff3e0", padding: "8px" }}
401
+ >
402
+ Expanding text that fills remaining space
403
+ </Text>
404
+ <Text
405
+ alignX="end"
406
+ style={{ background: "#ffebee", padding: "8px" }}
407
+ >
408
+ Fixed end
409
+ </Text>
410
+ </FlexRow>
411
+ </FlexColumn>
412
+ </div>
413
+ </div>
414
+
415
+ <div className="demo-container">
416
+ <h3>11. Text in Message/Chat Layout</h3>
417
+ <div className="visual-container">
418
+ <FlexColumn gap="10px" style={{ width: "400px" }}>
419
+ <FlexRow gap="10px">
420
+ <Text
421
+ style={{
422
+ background: "#e3f2fd",
423
+ padding: "10px",
424
+ borderRadius: "12px",
425
+ maxWidth: "70%",
426
+ }}
427
+ >
428
+ Hello! This is a message from the left side.
429
+ </Text>
430
+ </FlexRow>
431
+
432
+ <FlexRow gap="10px">
433
+ <Text
434
+ alignX="end"
435
+ style={{
436
+ background: "#e8f5e8",
437
+ padding: "10px",
438
+ borderRadius: "12px",
439
+ maxWidth: "70%",
440
+ }}
441
+ >
442
+ This message is aligned to the right side.
443
+ </Text>
444
+ </FlexRow>
445
+
446
+ <FlexRow gap="10px">
447
+ <Text
448
+ style={{
449
+ background: "#fff3e0",
450
+ padding: "10px",
451
+ borderRadius: "12px",
452
+ maxWidth: "70%",
453
+ }}
454
+ >
455
+ Another left message with some longer content to show
456
+ wrapping.
457
+ </Text>
458
+ </FlexRow>
459
+ </FlexColumn>
460
+ </div>
461
+ </div>
462
+
463
+ <div className="demo-container">
464
+ <h3>12. Combined Text Styling and Layout</h3>
465
+ <div className="visual-container">
466
+ <FlexColumn gap="15px">
467
+ <Text bold marginBottom="10px" style={{ fontSize: "24px" }}>
468
+ Main Heading
469
+ </Text>
470
+
471
+ <FlexRow gap="15px">
472
+ <Text italic marginRight="20px" style={{ color: "#666" }}>
473
+ Subtitle
474
+ </Text>
475
+ <Text alignX="end" style={{ fontSize: "12px" }}>
476
+ Metadata
477
+ </Text>
478
+ </FlexRow>
479
+
480
+ <Text
481
+ paddingX="15px"
482
+ paddingY="10px"
483
+ style={{
484
+ background: "#f8f9fa",
485
+ borderLeft: "4px solid #007bff",
486
+ fontStyle: "italic",
487
+ }}
488
+ >
489
+ This is a quote or highlighted text with custom padding and
490
+ styling.
491
+ </Text>
492
+
493
+ <FlexRow gap="10px">
494
+ <Text underline style={{ color: "#007bff" }}>
495
+ Link text
496
+ </Text>
497
+ <Text expand style={{ textAlign: "center" }}>
498
+ Centered expanding content
499
+ </Text>
500
+ <Text bold style={{ color: "#dc3545" }}>
501
+ Important
502
+ </Text>
503
+ </FlexRow>
504
+ </FlexColumn>
505
+ </div>
506
+ </div>
507
+ </div>
508
+ );
509
+ };
510
+
511
+ render(<Demo />, document.getElementById("app"));
512
+ </script>
513
+ </body>
514
+ </html>
@@ -1,22 +1,20 @@
1
- import { createContext } from "preact";
2
1
  import { useContext } from "preact/hooks";
3
2
 
4
3
  import { withPropsClassName } from "../props_composition/with_props_class_name.js";
5
4
  import { withPropsStyle } from "../props_composition/with_props_style.js";
6
- import { consumeSpacingProps } from "./spacing.jsx";
5
+ import { FlexDirectionContext } from "./layout_context.jsx";
6
+ import { useLayoutStyle } from "./use_layout_style.js";
7
7
 
8
8
  import.meta.css = /* css */ `
9
9
  .navi_flex_row {
10
10
  display: flex;
11
11
  flex-direction: row;
12
- align-items: center;
13
12
  gap: 0;
14
13
  }
15
14
 
16
15
  .navi_flex_column {
17
16
  display: flex;
18
17
  flex-direction: column;
19
- align-items: center;
20
18
  gap: 0;
21
19
  }
22
20
 
@@ -25,14 +23,16 @@ import.meta.css = /* css */ `
25
23
  }
26
24
  `;
27
25
 
28
- const FlexDirectionContext = createContext();
29
-
30
- export const FlexRow = ({ alignY, gap, style, children, ...rest }) => {
26
+ export const FlexRow = ({ alignX, alignY, gap, style, children, ...rest }) => {
27
+ const { all } = useLayoutStyle(rest);
31
28
  const innerStyle = withPropsStyle(
32
29
  {
33
- alignItems: alignY,
30
+ ...all,
31
+ // Only set justifyContent if it's not the default "start"
32
+ justifyContent: alignX !== "start" ? alignX : undefined,
33
+ // Only set alignItems if it's not the default "stretch"
34
+ alignItems: alignY !== "stretch" ? alignY : undefined,
34
35
  gap,
35
- ...consumeSpacingProps(rest),
36
36
  },
37
37
  style,
38
38
  );
@@ -45,12 +45,23 @@ export const FlexRow = ({ alignY, gap, style, children, ...rest }) => {
45
45
  </div>
46
46
  );
47
47
  };
48
- export const FlexColumn = ({ alignX, gap, style, children, ...rest }) => {
48
+ export const FlexColumn = ({
49
+ alignX,
50
+ alignY,
51
+ gap,
52
+ style,
53
+ children,
54
+ ...rest
55
+ }) => {
56
+ const { all } = useLayoutStyle(rest);
49
57
  const innerStyle = withPropsStyle(
50
58
  {
51
- alignItems: alignX,
59
+ ...all,
60
+ // Only set alignItems if it's not the default "stretch"
61
+ alignItems: alignX !== "stretch" ? alignX : undefined,
62
+ // Only set justifyContent if it's not the default "start"
63
+ justifyContent: alignY !== "start" ? alignY : undefined,
52
64
  gap,
53
- ...consumeSpacingProps(rest),
54
65
  },
55
66
  style,
56
67
  );
@@ -63,66 +74,11 @@ export const FlexColumn = ({ alignX, gap, style, children, ...rest }) => {
63
74
  </div>
64
75
  );
65
76
  };
66
- export const useConsumAlignProps = (props) => {
67
- const flexDirection = useContext(FlexDirectionContext);
68
77
 
69
- const alignX = props.alignX;
70
- const alignY = props.alignY;
71
- delete props.alignX;
72
- delete props.alignY;
73
-
74
- const style = {};
75
-
76
- if (flexDirection === "row") {
77
- // In row direction: alignX controls justify-content, alignY controls align-self
78
- // Default alignY is "center" from CSS, so only set alignSelf when different
79
- if (alignY !== undefined && alignY !== "center") {
80
- style.alignSelf = alignY;
81
- }
82
- // For row, alignX uses auto margins for positioning
83
- // NOTE: Auto margins only work effectively for positioning individual items.
84
- // When multiple adjacent items have the same auto margin alignment (e.g., alignX="end"),
85
- // only the first item will be positioned as expected because subsequent items
86
- // will be positioned relative to the previous item's margins, not the container edge.
87
- if (alignX !== undefined) {
88
- if (alignX === "start") {
89
- style.marginRight = "auto";
90
- } else if (alignX === "end") {
91
- style.marginLeft = "auto";
92
- } else if (alignX === "center") {
93
- style.marginLeft = "auto";
94
- style.marginRight = "auto";
95
- }
96
- }
97
- } else if (flexDirection === "column") {
98
- // In column direction: alignX controls align-self, alignY uses auto margins
99
- // Default alignX is "center" from CSS, so only set alignSelf when different
100
- if (alignX !== undefined && alignX !== "center") {
101
- style.alignSelf = alignX;
102
- }
103
- // For column, alignY uses auto margins for positioning
104
- // NOTE: Same auto margin limitation applies - multiple adjacent items with
105
- // the same alignY won't all position relative to container edges.
106
- if (alignY !== undefined) {
107
- if (alignY === "start") {
108
- style.marginBottom = "auto";
109
- } else if (alignY === "end") {
110
- style.marginTop = "auto";
111
- } else if (alignY === "center") {
112
- style.marginTop = "auto";
113
- style.marginBottom = "auto";
114
- }
115
- }
116
- }
117
-
118
- return style;
119
- };
120
78
  export const FlexItem = ({
121
- alignX,
122
- alignY,
123
- grow,
124
79
  shrink,
125
80
  className,
81
+ expand,
126
82
  style,
127
83
  children,
128
84
  ...rest
@@ -135,13 +91,12 @@ export const FlexItem = ({
135
91
  }
136
92
 
137
93
  const innerClassName = withPropsClassName("navi_flex_item", className);
138
- const alignStyle = useConsumAlignProps({ alignX, alignY });
94
+ const { all } = useLayoutStyle(rest);
139
95
  const innerStyle = withPropsStyle(
140
96
  {
141
- flexGrow: grow ? 1 : undefined,
97
+ ...all,
98
+ flexGrow: expand ? 1 : undefined,
142
99
  flexShrink: shrink ? 1 : undefined,
143
- ...consumeSpacingProps(rest),
144
- ...alignStyle,
145
100
  },
146
101
  style,
147
102
  );
@@ -0,0 +1,3 @@
1
+ import { createContext } from "preact";
2
+
3
+ export const FlexDirectionContext = createContext();