amateras 0.10.1 → 0.11.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.
Files changed (175) hide show
  1. package/README.md +30 -25
  2. package/build/core.js +1 -0
  3. package/build/css.js +1 -0
  4. package/build/for.js +1 -0
  5. package/build/i18n.js +1 -0
  6. package/build/idb.js +1 -0
  7. package/build/if.js +1 -0
  8. package/build/import-map.js +1 -0
  9. package/build/markdown.js +1 -0
  10. package/build/match.js +1 -0
  11. package/build/meta.js +1 -0
  12. package/build/prefetch.js +1 -0
  13. package/build/router.js +1 -0
  14. package/build/signal.js +1 -0
  15. package/build/ui.js +1 -0
  16. package/build/utils.js +1 -0
  17. package/build/widget.js +1 -0
  18. package/package.json +9 -7
  19. package/packages/core/package.json +19 -0
  20. package/packages/core/src/env.browser.ts +21 -0
  21. package/packages/core/src/env.node.ts +21 -0
  22. package/packages/core/src/global.ts +5 -0
  23. package/packages/core/src/index.ts +184 -0
  24. package/packages/core/src/lib/hmr.ts +145 -0
  25. package/packages/core/src/lib/symbols.ts +2 -0
  26. package/packages/core/src/structure/ElementProto.ts +95 -0
  27. package/packages/core/src/structure/GlobalState.ts +9 -0
  28. package/packages/core/src/structure/NodeProto.ts +18 -0
  29. package/packages/core/src/structure/Proto.ts +90 -0
  30. package/packages/core/src/structure/ProxyProto.ts +20 -0
  31. package/packages/core/src/structure/TextProto.ts +22 -0
  32. package/packages/core/src/structure/WidgetEvent.ts +17 -0
  33. package/packages/css/README.md +128 -0
  34. package/packages/css/package.json +15 -0
  35. package/packages/css/src/ext/colors/amber.ts +25 -0
  36. package/packages/css/src/ext/colors/blackwhite.ts +13 -0
  37. package/packages/css/src/ext/colors/blue.ts +25 -0
  38. package/packages/css/src/ext/colors/cyan.ts +25 -0
  39. package/packages/css/src/ext/colors/emerald.ts +25 -0
  40. package/packages/css/src/ext/colors/fuchsia.ts +25 -0
  41. package/packages/css/src/ext/colors/gray.ts +25 -0
  42. package/packages/css/src/ext/colors/green.ts +25 -0
  43. package/packages/css/src/ext/colors/indigo.ts +25 -0
  44. package/packages/css/src/ext/colors/lime.ts +25 -0
  45. package/packages/css/src/ext/colors/neutral.ts +25 -0
  46. package/packages/css/src/ext/colors/orange.ts +25 -0
  47. package/packages/css/src/ext/colors/pink.ts +25 -0
  48. package/packages/css/src/ext/colors/purple.ts +25 -0
  49. package/packages/css/src/ext/colors/red.ts +25 -0
  50. package/packages/css/src/ext/colors/rose.ts +25 -0
  51. package/packages/css/src/ext/colors/sky.ts +25 -0
  52. package/packages/css/src/ext/colors/slate.ts +25 -0
  53. package/packages/css/src/ext/colors/stone.ts +25 -0
  54. package/packages/css/src/ext/colors/teal.ts +25 -0
  55. package/packages/css/src/ext/colors/violet.ts +25 -0
  56. package/packages/css/src/ext/colors/yellow.ts +25 -0
  57. package/packages/css/src/ext/colors/zinc.ts +25 -0
  58. package/packages/css/src/ext/colors.ts +23 -0
  59. package/packages/css/src/ext/keyframes.ts +37 -0
  60. package/packages/css/src/ext/property.ts +68 -0
  61. package/packages/css/src/ext/variable.ts +51 -0
  62. package/packages/css/src/index.ts +103 -0
  63. package/packages/css/src/lib/cache.ts +27 -0
  64. package/packages/css/src/lib/colorAssign.ts +6 -0
  65. package/packages/css/src/lib/createRule.ts +31 -0
  66. package/packages/css/src/lib/utils.ts +1 -0
  67. package/packages/css/src/structure/$CSS.ts +4 -0
  68. package/packages/css/src/structure/$CSSKeyframes.ts +13 -0
  69. package/packages/css/src/structure/$CSSProperty.ts +21 -0
  70. package/packages/css/src/structure/$CSSRule.ts +39 -0
  71. package/packages/css/src/structure/$CSSVariable.ts +34 -0
  72. package/packages/css/src/types.ts +300 -0
  73. package/packages/for/package.json +15 -0
  74. package/packages/for/src/global.ts +7 -0
  75. package/packages/for/src/index.ts +15 -0
  76. package/packages/for/src/structure/For.ts +74 -0
  77. package/packages/hmr/package.json +13 -0
  78. package/packages/hmr/src/index.ts +27 -0
  79. package/packages/i18n/README.md +73 -0
  80. package/packages/i18n/package.json +15 -0
  81. package/packages/i18n/src/index.ts +78 -0
  82. package/packages/i18n/src/structure/I18n.ts +51 -0
  83. package/packages/i18n/src/structure/I18nDictionary.ts +31 -0
  84. package/packages/i18n/src/structure/I18nTranslation.ts +51 -0
  85. package/packages/i18n/src/types.ts +77 -0
  86. package/packages/idb/README.md +127 -0
  87. package/packages/idb/package.json +16 -0
  88. package/packages/idb/src/core.ts +6 -0
  89. package/packages/idb/src/index.ts +17 -0
  90. package/packages/idb/src/lib/$IDBRequest.ts +8 -0
  91. package/packages/idb/src/structure/$IDB.ts +63 -0
  92. package/packages/idb/src/structure/$IDBCursor.ts +34 -0
  93. package/packages/idb/src/structure/$IDBIndex.ts +48 -0
  94. package/packages/idb/src/structure/$IDBStore.ts +103 -0
  95. package/packages/idb/src/structure/$IDBStoreBase.ts +30 -0
  96. package/packages/idb/src/structure/$IDBTransaction.ts +38 -0
  97. package/packages/idb/src/structure/builder/$IDBBuilder.ts +229 -0
  98. package/packages/idb/src/structure/builder/$IDBStoreBuilder.ts +100 -0
  99. package/packages/if/package.json +15 -0
  100. package/packages/if/src/global.ts +15 -0
  101. package/packages/if/src/index.ts +51 -0
  102. package/packages/if/src/structure/Condition.ts +44 -0
  103. package/packages/if/src/structure/ConditionStatement.ts +25 -0
  104. package/packages/if/src/structure/Else.ts +6 -0
  105. package/packages/if/src/structure/ElseIf.ts +6 -0
  106. package/packages/if/src/structure/If.ts +6 -0
  107. package/packages/markdown/README.md +53 -0
  108. package/packages/markdown/package.json +15 -0
  109. package/packages/markdown/src/index.ts +3 -0
  110. package/packages/markdown/src/lib/type.ts +26 -0
  111. package/packages/markdown/src/lib/util.ts +21 -0
  112. package/packages/markdown/src/structure/Markdown.ts +57 -0
  113. package/packages/markdown/src/structure/MarkdownLexer.ts +111 -0
  114. package/packages/markdown/src/structure/MarkdownParser.ts +34 -0
  115. package/packages/markdown/src/syntax/alert.ts +46 -0
  116. package/packages/markdown/src/syntax/blockquote.ts +35 -0
  117. package/packages/markdown/src/syntax/bold.ts +11 -0
  118. package/packages/markdown/src/syntax/code.ts +11 -0
  119. package/packages/markdown/src/syntax/codeblock.ts +44 -0
  120. package/packages/markdown/src/syntax/heading.ts +14 -0
  121. package/packages/markdown/src/syntax/horizontalRule.ts +11 -0
  122. package/packages/markdown/src/syntax/image.ts +23 -0
  123. package/packages/markdown/src/syntax/italic.ts +11 -0
  124. package/packages/markdown/src/syntax/link.ts +46 -0
  125. package/packages/markdown/src/syntax/list.ts +121 -0
  126. package/packages/markdown/src/syntax/table.ts +67 -0
  127. package/packages/markdown/src/syntax/text.ts +19 -0
  128. package/packages/match/package.json +15 -0
  129. package/packages/match/src/global.ts +14 -0
  130. package/packages/match/src/index.ts +33 -0
  131. package/packages/match/src/structure/Case.ts +15 -0
  132. package/packages/match/src/structure/Default.ts +12 -0
  133. package/packages/match/src/structure/Match.ts +78 -0
  134. package/packages/meta/package.json +14 -0
  135. package/packages/meta/src/index.ts +36 -0
  136. package/packages/meta/src/lib/resolveMeta.ts +27 -0
  137. package/packages/meta/src/types.ts +36 -0
  138. package/packages/prefetch/package.json +14 -0
  139. package/packages/prefetch/src/index.ts +70 -0
  140. package/packages/router/README.md +18 -0
  141. package/packages/router/package.json +16 -0
  142. package/packages/router/src/global.ts +22 -0
  143. package/packages/router/src/index.ts +106 -0
  144. package/packages/router/src/structure/Link.ts +17 -0
  145. package/packages/router/src/structure/NavLink.ts +19 -0
  146. package/packages/router/src/structure/Page.ts +30 -0
  147. package/packages/router/src/structure/Route.ts +123 -0
  148. package/packages/router/src/structure/RouteGroup.ts +24 -0
  149. package/packages/router/src/structure/RouteNode.ts +54 -0
  150. package/packages/router/src/structure/RouteSlot.ts +34 -0
  151. package/packages/router/src/structure/Router.ts +192 -0
  152. package/packages/router/src/structure/RouterConstructor.ts +18 -0
  153. package/packages/router/src/types.ts +41 -0
  154. package/packages/signal/README.md +93 -0
  155. package/packages/signal/package.json +15 -0
  156. package/packages/signal/src/index.ts +97 -0
  157. package/packages/signal/src/lib/track.ts +18 -0
  158. package/packages/signal/src/structure/Signal.ts +59 -0
  159. package/packages/ui/package.json +14 -0
  160. package/packages/ui/src/index.ts +4 -0
  161. package/packages/ui/src/lib/slideshowAnimations.ts +39 -0
  162. package/packages/ui/src/structure/Radio.ts +77 -0
  163. package/packages/ui/src/structure/Slide.ts +11 -0
  164. package/packages/ui/src/structure/Slideshow.ts +99 -0
  165. package/packages/utils/package.json +18 -0
  166. package/packages/utils/src/global.ts +39 -0
  167. package/packages/utils/src/index.bun.ts +3 -0
  168. package/packages/utils/src/index.ts +2 -0
  169. package/packages/utils/src/lib/debugger.ts +14 -0
  170. package/packages/utils/src/lib/utils.ts +119 -0
  171. package/packages/utils/src/structure/UID.ts +18 -0
  172. package/packages/widget/README.md +29 -0
  173. package/packages/widget/package.json +14 -0
  174. package/packages/widget/src/index.ts +82 -0
  175. package/packages/widget/src/structure/Widget.ts +42 -0
@@ -0,0 +1,192 @@
1
+ import { onclient } from "@amateras/core";
2
+ import { Proto } from "@amateras/core";
3
+ import { _JSON_parse, _JSON_stringify, _null, _Object_entries, forEach, map, toURL } from "@amateras/utils";
4
+ import type { Widget } from "@amateras/widget";
5
+ import type { AsyncWidget, PageLayout, PathToParamsMap, RoutePath, ValidatePath } from "../types";
6
+ import type { Route } from "./Route";
7
+ import { RouteSlot } from "./RouteSlot";
8
+
9
+ type Mode = 1 | 2;
10
+ type RouterDicrection = 'forward' | 'back';
11
+
12
+ let index = 0;
13
+ const [PUSH, REPLACE] = [1, 2] as const;
14
+ const [FORWARD, BACK] = ['forward', 'back'] as const;
15
+ const SCROLL_KEY = '__scroll_history__';
16
+ const storage = onclient() ? sessionStorage : _null;
17
+ const _addEventListener = onclient() ? window.addEventListener : _null;
18
+ const _removeEventListener = onclient() ? window.removeEventListener : _null;
19
+ if (onclient()) history.scrollRestoration = 'manual';
20
+
21
+ type ScrollData = {[key: number]: { [id: string]: { x: number, y: number }}};
22
+
23
+ const scrollRecord = (e?: Event) => {
24
+ const data = RouterProto.scrollHistory;
25
+ if (e) {
26
+ let element = e.target as HTMLElement;
27
+ if (element.id === '') return;
28
+ data[index] = { [element.id]: { x: element.scrollLeft, y: element.scrollTop } };
29
+ } else {
30
+ forEach(_Object_entries(data), ([i]) => +i >= index && delete data[+i]);
31
+ }
32
+ storage?.setItem(SCROLL_KEY, _JSON_stringify(data));
33
+ }
34
+
35
+ export class RouterProto extends Proto {
36
+ direction: RouterDicrection = FORWARD;
37
+ prev: URL | null = _null;
38
+ routes = new Map<string, Route>();
39
+ slot = new RouteSlot();
40
+ static routers = new Set<RouterProto>();
41
+ constructor() {
42
+ super(() => $(this.slot));
43
+ if (onclient()) RouterProto.routers.add(this);
44
+ }
45
+
46
+ set href(url: URL) {
47
+ this.global.router.href = url;
48
+ }
49
+
50
+ override build() {
51
+ if (onclient()) {
52
+ const resolve = () => {
53
+ const stateIndex = history.state?.index ?? 0;
54
+ if (index > stateIndex) this.direction = BACK;
55
+ if (index < stateIndex) this.direction = FORWARD;
56
+ index = stateIndex;
57
+ this.prev = this.href;
58
+ this.href = toURL(location.href);
59
+ this.resolve(location.href);
60
+ }
61
+ resolve();
62
+ _addEventListener?.('popstate', resolve);
63
+ _addEventListener?.('scroll', scrollRecord, {
64
+ capture: true,
65
+ passive: false
66
+ });
67
+ this.disposers.add(() => {
68
+ _removeEventListener?.('popstate', resolve);
69
+ _removeEventListener?.('scroll', scrollRecord, {
70
+ capture: true
71
+ })
72
+ })
73
+ }
74
+ return super.build();
75
+ }
76
+
77
+ async resolve(path: string | URL) {
78
+ if (!path) return;
79
+ let url = toURL(path);
80
+ for (let [,route] of this.routes) {
81
+ let routes = await route.resolve(url.pathname, this.slot, {});
82
+ // 一旦有一个 route 解析成功就跳过剩下的 routes
83
+ if (routes) {
84
+ // 实现 NavLink 自动检测 href 匹配当前 location
85
+ this.global.router.routes = routes;
86
+ let parentPaths: string[] = [''];
87
+ let paths: string[] = [];
88
+ forEach(routes, route => {
89
+ parentPaths = map(route.validPaths, validPath =>
90
+ map(parentPaths, path => path + validPath)
91
+ ).flat()
92
+ paths.push(...parentPaths);
93
+ return parentPaths
94
+ })
95
+ this.global.router.matchPaths = paths;
96
+ break;
97
+ };
98
+ }
99
+ // NavLink 检测匹配
100
+ forEach(this.global.router.navlinks, navlink => navlink.checkActive())
101
+ // location 变更事件触发
102
+ RouterProto.dispatchEvent();
103
+ // restore scroll position
104
+ RouterProto.scrollRestoration();
105
+ }
106
+
107
+ static open(path: string, target?: string) {
108
+ RouterProto.writeState(path, PUSH, target);
109
+ }
110
+
111
+ static forward() {
112
+ history.forward();
113
+ }
114
+
115
+ static back() {
116
+ history.back();
117
+ }
118
+
119
+ static replace(path: string) {
120
+ RouterProto.writeState(path, REPLACE);
121
+ }
122
+
123
+ static get scrollData(): ScrollData[number] {
124
+ return this.scrollHistory[index] ?? {}
125
+ }
126
+
127
+ static get scrollHistory(): ScrollData {
128
+ return _JSON_parse(storage?.getItem(SCROLL_KEY) ?? '{}')
129
+ }
130
+
131
+ static scrollRestoration() {
132
+ if (onclient()) {
133
+ let scrollData = RouterProto.scrollData ?? {x: 0, y: 0};
134
+ forEach(_Object_entries(scrollData), ([id, {x, y}]) => document.querySelector(`#${id}`)?.scrollTo(x, y));
135
+ }
136
+ }
137
+
138
+ private static writeState(path: string | URL | Nullish, mode: Mode, target?: string) {
139
+ if (!path) return;
140
+ let url = toURL(path);
141
+ if (onclient() && url.href === location.href) return;
142
+ if (target && target !== '_self') return open(url, target);
143
+ if (mode === PUSH) index++;
144
+ if (onclient()) scrollRecord();
145
+ forEach(this.routers, router => {
146
+ router.direction = FORWARD;
147
+ if (onclient()) {
148
+ router.prev = toURL(location.href);
149
+ history[mode === PUSH ? 'pushState' : 'replaceState']({index}, '', url);
150
+ }
151
+ router.href = url;
152
+ router.resolve(path)
153
+ })
154
+ RouterProto.dispatchEvent();
155
+ }
156
+
157
+ private static dispatchEvent() {
158
+ if (onclient()) window.dispatchEvent(new Event('pathchange'));
159
+ }
160
+ }
161
+
162
+ declare global {
163
+ export interface GlobalEventHandlersEventMap {
164
+ pathchange: Event
165
+ }
166
+ }
167
+
168
+
169
+ export interface RouterProto {
170
+ route<_Path extends RoutePath, Props>(
171
+ path: ValidatePath<_Path, Props, _Path>,
172
+ widget: Widget<any, Props>,
173
+ handle?: (route: Route<'', _Path, PathToParamsMap<_Path>>) => void): void
174
+
175
+ route<_Path extends RoutePath, Props>(
176
+ path: ValidatePath<_Path, Props, _Path>,
177
+ widget: AsyncWidget<Props>,
178
+ handle?: (route: Route<'', _Path, PathToParamsMap<_Path>>) => void): void
179
+
180
+ route<
181
+ Path extends RoutePath,
182
+ Layout extends PageLayout<Path>
183
+ >(path: Path, layout: Layout, handle?: (route: Route<'', Path, PathToParamsMap<Path>>) => void): void
184
+
185
+
186
+ group<
187
+ Path extends RoutePath
188
+ >(path: Path, handle: (route: Route<'', Path, PathToParamsMap<Path>>) => void): void;
189
+
190
+ notFound(layout: Widget): void;
191
+ notFound(layout: PageLayout): void;
192
+ }
@@ -0,0 +1,18 @@
1
+ import { symbol_ProtoType } from "@amateras/core";
2
+ import { RouterProto } from "./Router";
3
+
4
+ export type RouterHandle = ($$: RouterProto) => void;
5
+
6
+ export interface Router {
7
+ new(): RouterProto;
8
+ }
9
+
10
+ export const RouterConstructor = (handle: RouterHandle) => {
11
+ return class extends RouterProto {
12
+ static override [symbol_ProtoType] = 'Router';
13
+ constructor() {
14
+ super();
15
+ handle(this);
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,41 @@
1
+ import type { RouteSlot } from "#structure/RouteSlot";
2
+ import type { Widget } from "@amateras/widget";
3
+
4
+ export type RoutePath = string;
5
+
6
+ export type RouteParams = { [key: string]: string }
7
+
8
+ export type PageLayout<Path extends RoutePath = any> = (context: { params: PathToParamsMap<Path>, slot: RouteSlot }) => void;
9
+
10
+ export type AsyncWidget<Params = any> = [() => Promise<{ default: Widget<any, Params> }>]
11
+
12
+ export type PathToParamsUnion<T extends RoutePath> =
13
+ T extends `${infer _Start}:${infer Param}/${infer Rest}`
14
+ ? Param | PathToParamsUnion<Rest>
15
+ : T extends `${infer _Start}:${infer Param}`
16
+ ? Param
17
+ : never;
18
+
19
+ export type ParamsArrayToParamsMap<ParamArray extends string[]> = ParamsUnionToMap<ParamArray[number]>
20
+ // type Test_ParamsArrayToParamsMap = ParamsArrayToParamsMap<['test1' | 'test2' | 'test3?' | 'test4']>
21
+
22
+ export type ParamsUnionToMap<T extends string> = Prettify<{
23
+ [P in T extends `${string}?` ? never : T]: string;
24
+ } & {
25
+ [P in T extends `${infer Key}?` ? Key : never]?: string;
26
+ }>
27
+ // type Test_ParamRequired = ParamsUnionToMap<'test1' | 'test2' | 'test3?' | 'test4'>
28
+
29
+ export type PathToParamsMap<Path extends RoutePath> = {
30
+ [P in PathToParamsUnion<Path>]: string
31
+ }
32
+ // type Test_PathToParamsMap = PathToParamsMap<'/path/:test1/path/@:test2/:test3/:test4'>
33
+
34
+ export type PathConcat<A extends string, B extends string, C extends string = ''> = `${A}${B}${C}`;
35
+
36
+ export type AliasRequired<Params, AliasParams> = Omit<Params, keyof AliasParams>;
37
+
38
+ export type ValidatePath<Path extends string, Props, FullPath extends string> =
39
+ RequiredKeys<Props> extends PathToParamsUnion<FullPath>
40
+ ? Path
41
+ : `Error: (${FullPath}) Missing keys: ${Exclude<RequiredKeys<Props>, PathToParamsUnion<FullPath>> & string}`;
@@ -0,0 +1,93 @@
1
+ # amateras/signal
2
+
3
+ ## Usage
4
+
5
+ ```ts
6
+ import 'amateras';
7
+ import 'amateras/signal';
8
+
9
+ // define a signal with value 0
10
+ const count$ = $.signal(0);
11
+
12
+ // this variable will be auto recalculate when count$ changes
13
+ const doubleCount$ = $.compute(() => count$() * 2);
14
+
15
+ // the console message will fired when count$ changes
16
+ $.effect(() => console.log( count$() ))
17
+
18
+ $(document.body).content([
19
+ // Display Counts
20
+ $('p').content( $`Counts: ${count$}` ),
21
+
22
+ // Display Double Counts
23
+ $('p').content( $`Double Counts: ${doubleCount$}` ),
24
+
25
+ // Create a button that make counts plus 1 on click
26
+ $('button').content('Add Count').on('click', () => count$.set(value => value + 1))
27
+ ])
28
+ ```
29
+
30
+ ## Read and Write
31
+
32
+ ```ts
33
+ const number$ = $.signal(0);
34
+ const string$ = $.singal('');
35
+ const boolean$ = $.signal(false);
36
+ const object$ = $.signal({ number: 1 });
37
+
38
+ // write value
39
+ number$.set(42);
40
+ string$.set('New Content');
41
+ boolean$.set(true);
42
+ object$.set({ number: 42 });
43
+
44
+ // read value
45
+ number$(); // 42
46
+ string$(); // 'New Content'
47
+ boolean$(); // true
48
+ object$(); // { number: 42 }
49
+ ```
50
+
51
+ ## Use in attribute methods
52
+
53
+ ```ts
54
+ const src$ = $.signal('/image-1.png');
55
+
56
+ $(document.body).content([
57
+ // you can set signal variable in attribute
58
+ $('img').src( src$ ),
59
+
60
+ $('button').content('Change Image').on('click', () => src$.set('/image-2.png'))
61
+ ])
62
+ ```
63
+
64
+ ## Reactive object
65
+
66
+ ```ts
67
+ const user$ = $.signal({
68
+ name: 'Amateras',
69
+ age: 16,
70
+ avatar: {
71
+ url: '/amateras/avatar.png',
72
+ size: '350x350'
73
+ }
74
+ })
75
+
76
+ $(document.body).content([
77
+ // Display name and age
78
+ $('h1').content( $`${user$.name$} (${user$.age$})` ),
79
+ // Display avatar image
80
+ $('img').src( user$.avatar$.url$ ),
81
+ // Change the user$ when button is clicked
82
+ $('button')
83
+ .content('Change User')
84
+ .on('click', () => user$.set({
85
+ name: 'Tsukimi',
86
+ age: 10,
87
+ avatar: {
88
+ url: '/tsukimi/avatar.png',
89
+ size: '350x350'
90
+ }
91
+ }))
92
+ ])
93
+ ```
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "@amateras/signal",
3
+ "peerDependencies": {
4
+ "@amateras/core": "workspace:*",
5
+ "@amateras/utils": "workspace:*"
6
+ },
7
+ "imports": {
8
+ "#structure/*": "./src/structure/*.ts",
9
+ "#lib/*": "./src/lib/*.ts",
10
+ "#node/*": "./src/node/*.ts"
11
+ },
12
+ "exports": {
13
+ ".": "./src/index.ts"
14
+ }
15
+ }
@@ -0,0 +1,97 @@
1
+ import { track, trackSet, untrack, type UntrackFunction } from "#lib/track";
2
+ import { Signal } from "#structure/Signal";
3
+ import { Proto } from "@amateras/core";
4
+ import { TextProto } from "@amateras/core";
5
+ import { _instanceof, _Object_assign, isEqual, forEach, isBoolean } from "@amateras/utils";
6
+
7
+ declare global {
8
+ export function $<T>(signal: Signal<T>): Signal<T>;
9
+
10
+ export namespace $ {
11
+ export function signal<T>(value: T): Signal<T>;
12
+ export function effect(callback: (untrack: UntrackFunction) => void): void;
13
+ export function compute<T>(callback: (untrack: UntrackFunction) => T): Signal<T>;
14
+ }
15
+ }
16
+
17
+ _Object_assign($, {
18
+ signal(value: any) {
19
+ return new Signal(value);
20
+ },
21
+
22
+ effect(
23
+ callback: (
24
+ untrack: UntrackFunction
25
+ ) => void
26
+ ) {
27
+ track(callback);
28
+ forEach(trackSet, signal => signal.subscribe(_ => callback(untrack)));
29
+ trackSet.clear();
30
+ },
31
+
32
+ compute<T>(
33
+ callback: (
34
+ untrack: UntrackFunction
35
+ ) => T
36
+ ) {
37
+ let result = track(callback);
38
+ let compute = new Signal(result);
39
+ forEach(trackSet, signal => signal.subscribe(_ => compute.set(callback(untrack))));
40
+ trackSet.clear();
41
+ return compute
42
+ }
43
+ })
44
+
45
+ let toTextProto = (signal: Signal) => {
46
+ if (_instanceof(signal, Signal)) {
47
+ let proto = new TextProto(`${signal}`);
48
+ proto.ondom(node => {
49
+ let fn = (value: any) => node.textContent = `${value}`;
50
+ signal.subscribe(fn);
51
+ proto.disposers.add(() => signal.unsubscribe(fn));
52
+ })
53
+
54
+ let fn = (value: any) => proto.content = `${value}`;
55
+ signal.subscribe(fn);
56
+ fn(signal.value);
57
+
58
+ proto.parent = Proto.proto;
59
+ return proto;
60
+ }
61
+ }
62
+
63
+ let setAttr = (name: string, node: HTMLElement, signal: Signal) => {
64
+ //@ts-ignore
65
+ if (name in node) node[name] = signal.value;
66
+ else node.setAttribute(name, `${signal}`)
67
+ }
68
+
69
+ $.process.text.add(toTextProto)
70
+ $.process.craft.add(toTextProto)
71
+ $.process.attr.add((name, signal, proto) => {
72
+ if (_instanceof(signal, Signal)) {
73
+ if (proto.tagname === 'input') {
74
+ if (isEqual(name, ['value', 'checked'] as const)) {
75
+ proto.on('input', e => signal.set((e.currentTarget as HTMLInputElement)[name]));
76
+ let value = signal.value;
77
+ if (isBoolean(value)) value && proto.attr(name, '');
78
+ else proto.attr(name, `${value}`)
79
+ }
80
+ }
81
+ else
82
+ proto.ondom(node => {
83
+ let setNodeAttr = () => setAttr(name, node, signal);
84
+ signal.subscribe(setNodeAttr);
85
+ setNodeAttr();
86
+ proto.disposers.add(() => signal.unsubscribe(setNodeAttr))
87
+ })
88
+
89
+ let setProtoAttr = () => {
90
+
91
+ }
92
+
93
+ return true;
94
+ }
95
+ })
96
+
97
+ export * from "#structure/Signal";
@@ -0,0 +1,18 @@
1
+ import type { Signal } from "#structure/Signal";
2
+
3
+ export type UntrackFunction = (fn: () => unknown) => ReturnType<typeof fn>
4
+
5
+ export let ontrack = false;
6
+ export let trackSet = new Set<Signal>();
7
+ export let untrack: UntrackFunction = fn => {
8
+ ontrack = false;
9
+ let result = fn();
10
+ ontrack = true
11
+ return result;
12
+ }
13
+ export let track = (callback: (untrack: UntrackFunction) => any) => {
14
+ ontrack = true;
15
+ let result = callback(untrack);
16
+ ontrack = false;
17
+ return result;
18
+ }
@@ -0,0 +1,59 @@
1
+ import { forEach, isFunction, isUndefined } from "@amateras/utils";
2
+ import { ontrack, trackSet } from "#lib/track";
3
+
4
+ let signalValueMap = new WeakMap<Signal, {value: any, subs: Set<(value: any) => void>}>();
5
+ let get = (signal: Signal) => signalValueMap.get(signal)!;
6
+
7
+ export interface Signal<T> {
8
+ (): T;
9
+ }
10
+ export class Signal<T = any> {
11
+ key: this;
12
+ constructor(value: T) {
13
+ const $state = () => {
14
+ if (ontrack) trackSet.add(this);
15
+ return get($state as this).value;
16
+ }
17
+ Object.setPrototypeOf($state, this);
18
+ signalValueMap.set($state as this, {value, subs: new Set()})
19
+ this.key = $state as this;
20
+ return $state as this
21
+ }
22
+
23
+ get value(): T {
24
+ return get(this.key).value;
25
+ }
26
+
27
+ get subs(): Set<(value: T) => void> {
28
+ return get(this.key).subs;
29
+ }
30
+
31
+ set(resolver: T | ((oldValue: T) => T)) {
32
+ if (isFunction(resolver)) this.set(resolver(this.value));
33
+ else if (this.value !== resolver) {
34
+ get(this).value = resolver;
35
+ this.emit();
36
+ }
37
+ }
38
+
39
+ modify(callback: (value: T) => void) {
40
+ callback(this.value);
41
+ this.emit();
42
+ }
43
+
44
+ emit() {
45
+ forEach(get(this).subs, subs => subs(this.value));
46
+ }
47
+
48
+ subscribe(callback: (value: T) => void) {
49
+ this.subs.add(callback);
50
+ }
51
+
52
+ unsubscribe(callback: (value: T) => void) {
53
+ this.subs.delete(callback);
54
+ }
55
+
56
+ toString(): string {
57
+ return `${this.value}`
58
+ }
59
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@amateras/ui",
3
+ "peerDependencies": {
4
+ "@amateras/core": "workspace:*",
5
+ "@amateras/utils": "workspace:*"
6
+ },
7
+ "imports": {
8
+ "#structure/*": "./src/structure/*.ts",
9
+ "#lib/*": "./src/lib/*.ts"
10
+ },
11
+ "exports": {
12
+ ".": "./src/index.ts"
13
+ }
14
+ }
@@ -0,0 +1,4 @@
1
+ export * from "#lib/slideshowAnimations";
2
+ export * from "#structure/Slide";
3
+ export * from "#structure/Slideshow";
4
+ export * from "#structure/Radio";
@@ -0,0 +1,39 @@
1
+ import type { SlideshowAnimationHandle } from "#structure/Slideshow";
2
+ import { onclient } from "@amateras/core";
3
+
4
+ export const slideInOut = (
5
+ options?: {
6
+ duration?: number,
7
+ easing?: string,
8
+ direction?: 'left' | 'right' | 'up' | 'down'
9
+ }
10
+ ): SlideshowAnimationHandle =>
11
+ (slideshow, newSlide, oldSlide) => {
12
+ slideshow.slide = newSlide;
13
+ if (!onclient()) return;
14
+ let newNodes = newSlide.toDOM();
15
+ slideshow.node?.append(...newNodes);
16
+
17
+ let animationOptions = {
18
+ duration: options?.duration ?? 500,
19
+ easing: options?.easing ?? 'ease'
20
+ }
21
+
22
+ let translate = $.match(options?.direction, $$ => $$
23
+ .case('up', () => [['0 100%', '0 0'], ['0 0', '0 -100%']])
24
+ .case('down', () => [['0 -100%', '0 0'], ['0 0', '0 100%']])
25
+ .case('left', () => [['100% 0', '0 0'], ['0 0', '-100% 0']])
26
+ .case('right', () => [['-100% 0', '0 0'], ['0 0', '100% 0']])
27
+ .default(() => [['100% 0', '0 0'], ['0 0', '-100% 0']])
28
+ )
29
+
30
+ newSlide.node?.animate({
31
+ translate: translate[0]
32
+ }, animationOptions)
33
+
34
+ const animation = oldSlide?.node?.animate({
35
+ translate: translate[1]
36
+ }, animationOptions)
37
+
38
+ if (animation) animation.onfinish = () => oldSlide?.node?.remove();
39
+ }
@@ -0,0 +1,77 @@
1
+ import { ElementProto } from "@amateras/core";
2
+ import { _null, is } from "@amateras/utils";
3
+ import { UID } from "@amateras/utils";
4
+
5
+ export interface RadioGroupProps {
6
+ value?: any;
7
+ name?: string;
8
+ }
9
+
10
+ export class RadioGroup extends ElementProto {
11
+ value: any;
12
+ constructor({value, ...props}: $.Props<RadioGroupProps>, layout?: $.Layout<RadioGroup>) {
13
+ super('radio-group', props, layout);
14
+ this.value = value;
15
+ this.on('input', e => {
16
+ this.value = is(e.target, HTMLInputElement)?.value
17
+ })
18
+ }
19
+
20
+ static {
21
+ $.style(RadioGroup, 'radio-group{display:block}')
22
+ }
23
+ }
24
+
25
+ export interface RadioItemProps {
26
+ inputId?: string;
27
+ name?: string;
28
+ value: any;
29
+ }
30
+
31
+ export class RadioItem<T> extends ElementProto {
32
+ inputId: string;
33
+ name: string | null;
34
+ value: T;
35
+ constructor({inputId, name, value, ...props}: $.Props<RadioItemProps>, layout?: $.Layout<RadioItem<T>>) {
36
+ super('radio-item', props, layout);
37
+ this.inputId = inputId ?? `input-${UID.persistInProto(this, 'radio-item')}`;
38
+ this.name = name ?? _null;
39
+ this.value = value;
40
+ }
41
+
42
+ static {
43
+ $.style(RadioItem, 'radio-item{display:block}')
44
+ }
45
+ }
46
+
47
+ export class Radio extends ElementProto {
48
+ constructor(props: $.Props, layout?: $.Layout<Radio>) {
49
+ super('input', {type: 'radio', ...props}, layout);
50
+ }
51
+
52
+ override build(children?: boolean): this {
53
+ let selector = this.findAbove(proto => is(proto, RadioItem));
54
+ if (selector) {
55
+ this.attr('id', selector.inputId);
56
+ this.attr('name', selector.name);
57
+ }
58
+ return super.build(children);
59
+ }
60
+ }
61
+
62
+ export class Label extends ElementProto {
63
+ constructor(props: $.Props, layout?: $.Layout<Label>) {
64
+ super('label', props, layout);
65
+ }
66
+
67
+ override build(children?: boolean): this {
68
+ let selector = this.findAbove(proto => is(proto, RadioItem));
69
+ if (selector) this.attr('for', selector.inputId)
70
+ return super.build(children);
71
+ }
72
+ }
73
+
74
+ const randomIdMap = new Map<string, Set<string>>();
75
+ function randomIdUnique() {
76
+
77
+ }
@@ -0,0 +1,11 @@
1
+ import { ElementProto } from "@amateras/core";
2
+
3
+ export class Slide extends ElementProto {
4
+ constructor(props: $.Props, layout?: $.Layout<Slide>) {
5
+ super('slide', props, layout);
6
+ }
7
+
8
+ static {
9
+ $.style(Slide, 'slide{display:block;height:100%;width:100%;position:absolute}')
10
+ }
11
+ }