godown 3.0.0-canary.1 → 3.0.0-canary.11

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.
Files changed (323) hide show
  1. package/README.md +38 -9
  2. package/components/alert.d.ts +6 -3
  3. package/components/alert.d.ts.map +1 -1
  4. package/components/alert.js +22 -14
  5. package/components/alert.js.map +1 -1
  6. package/components/avatar.d.ts +7 -2
  7. package/components/avatar.d.ts.map +1 -1
  8. package/components/avatar.js +13 -6
  9. package/components/avatar.js.map +1 -1
  10. package/components/breath.d.ts +4 -2
  11. package/components/breath.d.ts.map +1 -1
  12. package/components/breath.js +8 -6
  13. package/components/breath.js.map +1 -1
  14. package/components/button.d.ts +20 -11
  15. package/components/button.d.ts.map +1 -1
  16. package/components/button.js +23 -20
  17. package/components/button.js.map +1 -1
  18. package/components/card.d.ts +2 -2
  19. package/components/card.d.ts.map +1 -1
  20. package/components/card.js +8 -13
  21. package/components/card.js.map +1 -1
  22. package/components/carousel.d.ts +8 -5
  23. package/components/carousel.d.ts.map +1 -1
  24. package/components/carousel.js +18 -11
  25. package/components/carousel.js.map +1 -1
  26. package/components/details.d.ts +7 -3
  27. package/components/details.d.ts.map +1 -1
  28. package/components/details.js +7 -3
  29. package/components/details.js.map +1 -1
  30. package/components/dialog.d.ts +9 -5
  31. package/components/dialog.d.ts.map +1 -1
  32. package/components/dialog.js +20 -14
  33. package/components/dialog.js.map +1 -1
  34. package/components/divider.d.ts +3 -4
  35. package/components/divider.d.ts.map +1 -1
  36. package/components/divider.js +6 -7
  37. package/components/divider.js.map +1 -1
  38. package/components/dragbox.d.ts +9 -8
  39. package/components/dragbox.d.ts.map +1 -1
  40. package/components/dragbox.js +3 -2
  41. package/components/dragbox.js.map +1 -1
  42. package/components/flex.d.ts +3 -1
  43. package/components/flex.d.ts.map +1 -1
  44. package/components/flex.js +13 -4
  45. package/components/flex.js.map +1 -1
  46. package/components/form.d.ts +2 -3
  47. package/components/form.d.ts.map +1 -1
  48. package/components/form.js +0 -2
  49. package/components/form.js.map +1 -1
  50. package/components/grid.d.ts +9 -3
  51. package/components/grid.d.ts.map +1 -1
  52. package/components/grid.js +6 -4
  53. package/components/grid.js.map +1 -1
  54. package/components/input.d.ts +4 -4
  55. package/components/input.d.ts.map +1 -1
  56. package/components/input.js +5 -7
  57. package/components/input.js.map +1 -1
  58. package/components/layout.d.ts +4 -6
  59. package/components/layout.d.ts.map +1 -1
  60. package/components/layout.js +23 -12
  61. package/components/layout.js.map +1 -1
  62. package/components/link.d.ts +3 -1
  63. package/components/link.d.ts.map +1 -1
  64. package/components/link.js +3 -1
  65. package/components/link.js.map +1 -1
  66. package/components/progress.d.ts +4 -12
  67. package/components/progress.d.ts.map +1 -1
  68. package/components/progress.js +9 -17
  69. package/components/progress.js.map +1 -1
  70. package/components/range.d.ts +45 -18
  71. package/components/range.d.ts.map +1 -1
  72. package/components/range.js +133 -62
  73. package/components/range.js.map +1 -1
  74. package/components/rotate.d.ts +3 -2
  75. package/components/rotate.d.ts.map +1 -1
  76. package/components/rotate.js +1 -1
  77. package/components/rotate.js.map +1 -1
  78. package/components/router.d.ts +15 -17
  79. package/components/router.d.ts.map +1 -1
  80. package/components/router.js +10 -10
  81. package/components/router.js.map +1 -1
  82. package/components/select.d.ts +6 -9
  83. package/components/select.d.ts.map +1 -1
  84. package/components/select.js +48 -43
  85. package/components/select.js.map +1 -1
  86. package/components/skeleton.d.ts +2 -1
  87. package/components/skeleton.d.ts.map +1 -1
  88. package/components/skeleton.js +5 -5
  89. package/components/skeleton.js.map +1 -1
  90. package/components/split.d.ts +20 -2
  91. package/components/split.d.ts.map +1 -1
  92. package/components/split.js +54 -20
  93. package/components/split.js.map +1 -1
  94. package/components/switch.d.ts +6 -5
  95. package/components/switch.d.ts.map +1 -1
  96. package/components/switch.js +7 -6
  97. package/components/switch.js.map +1 -1
  98. package/components/text.d.ts +2 -1
  99. package/components/text.d.ts.map +1 -1
  100. package/components/text.js +5 -6
  101. package/components/text.js.map +1 -1
  102. package/components/time.d.ts +6 -22
  103. package/components/time.d.ts.map +1 -1
  104. package/components/time.js +7 -67
  105. package/components/time.js.map +1 -1
  106. package/components/tooltip.d.ts +13 -2
  107. package/components/tooltip.d.ts.map +1 -1
  108. package/components/tooltip.js +24 -5
  109. package/components/tooltip.js.map +1 -1
  110. package/components/typewriter.d.ts +4 -4
  111. package/components/typewriter.d.ts.map +1 -1
  112. package/components/typewriter.js +4 -5
  113. package/components/typewriter.js.map +1 -1
  114. package/core/global-style.d.ts.map +1 -1
  115. package/core/global-style.js +9 -2
  116. package/core/global-style.js.map +1 -1
  117. package/core/super-anchor.d.ts +2 -1
  118. package/core/super-anchor.d.ts.map +1 -1
  119. package/core/super-anchor.js +4 -3
  120. package/core/super-anchor.js.map +1 -1
  121. package/core/super-input.d.ts +5 -5
  122. package/core/super-input.d.ts.map +1 -1
  123. package/core/super-input.js +3 -6
  124. package/core/super-input.js.map +1 -1
  125. package/core/super-openable.d.ts.map +1 -1
  126. package/core/super-openable.js +1 -1
  127. package/core/super-openable.js.map +1 -1
  128. package/custom-elements.json +1 -1
  129. package/dev/components/alert.d.ts +6 -3
  130. package/dev/components/alert.d.ts.map +1 -1
  131. package/dev/components/alert.js +38 -25
  132. package/dev/components/alert.js.map +1 -1
  133. package/dev/components/avatar.d.ts +7 -2
  134. package/dev/components/avatar.d.ts.map +1 -1
  135. package/dev/components/avatar.js +28 -13
  136. package/dev/components/avatar.js.map +1 -1
  137. package/dev/components/breath.d.ts +4 -2
  138. package/dev/components/breath.d.ts.map +1 -1
  139. package/dev/components/breath.js +20 -6
  140. package/dev/components/breath.js.map +1 -1
  141. package/dev/components/button.d.ts +20 -11
  142. package/dev/components/button.d.ts.map +1 -1
  143. package/dev/components/button.js +34 -25
  144. package/dev/components/button.js.map +1 -1
  145. package/dev/components/card.d.ts +2 -2
  146. package/dev/components/card.d.ts.map +1 -1
  147. package/dev/components/card.js +25 -30
  148. package/dev/components/card.js.map +1 -1
  149. package/dev/components/carousel.d.ts +8 -5
  150. package/dev/components/carousel.d.ts.map +1 -1
  151. package/dev/components/carousel.js +35 -20
  152. package/dev/components/carousel.js.map +1 -1
  153. package/dev/components/details.d.ts +7 -3
  154. package/dev/components/details.d.ts.map +1 -1
  155. package/dev/components/details.js +12 -4
  156. package/dev/components/details.js.map +1 -1
  157. package/dev/components/dialog.d.ts +9 -5
  158. package/dev/components/dialog.d.ts.map +1 -1
  159. package/dev/components/dialog.js +30 -24
  160. package/dev/components/dialog.js.map +1 -1
  161. package/dev/components/divider.d.ts +3 -4
  162. package/dev/components/divider.d.ts.map +1 -1
  163. package/dev/components/divider.js +20 -8
  164. package/dev/components/divider.js.map +1 -1
  165. package/dev/components/dragbox.d.ts +9 -8
  166. package/dev/components/dragbox.d.ts.map +1 -1
  167. package/dev/components/dragbox.js +8 -1
  168. package/dev/components/dragbox.js.map +1 -1
  169. package/dev/components/flex.d.ts +3 -1
  170. package/dev/components/flex.d.ts.map +1 -1
  171. package/dev/components/flex.js +24 -4
  172. package/dev/components/flex.js.map +1 -1
  173. package/dev/components/form.d.ts +2 -3
  174. package/dev/components/form.d.ts.map +1 -1
  175. package/dev/components/form.js +0 -2
  176. package/dev/components/form.js.map +1 -1
  177. package/dev/components/grid.d.ts +9 -3
  178. package/dev/components/grid.d.ts.map +1 -1
  179. package/dev/components/grid.js +17 -4
  180. package/dev/components/grid.js.map +1 -1
  181. package/dev/components/input.d.ts +4 -4
  182. package/dev/components/input.d.ts.map +1 -1
  183. package/dev/components/input.js +3 -21
  184. package/dev/components/input.js.map +1 -1
  185. package/dev/components/layout.d.ts +4 -6
  186. package/dev/components/layout.d.ts.map +1 -1
  187. package/dev/components/layout.js +33 -13
  188. package/dev/components/layout.js.map +1 -1
  189. package/dev/components/link.d.ts +3 -1
  190. package/dev/components/link.d.ts.map +1 -1
  191. package/dev/components/link.js +3 -1
  192. package/dev/components/link.js.map +1 -1
  193. package/dev/components/progress.d.ts +4 -12
  194. package/dev/components/progress.d.ts.map +1 -1
  195. package/dev/components/progress.js +14 -18
  196. package/dev/components/progress.js.map +1 -1
  197. package/dev/components/range.d.ts +45 -18
  198. package/dev/components/range.d.ts.map +1 -1
  199. package/dev/components/range.js +164 -109
  200. package/dev/components/range.js.map +1 -1
  201. package/dev/components/rotate.d.ts +3 -2
  202. package/dev/components/rotate.d.ts.map +1 -1
  203. package/dev/components/rotate.js +1 -1
  204. package/dev/components/rotate.js.map +1 -1
  205. package/dev/components/router.d.ts +15 -17
  206. package/dev/components/router.d.ts.map +1 -1
  207. package/dev/components/router.js +10 -10
  208. package/dev/components/router.js.map +1 -1
  209. package/dev/components/select.d.ts +6 -9
  210. package/dev/components/select.d.ts.map +1 -1
  211. package/dev/components/select.js +58 -45
  212. package/dev/components/select.js.map +1 -1
  213. package/dev/components/skeleton.d.ts +2 -1
  214. package/dev/components/skeleton.d.ts.map +1 -1
  215. package/dev/components/skeleton.js +6 -6
  216. package/dev/components/skeleton.js.map +1 -1
  217. package/dev/components/split.d.ts +20 -2
  218. package/dev/components/split.d.ts.map +1 -1
  219. package/dev/components/split.js +69 -32
  220. package/dev/components/split.js.map +1 -1
  221. package/dev/components/switch.d.ts +6 -5
  222. package/dev/components/switch.d.ts.map +1 -1
  223. package/dev/components/switch.js +23 -20
  224. package/dev/components/switch.js.map +1 -1
  225. package/dev/components/text.d.ts +2 -1
  226. package/dev/components/text.d.ts.map +1 -1
  227. package/dev/components/text.js +7 -7
  228. package/dev/components/text.js.map +1 -1
  229. package/dev/components/time.d.ts +6 -22
  230. package/dev/components/time.d.ts.map +1 -1
  231. package/dev/components/time.js +9 -67
  232. package/dev/components/time.js.map +1 -1
  233. package/dev/components/tooltip.d.ts +13 -2
  234. package/dev/components/tooltip.d.ts.map +1 -1
  235. package/dev/components/tooltip.js +55 -20
  236. package/dev/components/tooltip.js.map +1 -1
  237. package/dev/components/typewriter.d.ts +4 -4
  238. package/dev/components/typewriter.d.ts.map +1 -1
  239. package/dev/components/typewriter.js +11 -5
  240. package/dev/components/typewriter.js.map +1 -1
  241. package/dev/core/global-style.d.ts.map +1 -1
  242. package/dev/core/global-style.js +18 -10
  243. package/dev/core/global-style.js.map +1 -1
  244. package/dev/core/super-anchor.d.ts +2 -1
  245. package/dev/core/super-anchor.d.ts.map +1 -1
  246. package/dev/core/super-anchor.js +8 -5
  247. package/dev/core/super-anchor.js.map +1 -1
  248. package/dev/core/super-input.d.ts +5 -5
  249. package/dev/core/super-input.d.ts.map +1 -1
  250. package/dev/core/super-input.js +21 -20
  251. package/dev/core/super-input.js.map +1 -1
  252. package/dev/core/super-openable.d.ts.map +1 -1
  253. package/dev/core/super-openable.js +1 -1
  254. package/dev/core/super-openable.js.map +1 -1
  255. package/dev/range.d.ts +1 -1
  256. package/dev/range.d.ts.map +1 -1
  257. package/index.js +13 -13
  258. package/package.json +11 -8
  259. package/range.d.ts +1 -1
  260. package/range.d.ts.map +1 -1
  261. package/src/alert.ts +11 -0
  262. package/src/avatar.ts +11 -0
  263. package/src/breath.ts +13 -0
  264. package/src/button.ts +11 -0
  265. package/src/card.ts +11 -0
  266. package/src/carousel.ts +11 -0
  267. package/src/components/alert.ts +284 -0
  268. package/src/components/avatar.ts +109 -0
  269. package/src/components/breath.ts +165 -0
  270. package/src/components/button.ts +292 -0
  271. package/src/components/card.ts +83 -0
  272. package/src/components/carousel.ts +183 -0
  273. package/src/components/details.ts +121 -0
  274. package/src/components/dialog.ts +166 -0
  275. package/src/components/divider.ts +56 -0
  276. package/src/components/dragbox.ts +134 -0
  277. package/src/components/flex.ts +82 -0
  278. package/src/components/form.ts +82 -0
  279. package/src/components/grid.ts +87 -0
  280. package/src/components/input.ts +73 -0
  281. package/src/components/layout.ts +89 -0
  282. package/src/components/link.ts +38 -0
  283. package/src/components/progress.ts +100 -0
  284. package/src/components/range.ts +399 -0
  285. package/src/components/rotate.ts +95 -0
  286. package/src/components/router.ts +281 -0
  287. package/src/components/select.ts +281 -0
  288. package/src/components/skeleton.ts +119 -0
  289. package/src/components/split.ts +225 -0
  290. package/src/components/switch.ts +184 -0
  291. package/src/components/text.ts +93 -0
  292. package/src/components/time.ts +84 -0
  293. package/src/components/tooltip.ts +150 -0
  294. package/src/components/typewriter.ts +159 -0
  295. package/src/core/global-style.ts +105 -0
  296. package/src/core/super-anchor.ts +55 -0
  297. package/src/core/super-input.ts +230 -0
  298. package/src/core/super-openable.ts +51 -0
  299. package/src/details.ts +11 -0
  300. package/src/dialog.ts +11 -0
  301. package/src/divider.ts +11 -0
  302. package/src/dragbox.ts +11 -0
  303. package/src/flex.ts +11 -0
  304. package/src/form.ts +11 -0
  305. package/src/grid.ts +11 -0
  306. package/src/index.ts +28 -0
  307. package/src/input.ts +13 -0
  308. package/src/layout.ts +12 -0
  309. package/src/link.ts +13 -0
  310. package/src/progress.ts +12 -0
  311. package/src/range.ts +13 -0
  312. package/src/rotate.ts +13 -0
  313. package/src/router.ts +12 -0
  314. package/src/select.ts +13 -0
  315. package/src/skeleton.ts +13 -0
  316. package/src/split.ts +13 -0
  317. package/src/switch.ts +13 -0
  318. package/src/text.ts +13 -0
  319. package/src/time.ts +13 -0
  320. package/src/tooltip.ts +13 -0
  321. package/src/typewriter.ts +11 -0
  322. package/vscode.html-custom-data.json +1 -1
  323. package/web-types.json +1 -1
@@ -0,0 +1,281 @@
1
+ import { godown } from "@godown/element/decorators/godown.js";
2
+ import { styles } from "@godown/element/decorators/styles.js";
3
+ import { htmlSlot } from "@godown/element/directives/html-slot.js";
4
+ import { RouteTree } from "@godown/element/tools/route-tree.js";
5
+ import { css, type PropertyValueMap, type TemplateResult } from "lit";
6
+ import { property, state } from "lit/decorators.js";
7
+
8
+ import { GlobalStyle } from "../core/global-style.js";
9
+
10
+ const protoName = "router";
11
+
12
+ /**
13
+ * {@linkcode Router} has basic routing control.
14
+ *
15
+ * To switch routes, use `router-link component`.
16
+ *
17
+ * It has two methods to collect routes.
18
+ *
19
+ * 1. From field `routes`, an array, each elements require "path" and "component".
20
+ * 2. From child elements, which have the slot attribute for matching routes.
21
+ *
22
+ * If only the method 1 is used, set `type` to `"field"`.
23
+ *
24
+ * If only the method 2 is used, set `type` to `"slotted"`.
25
+ *
26
+ * `type` defaults to `"united"`, which will try method 1, then method 2.
27
+ *
28
+ * If no routes are matched, the default value (no named slot) will be rendered.
29
+ *
30
+ * @slot - Display slot when there is no match.
31
+ * @slot * - Matching slot will be displayed.
32
+ * @category navigation
33
+ */
34
+ @godown(protoName)
35
+ @styles(css`:host{display:contents;}`)
36
+ class Router extends GlobalStyle {
37
+ static routerInstances: Set<Router> = new Set<Router>();
38
+
39
+ private __fieldRouteTree: RouteTree = new RouteTree();
40
+ private __slottedRouteTree: RouteTree = new RouteTree();
41
+ private __cacheRecord = new Map<string, ReturnType<typeof this.useRouter>>();
42
+ private __routes: (
43
+ & Record<string, any>
44
+ & {
45
+ path: string;
46
+ component?: unknown;
47
+ }
48
+ )[];
49
+
50
+ /**
51
+ * Render result.
52
+ */
53
+ @state()
54
+ component: unknown | TemplateResult = null;
55
+
56
+ /**
57
+ * Dynamic parameters record.
58
+ */
59
+ @state()
60
+ params: Record<string, string> = {};
61
+
62
+ /**
63
+ * Value of matched path in routes.
64
+ */
65
+ @state()
66
+ path: string;
67
+
68
+ /**
69
+ * Current pathname (equals to location.pathname).
70
+ */
71
+ @property()
72
+ pathname = "";
73
+
74
+ /**
75
+ * Path prefix.
76
+ */
77
+ @property()
78
+ baseURL = "";
79
+
80
+ /**
81
+ * Rendered content when there is no match.
82
+ */
83
+ @state()
84
+ default: TemplateResult = htmlSlot();
85
+
86
+ /**
87
+ * The type of routing sources.
88
+ *
89
+ * If field, it won't collect the slot attribute of the child elements.
90
+ *
91
+ * This property should not be changed after the rendering is complete.
92
+ */
93
+ @property()
94
+ type: "united" | "slotted" | "field" = "united";
95
+
96
+ /**
97
+ * Cache accessed records.
98
+ *
99
+ * Emptied at each re-collection.
100
+ */
101
+ @property({ type: Boolean })
102
+ cache = false;
103
+
104
+ @state()
105
+ set routes(value) {
106
+ this.__routes = value;
107
+ this.collectFieldRoutes(value);
108
+ }
109
+
110
+ get routes(): (Record<string, any> & {
111
+ path: string;
112
+ component?: unknown;
113
+ })[] {
114
+ return this.__routes;
115
+ }
116
+
117
+ clear(): void {
118
+ this.__cacheRecord.clear();
119
+ }
120
+
121
+ protected render(): unknown {
122
+ this.params = {};
123
+ if (this.cache) {
124
+ const cached = this.__cacheRecord.get(this.pathname);
125
+ if (cached) {
126
+ Object.assign(this, cached);
127
+ return this.component;
128
+ }
129
+ }
130
+ switch (this.type) {
131
+ case "field":
132
+ this.component = this.fieldComponent();
133
+ break;
134
+ case "slotted":
135
+ this.component = this.slottedComponent();
136
+ break;
137
+ default:
138
+ this.component = this.fieldComponent() ?? this.slottedComponent();
139
+ }
140
+ return this.component ?? this.default ?? null;
141
+ }
142
+
143
+ connectedCallback(): void {
144
+ super.connectedCallback();
145
+ Router.routerInstances.add(this);
146
+ this.pathname ??= location.pathname;
147
+
148
+ if (this.type !== "field") {
149
+ const mutationObserver = new MutationObserver(this.collectSlottedRoutes);
150
+ mutationObserver.observe(this, {
151
+ attributeFilter: ["slot"],
152
+ attributes: true,
153
+ subtree: true,
154
+ });
155
+ this.collectSlottedRoutes();
156
+ }
157
+ }
158
+
159
+ disconnectedCallback(): void {
160
+ super.disconnectedCallback();
161
+ Router.routerInstances.delete(this);
162
+ }
163
+
164
+ useRouter(): {
165
+ pathname: string;
166
+ params: Record<string, string>;
167
+ path: string;
168
+ component: unknown;
169
+ } {
170
+ return {
171
+ pathname: this.pathname,
172
+ params: this.params,
173
+ path: this.path,
174
+ component: this.component,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * Callback function when the route changes.
180
+ */
181
+ routeChangeCallback: (params: {
182
+ pathname: string;
183
+ params: Record<string, string>;
184
+ path: string;
185
+ component: unknown | TemplateResult;
186
+ }, first: boolean) => void = null;
187
+
188
+ protected updated(changedProperties: PropertyValueMap<this>): void {
189
+ const shouldDispatch = changedProperties.has("pathname") || changedProperties.has("path");
190
+ if (shouldDispatch) {
191
+ const ur = this.useRouter();
192
+ const noRecord = !this.__cacheRecord.has(this.pathname);
193
+ if (noRecord) {
194
+ this.__cacheRecord.set(this.pathname, ur);
195
+ }
196
+ this.routeChangeCallback?.(ur, noRecord);
197
+ this.dispatchEvent(new CustomEvent("change", { detail: ur }));
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Get component from {@linkcode routes} by query.
203
+ */
204
+ fieldComponent(query?: string): unknown {
205
+ query ||= this.useWhich(this.pathname);
206
+ this.path = query;
207
+
208
+ if (!query) {
209
+ return null;
210
+ }
211
+
212
+ this.params = this.parseParams(this.path, this.pathname);
213
+ const route = this.routes.find((r) => r.path === query);
214
+ if (!route) {
215
+ return null;
216
+ }
217
+ return route.component;
218
+ }
219
+
220
+ /**
221
+ * Get component from slotted elements by query.
222
+ */
223
+ slottedComponent(usedRouteTemplate?: string): TemplateResult<1> {
224
+ const slottedPaths = this._slottedNames;
225
+ usedRouteTemplate ||= this.__slottedRouteTree.useWhich(this.pathname);
226
+ this.path = usedRouteTemplate;
227
+
228
+ if (!usedRouteTemplate) {
229
+ return null;
230
+ }
231
+
232
+ this.path = slottedPaths.find((s) => s === usedRouteTemplate);
233
+ if (!this.path) {
234
+ return null;
235
+ }
236
+ this.params = this.parseParams(usedRouteTemplate, this.pathname);
237
+ return htmlSlot(this.path);
238
+ }
239
+
240
+ /**
241
+ * Reset the route tree, clear cache, collect routes from child elements.
242
+ */
243
+ collectSlottedRoutes(): void {
244
+ this.__slottedRouteTree = new RouteTree();
245
+ this.clear();
246
+ this._slottedNames.forEach(slotName => {
247
+ this.__slottedRouteTree.insert(slotName);
248
+ });
249
+ }
250
+
251
+ /**
252
+ * Reset the route tree, clear cache, collect routes from value.
253
+ */
254
+ collectFieldRoutes(value: typeof this.routes): void {
255
+ this.__fieldRouteTree = new RouteTree();
256
+ this.clear();
257
+ value.forEach(({ path }) => {
258
+ this.__fieldRouteTree.insert(path);
259
+ });
260
+ }
261
+
262
+ useWhich(path: string): string {
263
+ return this.__fieldRouteTree.useWhich(this.baseURL + path);
264
+ }
265
+
266
+ parseParams(routeTemplate: string, path: string): Record<string, string> {
267
+ return this.__fieldRouteTree.parseParams(path, routeTemplate);
268
+ }
269
+
270
+ static updateAll(): void {
271
+ this.routerInstances.forEach((i) => {
272
+ i.handlePopstate();
273
+ });
274
+ }
275
+
276
+ handlePopstate = this.events.add(window, "popstate", () => {
277
+ this.pathname = location.pathname;
278
+ }) as () => void;
279
+ }
280
+
281
+ export default Router;
@@ -0,0 +1,281 @@
1
+ import { HandlerEvent } from "@godown/element";
2
+ import { godown } from "@godown/element/decorators/godown.js";
3
+ import { part } from "@godown/element/decorators/part.js";
4
+ import { styles } from "@godown/element/decorators/styles.js";
5
+ import { attr } from "@godown/element/directives/attr.js";
6
+ import { htmlSlot } from "@godown/element/directives/html-slot.js";
7
+ import svgCaretDown from "@godown/f7-icon/icons/chevron-down.js";
8
+ import { css, html, nothing, type TemplateResult } from "lit";
9
+ import { property, state } from "lit/decorators.js";
10
+ import { ifDefined } from "lit/directives/if-defined.js";
11
+
12
+ import Input from "./input.js";
13
+
14
+ function contain(a: string, b: string): boolean {
15
+ return a && b && a.toLowerCase().includes(b.toLowerCase());
16
+ }
17
+
18
+ function betweenAt(i: number, s: string, c: string) {
19
+ const start = s.slice(0, i).lastIndexOf(c) + 1 || 0;
20
+ const end = s.indexOf(c, i) || s.length;
21
+ return s.slice(start, end);
22
+ }
23
+
24
+ function updateChecked(element: HTMLElement | null, operation: 0 | 1) {
25
+ if (element) {
26
+ const name = "checked";
27
+ if (operation) {
28
+ element.setAttribute(name, "");
29
+ } else {
30
+ element.removeAttribute(name);
31
+ }
32
+ }
33
+ }
34
+
35
+ const protoName = "select";
36
+
37
+ /**
38
+ * {@linkcode Select} is similar to `<select>`.
39
+ *
40
+ * Elements with the value attribute/property can be used as options.
41
+ *
42
+ * The checked attribute will be added to the selected element.
43
+ *
44
+ * Multi-selected state looks the same as single-selected.
45
+ *
46
+ * Input will filter the element.
47
+ *
48
+ * @slot - Options.
49
+ * @category input
50
+ */
51
+ @godown(protoName)
52
+ @styles(
53
+ css`
54
+ [part=input] {
55
+ text-overflow: ellipsis;
56
+ }
57
+
58
+ [part=content] {
59
+ position: absolute;
60
+ width: 100%;
61
+ visibility: hidden;
62
+ }
63
+
64
+ [direction=bottom] [part=content] {
65
+ top: 100%;
66
+ }
67
+
68
+ [direction=top] [part=content] {
69
+ bottom: 100%;
70
+ }
71
+
72
+ [visible] [part=content] {
73
+ visibility: visible
74
+ }
75
+ `,
76
+ )
77
+ class Select extends Input {
78
+ // @ts-ignore
79
+ value: string | string[];
80
+
81
+ /**
82
+ * Selected texts.
83
+ */
84
+ @property()
85
+ text: string;
86
+
87
+ @part("content")
88
+ protected _content: HTMLElement;
89
+
90
+ @property()
91
+ direction: "top" | "bottom" | undefined;
92
+
93
+ @property({ type: Boolean })
94
+ multiple: boolean;
95
+
96
+ @property({ type: Boolean })
97
+ visible: boolean;
98
+
99
+ @state()
100
+ protected autoDirection: "top" | "bottom" = "bottom";
101
+
102
+ protected lastChecked: HTMLElement;
103
+ protected defaultText: string;
104
+ protected defaultChecked: HTMLElement[];
105
+ private _store: { value: string; text: string; }[] = [];
106
+
107
+ protected render(): TemplateResult<1> {
108
+ return html`<div part="root" ${
109
+ attr({
110
+ ...this.observedRecord,
111
+ direction: this.direction || this.autoDirection,
112
+ })
113
+ } class="input-field">
114
+ ${[
115
+ this._renderPrefix(),
116
+ html`<input
117
+ part="input"
118
+ dir="${ifDefined(this.dir)}"
119
+ id="${this.makeId}"
120
+ .value="${this.text}"
121
+ type="${this.type}"
122
+ placeholder="${this.placeholder || nothing}"
123
+ ?autofocus="${this.autofocus}"
124
+ autocapitalize="${this.autocapitalize || nothing}"
125
+ autocomplete="${this.autocomplete || nothing}"
126
+ ?disabled="${this.disabled}"
127
+ @focus="${this._handleFocus}"
128
+ @input="${this._handleInput}"
129
+ >`,
130
+ html`<label for="${this.makeId}" part="suffix">
131
+ <i part="space"></i>
132
+ <i part="icon">${svgCaretDown()}</i>
133
+ <i part="space"></i>
134
+ </label>`,
135
+ html`<label for="${this.makeId}" part="content">
136
+ ${htmlSlot()}
137
+ </label>`,
138
+ ]}
139
+ </div>`;
140
+ }
141
+
142
+ protected _handleFocus(): void {
143
+ if (!this.direction) {
144
+ const { top, bottom } = this.getBoundingClientRect();
145
+ if (window.innerHeight - bottom < this._content.clientHeight && top > this._content.clientHeight) {
146
+ this.autoDirection = "top";
147
+ } else {
148
+ this.autoDirection = "bottom";
149
+ }
150
+ }
151
+ this.visible = true;
152
+ }
153
+
154
+ protected firstUpdated(): void {
155
+ this.events.add(this._content, "click", (e: HandlerEvent) => {
156
+ e.preventDefault();
157
+ e.stopPropagation();
158
+ const { target } = e;
159
+ const value = this.optionValue(target);
160
+ if (value) {
161
+ const operation = this.select(value, target.textContent);
162
+ if (!this.multiple) {
163
+ updateChecked(this.lastChecked, 0);
164
+ }
165
+ updateChecked(target, operation);
166
+ this.lastChecked = target;
167
+ }
168
+ this._input.focus();
169
+ });
170
+ this.events.add(document, "click", (e: HandlerEvent) => {
171
+ // e.preventDefault();
172
+ e.stopPropagation();
173
+ const composed1 = e.composedPath()[0] as HTMLElement;
174
+ if (composed1 && !this.shadowRoot.contains(composed1)) {
175
+ this.blur();
176
+ }
177
+ });
178
+ }
179
+
180
+ protected _connectedInit(): void {
181
+ if (!this.value) {
182
+ const checked = [...this.querySelectorAll<HTMLElement>("[checked]")];
183
+ const list = this.multiple
184
+ ? checked
185
+ : checked.length
186
+ ? [this.lastChecked = checked[0]]
187
+ : [];
188
+ list.forEach((element: HTMLElement) => {
189
+ const operation = this.select(this.optionValue(element), element.textContent);
190
+ updateChecked(element, operation);
191
+ });
192
+
193
+ this.default = this.value;
194
+ this.defaultText = this.text;
195
+ this.defaultChecked = checked;
196
+ }
197
+ if (!this.text) {
198
+ this.text = "";
199
+ }
200
+ }
201
+
202
+ reset(): void {
203
+ this.value = this.default;
204
+ this.text = this.defaultText;
205
+ this.querySelectorAll<HTMLElement>("[checked]").forEach(element => updateChecked(element, 0));
206
+ this.defaultChecked.forEach(element => updateChecked(element, 1));
207
+ }
208
+
209
+ select(value: string, text?: string): 0 | 1 {
210
+ text ||= value;
211
+ let operation: 0 | 1 = 0;
212
+ if (this.multiple) {
213
+ const i = this._store.findIndex(s => s.value === value);
214
+ if (i > -1) {
215
+ this._store.splice(i, 1);
216
+ } else {
217
+ this._store.push({ value, text });
218
+ operation = 1;
219
+ }
220
+ this.value = this._store.map(s => s.value);
221
+ this.text = this._store.map(s => s.text).join(", ");
222
+ } else {
223
+ if (this.value === value) {
224
+ this.value = "";
225
+ this.text = "";
226
+ } else {
227
+ this.value = value;
228
+ this.text = text;
229
+ operation = 1;
230
+ }
231
+ }
232
+ this.dispatchEvent(new CustomEvent("change", { detail: this.namevalue() }));
233
+ this.filter();
234
+ return operation;
235
+ }
236
+
237
+ filter(query?: string): void {
238
+ query = query?.trim();
239
+ [...this.children].forEach((element: HTMLElement) => {
240
+ this.filterCallback(
241
+ element,
242
+ !query
243
+ || contain(this.optionValue(element), query)
244
+ || contain(element.textContent, query),
245
+ query,
246
+ );
247
+ });
248
+ }
249
+
250
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
251
+ filterCallback(element: HTMLElement, match: boolean, query: string): void {
252
+ element.style.display = match ? "" : "none";
253
+ }
254
+
255
+ protected _handleInput(e: HandlerEvent<HTMLInputElement>): void {
256
+ e.stopPropagation();
257
+ if (this.compositing) {
258
+ return;
259
+ }
260
+ const s = this._input.value;
261
+ this.filter(this.multiple ? betweenAt(this._input.selectionStart, s, ",") : s);
262
+ this.dispatchEvent(new CustomEvent("input", { detail: this.namevalue() }));
263
+ }
264
+
265
+ focus(options?: FocusOptions): void {
266
+ this._input.focus(options);
267
+ this.visible = true;
268
+ }
269
+
270
+ blur(): void {
271
+ this._input.blur();
272
+ this.visible = false;
273
+ super.blur();
274
+ }
275
+
276
+ optionValue(option: HTMLElement): string {
277
+ return (option as any).value || option.getAttribute("value") || "";
278
+ }
279
+ }
280
+
281
+ export default Select;
@@ -0,0 +1,119 @@
1
+ import { godown } from "@godown/element/decorators/godown.js";
2
+ import { styles } from "@godown/element/decorators/styles.js";
3
+ import { attr } from "@godown/element/directives/attr.js";
4
+ import { htmlSlot } from "@godown/element/directives/html-slot.js";
5
+ import iconPhoto from "@godown/f7-icon/icons/photo.js";
6
+ import { css, html, type TemplateResult } from "lit";
7
+ import { property, state } from "lit/decorators.js";
8
+
9
+ import { cssGlobalVars, GlobalStyle, scopePrefix } from "../core/global-style.js";
10
+
11
+ const protoName = "skeleton";
12
+ const cssScope = scopePrefix(protoName);
13
+
14
+ /**
15
+ * {@linkcode Skeleton} renders a skeleton screen.
16
+ *
17
+ * @slot loading - The content if loading is true.
18
+ * @slot - The content if loading is false.
19
+ * @category feedback
20
+ */
21
+ @godown(protoName)
22
+ @styles(
23
+ css`
24
+ :host {
25
+ ${cssScope}--from: var(${cssGlobalVars._colors.darkgray[9]});
26
+ ${cssScope}--to: var(${cssGlobalVars._colors.darkgray[7]});
27
+ ${cssScope}--deg: 95deg;
28
+ ${cssScope}--duration: 1.5s;
29
+ ${cssScope}--icon-size: 5em;
30
+ ${cssScope}--icon-margin: .25em;
31
+ color: var(${cssGlobalVars._colors.darkgray[5]});
32
+ background: var(${cssScope}--from);
33
+ min-height: 1.5em;
34
+ width: 100%;
35
+ flex-shrink: 0;
36
+ display: block;
37
+ overflow: hidden;
38
+ }
39
+
40
+ [part=root] {
41
+ height: 100%;
42
+ min-height: inherit;
43
+ text-align: center;
44
+ animation: var(${cssScope}--duration) ease-in-out 0s infinite none running;
45
+ }
46
+
47
+ svg {
48
+ --size:var(${cssScope}--icon-size);
49
+ font-size: var(--size);
50
+ margin: calc(var(--size) * 0.05);
51
+ }
52
+
53
+ [animation=position] {
54
+ background-image: linear-gradient(
55
+ var(${cssScope}--deg),
56
+ var(${cssScope}--from) 36%,
57
+ var(${cssScope}--to) 50%,
58
+ var(${cssScope}--from) 64%
59
+ );
60
+ background-color: transparent;
61
+ background-size: 200% 100%;
62
+ animation-name: po;
63
+ }
64
+
65
+ @keyframes po {
66
+ from {
67
+ background-position: 150% center;
68
+ }
69
+ to {
70
+ background-position: -50% center;
71
+ }
72
+ }
73
+
74
+ [animation=opacity] {
75
+ animation-name: op;
76
+ animation-direction: alternate;
77
+ }
78
+
79
+ @keyframes op {
80
+ 50% {
81
+ opacity: 0.25;
82
+ }
83
+ to {
84
+ opacity: 1;
85
+ }
86
+ }
87
+ `,
88
+ )
89
+ class Skeleton extends GlobalStyle {
90
+ /**
91
+ * If "image", render a image placeholder.
92
+ */
93
+ @property()
94
+ type: "text" | "image";
95
+
96
+ /**
97
+ * Animation type.
98
+ * opacity animation only effect on slotted element and image icon.
99
+ */
100
+ @property()
101
+ animation: "position" | "opacity" = "position";
102
+
103
+ /**
104
+ * If false, render slot only.
105
+ */
106
+ @state()
107
+ loading = true;
108
+
109
+ protected render(): TemplateResult<1> {
110
+ if (!this.loading) {
111
+ return htmlSlot();
112
+ }
113
+ return html`<div part="root" ${attr(this.observedRecord)}>
114
+ ${this.type === "image" ? iconPhoto() : ""}
115
+ ${htmlSlot("loading")}</div>`;
116
+ }
117
+ }
118
+
119
+ export default Skeleton;