@jsenv/navi 0.4.1 → 0.5.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.
@@ -17092,16 +17092,9 @@ const createRoute = (urlPatternInput) => {
17092
17092
  if (processedRelativeUrl[0] === "/") {
17093
17093
  processedRelativeUrl = processedRelativeUrl.slice(1);
17094
17094
  }
17095
-
17096
17095
  if (hasRawUrlPartWithInvalidChars) {
17097
- // Manually construct URL to avoid automatic encoding by URL constructor
17098
- const baseUrlObj = new URL(baseUrl);
17099
- if (baseUrlObj.pathname.endsWith("/")) {
17100
- return `${baseUrl}${processedRelativeUrl}`;
17101
- }
17102
17096
  return `${baseUrl}/${processedRelativeUrl}`;
17103
17097
  }
17104
-
17105
17098
  const url = new URL(processedRelativeUrl, baseUrl).href;
17106
17099
  return url;
17107
17100
  };
@@ -21203,11 +21196,15 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
21203
21196
  @layer navi {
21204
21197
  .navi_button {
21205
21198
  position: relative;
21206
- display: inline-block;
21199
+ display: inline-flex;
21200
+ width: fit-content;
21201
+ height: fit-content;
21207
21202
  padding: 0;
21208
21203
  background: none;
21209
21204
  border: none;
21205
+ border-radius: inherit;
21210
21206
  outline: none;
21207
+ cursor: pointer;
21211
21208
 
21212
21209
  --border-width: 1px;
21213
21210
  --outline-width: 1px;
@@ -21301,6 +21298,9 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
21301
21298
  --color: var(--color-readonly);
21302
21299
  }
21303
21300
  /* Disabled */
21301
+ .navi_button[data-disabled] {
21302
+ cursor: default;
21303
+ }
21304
21304
  .navi_button[data-disabled] .navi_button_content {
21305
21305
  --border-color: var(--border-color-disabled);
21306
21306
  --background-color: var(--background-color-disabled);
@@ -21362,6 +21362,8 @@ const ButtonBasic = forwardRef((props, ref) => {
21362
21362
  constraints = [],
21363
21363
  autoFocus,
21364
21364
  appearance = "navi",
21365
+ alignX = "start",
21366
+ style,
21365
21367
  discrete,
21366
21368
  children,
21367
21369
  ...rest
@@ -21382,10 +21384,18 @@ const ButtonBasic = forwardRef((props, ref) => {
21382
21384
  } else {
21383
21385
  buttonChildren = children;
21384
21386
  }
21387
+ const innerStyle = {
21388
+ ...style
21389
+ };
21390
+ if (alignX !== "start") {
21391
+ innerStyle["align-self"] = alignX === "center" ? "center" : "flex-end";
21392
+ }
21385
21393
  return jsx("button", {
21386
21394
  ...rest,
21387
21395
  ref: innerRef,
21388
21396
  className: appearance === "navi" ? "navi_button" : undefined,
21397
+ style: innerStyle,
21398
+ disabled: innerDisabled,
21389
21399
  "data-discrete": discrete ? "" : undefined,
21390
21400
  "data-readonly": innerReadOnly ? "" : undefined,
21391
21401
  "data-readonly-silent": innerLoading ? "" : undefined,
@@ -27643,12 +27653,14 @@ const useSignalSync = (value, initialValue = value) => {
27643
27653
  const FontSizedSvg = ({
27644
27654
  width = "1em",
27645
27655
  height = "1em",
27656
+ style,
27646
27657
  children,
27647
27658
  ...props
27648
27659
  }) => {
27649
27660
  return jsx("span", {
27650
27661
  ...props,
27651
27662
  style: {
27663
+ ...style,
27652
27664
  display: "flex",
27653
27665
  alignItems: "center",
27654
27666
  width,
@@ -27810,6 +27822,63 @@ const Overflow = ({
27810
27822
  });
27811
27823
  };
27812
27824
 
27825
+ installImportMetaCss(import.meta);import.meta.css = /* css */`
27826
+ :root {
27827
+ --navi-icon-align-y: center;
27828
+ }
27829
+
27830
+ .navi_text {
27831
+ display: inline-flex;
27832
+ align-items: baseline;
27833
+ gap: 0.1em;
27834
+ }
27835
+
27836
+ .navi_icon {
27837
+ --align-y: var(--navi-icon-align-y, center);
27838
+
27839
+ display: inline-flex;
27840
+ width: 1em;
27841
+ height: 1em;
27842
+ flex-shrink: 0;
27843
+ align-self: var(--align-y);
27844
+ line-height: 1em;
27845
+ }
27846
+ `;
27847
+ const Text = ({
27848
+ children,
27849
+ ...rest
27850
+ }) => {
27851
+ return jsx("span", {
27852
+ ...rest,
27853
+ className: "navi_text",
27854
+ children: children
27855
+ });
27856
+ };
27857
+ const alignYMapping = {
27858
+ start: "flex-start",
27859
+ center: "center",
27860
+ end: "flex-end"
27861
+ };
27862
+ const Icon = ({
27863
+ alignY,
27864
+ style,
27865
+ children,
27866
+ ...rest
27867
+ }) => {
27868
+ const innerStyle = {
27869
+ ...style
27870
+ };
27871
+ if (alignY !== "center") {
27872
+ innerStyle["--align-y"] = alignYMapping[alignY];
27873
+ }
27874
+ return jsx("span", {
27875
+ ...rest,
27876
+ className: "navi_icon",
27877
+ style: innerStyle,
27878
+ children: children
27879
+ });
27880
+ };
27881
+
27813
27882
  installImportMetaCss(import.meta);import.meta.css = /* css */`
27814
27883
  .text_and_count {
27815
27884
  display: flex;
@@ -27919,4 +27988,4 @@ const useDependenciesDiff = (inputs) => {
27919
27988
  return diffRef.current;
27920
27989
  };
27921
27990
 
27922
- export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, TextAndCount, Thead, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, defineRoutes, enableDebugActions, enableDebugOnDocumentLoading, goBack, goForward, goTo, isCellSelected, isColumnSelected, isRowSelected, openCallout, rawUrlPart, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, valueInLocalStorage };
27991
+ export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, Icon, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, Text, TextAndCount, Thead, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, defineRoutes, enableDebugActions, enableDebugOnDocumentLoading, goBack, goForward, goTo, isCellSelected, isColumnSelected, isRowSelected, openCallout, rawUrlPart, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, valueInLocalStorage };
package/index.js CHANGED
@@ -88,6 +88,7 @@ export { FontSizedSvg } from "./src/components/svg/font_sized_svg.jsx";
88
88
  export { IconAndText } from "./src/components/svg/icon_and_text.jsx";
89
89
  export { SVGMaskOverlay } from "./src/components/svg/svg_mask_overlay.jsx";
90
90
  export { Overflow } from "./src/components/text/overflow.jsx";
91
+ export { Icon, Text } from "./src/components/text/text.jsx";
91
92
  export { TextAndCount } from "./src/components/text/text_and_count.jsx";
92
93
  // Callout, dialogs, ...
93
94
  export { openCallout } from "./src/components/callout/callout.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Library of components including navigation to create frontend applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -40,11 +40,15 @@ import.meta.css = /* css */ `
40
40
  @layer navi {
41
41
  .navi_button {
42
42
  position: relative;
43
- display: inline-block;
43
+ display: inline-flex;
44
+ width: fit-content;
45
+ height: fit-content;
44
46
  padding: 0;
45
47
  background: none;
46
48
  border: none;
49
+ border-radius: inherit;
47
50
  outline: none;
51
+ cursor: pointer;
48
52
 
49
53
  --border-width: 1px;
50
54
  --outline-width: 1px;
@@ -138,6 +142,9 @@ import.meta.css = /* css */ `
138
142
  --color: var(--color-readonly);
139
143
  }
140
144
  /* Disabled */
145
+ .navi_button[data-disabled] {
146
+ cursor: default;
147
+ }
141
148
  .navi_button[data-disabled] .navi_button_content {
142
149
  --border-color: var(--border-color-disabled);
143
150
  --background-color: var(--background-color-disabled);
@@ -200,6 +207,8 @@ const ButtonBasic = forwardRef((props, ref) => {
200
207
  constraints = [],
201
208
  autoFocus,
202
209
  appearance = "navi",
210
+ alignX = "start",
211
+ style,
203
212
  discrete,
204
213
  children,
205
214
  ...rest
@@ -221,11 +230,18 @@ const ButtonBasic = forwardRef((props, ref) => {
221
230
  buttonChildren = children;
222
231
  }
223
232
 
233
+ const innerStyle = { ...style };
234
+ if (alignX !== "start") {
235
+ innerStyle["align-self"] = alignX === "center" ? "center" : "flex-end";
236
+ }
237
+
224
238
  return (
225
239
  <button
226
240
  {...rest}
227
241
  ref={innerRef}
228
242
  className={appearance === "navi" ? "navi_button" : undefined}
243
+ style={innerStyle}
244
+ disabled={innerDisabled}
229
245
  data-discrete={discrete ? "" : undefined}
230
246
  data-readonly={innerReadOnly ? "" : undefined}
231
247
  data-readonly-silent={innerLoading ? "" : undefined}
@@ -23,6 +23,7 @@
23
23
  export const FontSizedSvg = ({
24
24
  width = "1em",
25
25
  height = "1em",
26
+ style,
26
27
  children,
27
28
  ...props
28
29
  }) => {
@@ -30,6 +31,7 @@ export const FontSizedSvg = ({
30
31
  <span
31
32
  {...props}
32
33
  style={{
34
+ ...style,
33
35
  display: "flex",
34
36
  alignItems: "center",
35
37
  width,
@@ -0,0 +1,465 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>Text and Icon Demo</title>
6
+ <script type="importmap">
7
+ {
8
+ "imports": {
9
+ "preact": "https://esm.sh/preact@10.22.0",
10
+ "preact/": "https://esm.sh/preact@10.22.0/"
11
+ }
12
+ }
13
+ </script>
14
+ </head>
15
+ <body>
16
+ <div id="app"></div>
17
+ <script type="module" jsenv-type="module/jsx">
18
+ import { render } from "preact";
19
+ /* eslint-disable no-unused-vars */
20
+ import { Text, Icon } from "../text.jsx";
21
+
22
+ // Sample SVG icons
23
+ const HomeIcon = () => (
24
+ <svg
25
+ viewBox="0 0 24 24"
26
+ width="100%"
27
+ height="100%"
28
+ xmlns="http://www.w3.org/2000/svg"
29
+ >
30
+ <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" fill="currentColor" />
31
+ </svg>
32
+ );
33
+
34
+ const UserIcon = () => (
35
+ <svg
36
+ viewBox="0 0 24 24"
37
+ width="100%"
38
+ height="100%"
39
+ xmlns="http://www.w3.org/2000/svg"
40
+ >
41
+ <path
42
+ d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"
43
+ fill="currentColor"
44
+ />
45
+ </svg>
46
+ );
47
+
48
+ const SettingsIcon = () => (
49
+ <svg
50
+ viewBox="0 0 24 24"
51
+ width="100%"
52
+ height="100%"
53
+ xmlns="http://www.w3.org/2000/svg"
54
+ >
55
+ <path
56
+ d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.82,11.69,4.82,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"
57
+ fill="currentColor"
58
+ />
59
+ </svg>
60
+ );
61
+
62
+ const SearchIcon = () => (
63
+ <svg
64
+ viewBox="0 0 24 24"
65
+ width="100%"
66
+ height="100%"
67
+ xmlns="http://www.w3.org/2000/svg"
68
+ >
69
+ <path
70
+ d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
71
+ fill="currentColor"
72
+ />
73
+ </svg>
74
+ );
75
+
76
+ const StarIcon = () => (
77
+ <svg
78
+ viewBox="0 0 24 24"
79
+ width="100%"
80
+ height="100%"
81
+ xmlns="http://www.w3.org/2000/svg"
82
+ >
83
+ <path
84
+ d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"
85
+ fill="currentColor"
86
+ />
87
+ </svg>
88
+ );
89
+
90
+ const HeartIcon = () => (
91
+ <svg
92
+ viewBox="0 0 24 24"
93
+ width="100%"
94
+ height="100%"
95
+ xmlns="http://www.w3.org/2000/svg"
96
+ >
97
+ <path
98
+ d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"
99
+ fill="currentColor"
100
+ />
101
+ </svg>
102
+ );
103
+
104
+ const Demo = () => {
105
+ return (
106
+ <div
107
+ style={{
108
+ padding: "20px",
109
+ fontFamily: "system-ui, sans-serif",
110
+ lineHeight: "1.6",
111
+ maxWidth: "800px",
112
+ }}
113
+ >
114
+ <h1>Text and Icon Combinations</h1>
115
+
116
+ <section style={{ marginBottom: "40px" }}>
117
+ <h2>Basic Icon + Text</h2>
118
+ <div
119
+ style={{
120
+ display: "flex",
121
+ flexDirection: "column",
122
+ gap: "10px",
123
+ }}
124
+ >
125
+ <Text>
126
+ <Icon>
127
+ <HomeIcon />
128
+ </Icon>
129
+ Home
130
+ </Text>
131
+
132
+ <Text>
133
+ <Icon>
134
+ <UserIcon />
135
+ </Icon>
136
+ Profile
137
+ </Text>
138
+
139
+ <Text>
140
+ <Icon>
141
+ <SettingsIcon />
142
+ </Icon>
143
+ Settings
144
+ </Text>
145
+ </div>
146
+ </section>
147
+
148
+ <section style={{ marginBottom: "40px" }}>
149
+ <h2>Text + Icon (Reversed Order)</h2>
150
+ <div
151
+ style={{
152
+ display: "flex",
153
+ flexDirection: "column",
154
+ gap: "10px",
155
+ }}
156
+ >
157
+ <Text>
158
+ Search
159
+ <Icon>
160
+ <SearchIcon />
161
+ </Icon>
162
+ </Text>
163
+
164
+ <Text>
165
+ Favorites
166
+ <Icon>
167
+ <StarIcon />
168
+ </Icon>
169
+ </Text>
170
+
171
+ <Text>
172
+ Liked
173
+ <Icon>
174
+ <HeartIcon />
175
+ </Icon>
176
+ </Text>
177
+ </div>
178
+ </section>
179
+
180
+ <section style={{ marginBottom: "40px" }}>
181
+ <h2>Multiple Icons</h2>
182
+ <div
183
+ style={{
184
+ display: "flex",
185
+ flexDirection: "column",
186
+ gap: "10px",
187
+ }}
188
+ >
189
+ <Text>
190
+ <Icon>
191
+ <HomeIcon />
192
+ </Icon>
193
+ Left and right
194
+ <Icon>
195
+ <StarIcon />
196
+ </Icon>
197
+ </Text>
198
+
199
+ <Text>
200
+ <Icon>
201
+ <UserIcon />
202
+ </Icon>
203
+ <Icon>
204
+ <SettingsIcon />
205
+ </Icon>
206
+ Two on the left
207
+ </Text>
208
+ </div>
209
+ </section>
210
+
211
+ <section style={{ marginBottom: "40px" }}>
212
+ <h2>Different Font Sizes</h2>
213
+ <div
214
+ style={{
215
+ display: "flex",
216
+ flexDirection: "column",
217
+ gap: "15px",
218
+ }}
219
+ >
220
+ <Text style={{ fontSize: "12px" }}>
221
+ <Icon>
222
+ <HomeIcon />
223
+ </Icon>
224
+ Small text with icon
225
+ </Text>
226
+
227
+ <Text style={{ fontSize: "16px" }}>
228
+ <Icon>
229
+ <UserIcon />
230
+ </Icon>
231
+ Normal text with icon
232
+ </Text>
233
+
234
+ <Text style={{ fontSize: "24px" }}>
235
+ <Icon>
236
+ <SettingsIcon />
237
+ </Icon>
238
+ Large text with icon
239
+ </Text>
240
+
241
+ <Text style={{ fontSize: "32px" }}>
242
+ <Icon>
243
+ <StarIcon />
244
+ </Icon>
245
+ Extra large text
246
+ </Text>
247
+ </div>
248
+ </section>
249
+
250
+ <section style={{ marginBottom: "40px" }}>
251
+ <h2>Colored Examples</h2>
252
+ <div
253
+ style={{
254
+ display: "flex",
255
+ flexDirection: "column",
256
+ gap: "10px",
257
+ }}
258
+ >
259
+ <Text style={{ color: "blue" }}>
260
+ <Icon>
261
+ <HomeIcon />
262
+ </Icon>
263
+ Blue home link
264
+ </Text>
265
+
266
+ <Text style={{ color: "green" }}>
267
+ <Icon>
268
+ <UserIcon />
269
+ </Icon>
270
+ Green user profile
271
+ </Text>
272
+
273
+ <Text style={{ color: "red" }}>
274
+ <Icon>
275
+ <HeartIcon />
276
+ </Icon>
277
+ Red heart favorite
278
+ </Text>
279
+
280
+ <Text style={{ color: "purple" }}>
281
+ <Icon>
282
+ <StarIcon />
283
+ </Icon>
284
+ Purple star rating
285
+ </Text>
286
+ </div>
287
+ </section>
288
+
289
+ <section style={{ marginBottom: "40px" }}>
290
+ <h2>Interactive Buttons</h2>
291
+ <div
292
+ style={{
293
+ display: "flex",
294
+ flexDirection: "column",
295
+ gap: "10px",
296
+ }}
297
+ >
298
+ <button
299
+ style={{
300
+ padding: "10px 15px",
301
+ border: "1px solid #ccc",
302
+ borderRadius: "4px",
303
+ background: "white",
304
+ cursor: "pointer",
305
+ fontSize: "14px",
306
+ }}
307
+ >
308
+ <Text>
309
+ <Icon>
310
+ <SearchIcon />
311
+ </Icon>
312
+ Search Files
313
+ </Text>
314
+ </button>
315
+
316
+ <button
317
+ style={{
318
+ padding: "10px 15px",
319
+ border: "none",
320
+ borderRadius: "4px",
321
+ background: "#007bff",
322
+ color: "white",
323
+ cursor: "pointer",
324
+ fontSize: "14px",
325
+ }}
326
+ >
327
+ <Text>
328
+ <Icon>
329
+ <UserIcon />
330
+ </Icon>
331
+ Create Account
332
+ </Text>
333
+ </button>
334
+
335
+ <button
336
+ style={{
337
+ padding: "10px 15px",
338
+ border: "1px solid #dc3545",
339
+ borderRadius: "4px",
340
+ background: "transparent",
341
+ color: "#dc3545",
342
+ cursor: "pointer",
343
+ fontSize: "14px",
344
+ }}
345
+ >
346
+ <Text>
347
+ <Icon>
348
+ <HeartIcon />
349
+ </Icon>
350
+ Add to Favorites
351
+ </Text>
352
+ </button>
353
+ </div>
354
+ </section>
355
+
356
+ <section style={{ marginBottom: "40px" }}>
357
+ <h2>Navigation Menu Style</h2>
358
+ <nav
359
+ style={{
360
+ background: "#f8f9fa",
361
+ padding: "20px",
362
+ borderRadius: "8px",
363
+ }}
364
+ >
365
+ <div
366
+ style={{
367
+ display: "flex",
368
+ flexDirection: "column",
369
+ gap: "15px",
370
+ }}
371
+ >
372
+ <Text
373
+ style={{
374
+ cursor: "pointer",
375
+ padding: "8px 12px",
376
+ borderRadius: "4px",
377
+ transition: "background-color 0.2s",
378
+ }}
379
+ >
380
+ <Icon>
381
+ <HomeIcon />
382
+ </Icon>
383
+ Dashboard
384
+ </Text>
385
+
386
+ <Text
387
+ style={{
388
+ cursor: "pointer",
389
+ padding: "8px 12px",
390
+ borderRadius: "4px",
391
+ background: "#007bff",
392
+ color: "white",
393
+ }}
394
+ >
395
+ <Icon>
396
+ <UserIcon />
397
+ </Icon>
398
+ Users
399
+ </Text>
400
+
401
+ <Text
402
+ style={{
403
+ cursor: "pointer",
404
+ padding: "8px 12px",
405
+ borderRadius: "4px",
406
+ }}
407
+ >
408
+ <Icon>
409
+ <SettingsIcon />
410
+ </Icon>
411
+ Settings
412
+ </Text>
413
+ </div>
414
+ </nav>
415
+ </section>
416
+
417
+ <section>
418
+ <h2>Inline Text with Icons</h2>
419
+ <p style={{ fontSize: "16px", lineHeight: "1.6" }}>
420
+ Welcome to our platform! Click on the{" "}
421
+ <Text>
422
+ <Icon>
423
+ <HomeIcon />
424
+ </Icon>
425
+ home
426
+ </Text>{" "}
427
+ button to return to the dashboard, or visit your{" "}
428
+ <Text>
429
+ <Icon>
430
+ <UserIcon />
431
+ </Icon>
432
+ profile
433
+ </Text>{" "}
434
+ to update your settings. Don't forget to{" "}
435
+ <Text>
436
+ <Icon>
437
+ <StarIcon />
438
+ </Icon>
439
+ star
440
+ </Text>{" "}
441
+ your favorite items and{" "}
442
+ <Text>
443
+ <Icon>
444
+ <HeartIcon />
445
+ </Icon>
446
+ like
447
+ </Text>{" "}
448
+ the content you enjoy. Use the{" "}
449
+ <Text>
450
+ <Icon>
451
+ <SearchIcon />
452
+ </Icon>
453
+ search
454
+ </Text>{" "}
455
+ feature to find what you're looking for quickly.
456
+ </p>
457
+ </section>
458
+ </div>
459
+ );
460
+ };
461
+
462
+ render(<Demo />, document.getElementById("app"));
463
+ </script>
464
+ </body>
465
+ </html>
@@ -0,0 +1,48 @@
1
+ import.meta.css = /* css */ `
2
+ :root {
3
+ --navi-icon-align-y: center;
4
+ }
5
+
6
+ .navi_text {
7
+ display: inline-flex;
8
+ align-items: baseline;
9
+ gap: 0.1em;
10
+ }
11
+
12
+ .navi_icon {
13
+ --align-y: var(--navi-icon-align-y, center);
14
+
15
+ display: inline-flex;
16
+ width: 1em;
17
+ height: 1em;
18
+ flex-shrink: 0;
19
+ align-self: var(--align-y);
20
+ line-height: 1em;
21
+ }
22
+ `;
23
+
24
+ export const Text = ({ children, ...rest }) => {
25
+ return (
26
+ <span {...rest} className="navi_text">
27
+ {children}
28
+ </span>
29
+ );
30
+ };
31
+
32
+ const alignYMapping = {
33
+ start: "flex-start",
34
+ center: "center",
35
+ end: "flex-end",
36
+ };
37
+ export const Icon = ({ alignY, style, children, ...rest }) => {
38
+ const innerStyle = { ...style };
39
+ if (alignY !== "center") {
40
+ innerStyle["--align-y"] = alignYMapping[alignY];
41
+ }
42
+
43
+ return (
44
+ <span {...rest} className="navi_icon" style={innerStyle}>
45
+ {children}
46
+ </span>
47
+ );
48
+ };
@@ -359,16 +359,9 @@ const createRoute = (urlPatternInput) => {
359
359
  if (processedRelativeUrl[0] === "/") {
360
360
  processedRelativeUrl = processedRelativeUrl.slice(1);
361
361
  }
362
-
363
362
  if (hasRawUrlPartWithInvalidChars) {
364
- // Manually construct URL to avoid automatic encoding by URL constructor
365
- const baseUrlObj = new URL(baseUrl);
366
- if (baseUrlObj.pathname.endsWith("/")) {
367
- return `${baseUrl}${processedRelativeUrl}`;
368
- }
369
363
  return `${baseUrl}/${processedRelativeUrl}`;
370
364
  }
371
-
372
365
  const url = new URL(processedRelativeUrl, baseUrl).href;
373
366
  return url;
374
367
  };