southpaw 0.2.7

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 (169) hide show
  1. package/README.md +51 -0
  2. package/api/client.ts +83 -0
  3. package/api/helpers/fs.ts +98 -0
  4. package/api/helpers/stream.ts +6 -0
  5. package/api/index.ts +50 -0
  6. package/api/server.ts +566 -0
  7. package/constants/fonts.ts +1583 -0
  8. package/constants/icons.ts +2124 -0
  9. package/constants/langs.ts +2592 -0
  10. package/constants/ssr-loading.tsx +17 -0
  11. package/contexts/dev.tsx +124 -0
  12. package/hooks/useDebounceCb.ts +21 -0
  13. package/modules/app.tsx +102 -0
  14. package/modules/behaviors.ts +161 -0
  15. package/modules/component.tsx +483 -0
  16. package/modules/config/index.ts +8 -0
  17. package/modules/database.ts +25 -0
  18. package/modules/document.tsx +23 -0
  19. package/modules/error.tsx +16 -0
  20. package/modules/page/autozoom.tsx +92 -0
  21. package/modules/page/browser.tsx +166 -0
  22. package/modules/page/index.tsx +327 -0
  23. package/modules/page/insert-modal.tsx +225 -0
  24. package/modules/page/inspect/index.tsx +237 -0
  25. package/modules/page/inspect/wrapper.tsx +88 -0
  26. package/modules/page/knobs/action.tsx +58 -0
  27. package/modules/page/knobs/color.tsx +78 -0
  28. package/modules/page/knobs/helpers.ts +71 -0
  29. package/modules/page/knobs/icon.tsx +85 -0
  30. package/modules/page/knobs/linker.tsx +188 -0
  31. package/modules/page/knobs/list-json.tsx +149 -0
  32. package/modules/page/knobs/list-text.tsx +129 -0
  33. package/modules/page/knobs/number.tsx +84 -0
  34. package/modules/page/knobs/segmented.tsx +81 -0
  35. package/modules/page/knobs/select.tsx +89 -0
  36. package/modules/page/knobs/size.tsx +106 -0
  37. package/modules/page/knobs/switch.tsx +75 -0
  38. package/modules/page/knobs/text.tsx +93 -0
  39. package/modules/page/left-tabs.tsx +36 -0
  40. package/modules/page/right-tabs.tsx +31 -0
  41. package/modules/page/seo.tsx +69 -0
  42. package/modules/page/tree.tsx +860 -0
  43. package/modules/state.ts +787 -0
  44. package/modules/store.ts +37 -0
  45. package/modules/theme.ts +105 -0
  46. package/modules/translations.ts +15 -0
  47. package/package.json +68 -0
  48. package/scripts/bin.js +607 -0
  49. package/scripts/test.js +145 -0
  50. package/themes/base.tsx +8962 -0
  51. package/themes/index.ts +4 -0
  52. package/themes/m2.ts +39 -0
  53. package/themes/newspaper.ts +39 -0
  54. package/types/api.ts +57 -0
  55. package/types/fiber_node.ts +69 -0
  56. package/types/react_element.ts +14 -0
  57. package/ui/accordion.tsx +70 -0
  58. package/ui/actionicon.tsx +41 -0
  59. package/ui/affix.tsx +14 -0
  60. package/ui/alert.tsx +37 -0
  61. package/ui/anchor.tsx +52 -0
  62. package/ui/arrow.tsx +116 -0
  63. package/ui/aspectratio.tsx +21 -0
  64. package/ui/autocomplete.tsx +34 -0
  65. package/ui/avatar.tsx +36 -0
  66. package/ui/backgroundimage.tsx +16 -0
  67. package/ui/badge.tsx +28 -0
  68. package/ui/blockquote.tsx +15 -0
  69. package/ui/blocks/faq.tsx +145 -0
  70. package/ui/blocks/features.tsx +167 -0
  71. package/ui/blocks/footer.tsx +182 -0
  72. package/ui/blocks/herobanner.tsx +391 -0
  73. package/ui/blocks/index.ts +44 -0
  74. package/ui/blocks/leftnav.tsx +141 -0
  75. package/ui/blocks/pricing.tsx +210 -0
  76. package/ui/blocks/testimonials.tsx +149 -0
  77. package/ui/blocks/uidocs.tsx +1429 -0
  78. package/ui/box.tsx +27 -0
  79. package/ui/breadcrumbs.tsx +33 -0
  80. package/ui/burger.tsx +23 -0
  81. package/ui/button.tsx +85 -0
  82. package/ui/card.tsx +28 -0
  83. package/ui/center.tsx +26 -0
  84. package/ui/checkbox.tsx +35 -0
  85. package/ui/chip.tsx +32 -0
  86. package/ui/closebutton.tsx +15 -0
  87. package/ui/code.tsx +104 -0
  88. package/ui/collapse.tsx +16 -0
  89. package/ui/colorinput.tsx +32 -0
  90. package/ui/colorpicker.tsx +26 -0
  91. package/ui/colorswatch.tsx +17 -0
  92. package/ui/combobox.tsx +80 -0
  93. package/ui/container.tsx +27 -0
  94. package/ui/dialog.tsx +15 -0
  95. package/ui/divider.tsx +58 -0
  96. package/ui/drawer.tsx +28 -0
  97. package/ui/fieldset.tsx +15 -0
  98. package/ui/filebutton.tsx +23 -0
  99. package/ui/fileinput.tsx +36 -0
  100. package/ui/flex.tsx +139 -0
  101. package/ui/focustrap.tsx +15 -0
  102. package/ui/form.tsx +26 -0
  103. package/ui/grid.tsx +69 -0
  104. package/ui/highlight.tsx +15 -0
  105. package/ui/hoverable.tsx +66 -0
  106. package/ui/hovercard.tsx +45 -0
  107. package/ui/icon.tsx +76 -0
  108. package/ui/image.tsx +32 -0
  109. package/ui/index.ts +1355 -0
  110. package/ui/indicator.tsx +42 -0
  111. package/ui/infobox.tsx +200 -0
  112. package/ui/input.tsx +18 -0
  113. package/ui/inviewport.tsx +23 -0
  114. package/ui/jsoninput.tsx +35 -0
  115. package/ui/kbd.tsx +20 -0
  116. package/ui/list.tsx +33 -0
  117. package/ui/loader.tsx +26 -0
  118. package/ui/loadingoverlay.tsx +22 -0
  119. package/ui/mark.tsx +15 -0
  120. package/ui/menu.tsx +89 -0
  121. package/ui/modal.tsx +27 -0
  122. package/ui/multiselect.tsx +42 -0
  123. package/ui/nativeselect.tsx +17 -0
  124. package/ui/navlink.tsx +40 -0
  125. package/ui/notification.tsx +25 -0
  126. package/ui/numberformatter.tsx +15 -0
  127. package/ui/numberinput.tsx +37 -0
  128. package/ui/overlay.tsx +15 -0
  129. package/ui/pagination.tsx +21 -0
  130. package/ui/paper.tsx +33 -0
  131. package/ui/passwordinput.tsx +36 -0
  132. package/ui/pill.tsx +17 -0
  133. package/ui/pillsinput.tsx +37 -0
  134. package/ui/pininput.tsx +30 -0
  135. package/ui/popover.tsx +43 -0
  136. package/ui/portal.tsx +15 -0
  137. package/ui/progress.tsx +49 -0
  138. package/ui/props.ts +91 -0
  139. package/ui/propsDef.ts +38 -0
  140. package/ui/radio.tsx +30 -0
  141. package/ui/rating.tsx +25 -0
  142. package/ui/ringprogress.tsx +17 -0
  143. package/ui/scrollarea.tsx +27 -0
  144. package/ui/segmentedcontrol.tsx +38 -0
  145. package/ui/select.tsx +43 -0
  146. package/ui/simplegrid.tsx +23 -0
  147. package/ui/skeleton.tsx +15 -0
  148. package/ui/slider.tsx +31 -0
  149. package/ui/space.tsx +19 -0
  150. package/ui/spoiler.tsx +23 -0
  151. package/ui/stepper.tsx +63 -0
  152. package/ui/switch.tsx +29 -0
  153. package/ui/table.tsx +93 -0
  154. package/ui/tabs.tsx +80 -0
  155. package/ui/tagsinput.tsx +19 -0
  156. package/ui/terminal.tsx +44 -0
  157. package/ui/text.tsx +168 -0
  158. package/ui/textarea.tsx +36 -0
  159. package/ui/textinput.tsx +34 -0
  160. package/ui/timeline.tsx +30 -0
  161. package/ui/title.tsx +42 -0
  162. package/ui/tooltip.tsx +32 -0
  163. package/ui/transition.tsx +21 -0
  164. package/ui/tree.tsx +42 -0
  165. package/ui/typographystylesprovider.tsx +20 -0
  166. package/ui/unstyledbutton.tsx +18 -0
  167. package/ui/video.tsx +16 -0
  168. package/ui/visuallyhidden.tsx +15 -0
  169. package/utils/index.ts +76 -0
@@ -0,0 +1,483 @@
1
+ /* TODO: Refactor typings */
2
+ import {
3
+ FunctionComponent,
4
+ RefObject,
5
+ ReactNode,
6
+ ReactElement,
7
+ useEffect,
8
+ useMemo,
9
+ useReducer,
10
+ useRef,
11
+ useState,
12
+ createRef,
13
+ memo,
14
+ cloneElement,
15
+ } from "react";
16
+ import ObservableSlim from "./state";
17
+ import isEqual from "react-fast-compare";
18
+ import { _ErrorBoundary } from "./error";
19
+ import { NextRouter, useRouter } from "next/router";
20
+ import { ComboboxData, SegmentedControlItem } from "@mantine/core";
21
+ import { CommonProps, MOUNT_ANIMATIONS } from "../ui/props";
22
+
23
+ type PropsType<T = Record<string, unknown>> = CommonProps & T;
24
+ type StateType = Record<string, unknown>;
25
+ type RefsType = Record<string, unknown>;
26
+ type PropDef = {
27
+ label: string;
28
+ } & (
29
+ | {
30
+ type: "color";
31
+ }
32
+ | {
33
+ type: "number";
34
+ min?: number;
35
+ max?: number;
36
+ step?: number;
37
+ }
38
+ | {
39
+ type: "size";
40
+ min?: number;
41
+ max?: number;
42
+ step?: number;
43
+ }
44
+ | {
45
+ type: "icon";
46
+ }
47
+ | {
48
+ type: "segmented";
49
+ options: SegmentedControlItem[];
50
+ }
51
+ | {
52
+ type: "select";
53
+ options: ComboboxData;
54
+ }
55
+ | {
56
+ type: "switch";
57
+ }
58
+ | {
59
+ type: "text";
60
+ multiline?: boolean;
61
+ }
62
+ | {
63
+ type: "list-text";
64
+ }
65
+ | {
66
+ type: "list-json";
67
+ }
68
+ | {
69
+ type: "action";
70
+ }
71
+ );
72
+ export type PropsDef = Record<string, PropDef>;
73
+
74
+ type ComputedType<P, S> = Record<
75
+ string,
76
+ (
77
+ this: {
78
+ computed: any;
79
+ state: S;
80
+ props: P;
81
+ router: NextRouter;
82
+ el: HTMLElement | null;
83
+ },
84
+ ...args: unknown[]
85
+ ) => unknown
86
+ >;
87
+
88
+ type MethodsType<P, S, C extends ComputedType<P, S>, R> = Record<
89
+ string,
90
+ (
91
+ this: {
92
+ state: S;
93
+ props: P;
94
+ computed: {
95
+ [KC in keyof C]: ReturnType<C[KC]>;
96
+ };
97
+ refs: {
98
+ [K in keyof R]: {
99
+ current: R[K];
100
+ };
101
+ };
102
+ methods: Record<string, (...args: any[]) => any>;
103
+ router: NextRouter;
104
+ devError: (error: ReactElement) => void;
105
+ el: HTMLElement | null;
106
+ },
107
+ ...args: any[]
108
+ ) => any
109
+ >;
110
+
111
+ type RenderThisType<
112
+ P,
113
+ S,
114
+ C extends ComputedType<P, S>,
115
+ R,
116
+ M extends MethodsType<P, S, C, R>,
117
+ > = {
118
+ methods: { [K in keyof M]: (...args: Parameters<M[K]>) => ReturnType<M[K]> };
119
+ state: S;
120
+ props: P & { mount_animation?: any };
121
+ refs: { [K in keyof R]: RefObject<R[K]> };
122
+ router: NextRouter;
123
+ computed: {
124
+ [KC in keyof C]: ReturnType<C[KC]>;
125
+ };
126
+ devError: (error: ReactElement) => void;
127
+ el: HTMLElement | null;
128
+ };
129
+
130
+ export interface ComponentParams<
131
+ P = PropsType,
132
+ S = StateType,
133
+ C extends ComputedType<P, S> = ComputedType<P, S>,
134
+ R = RefsType,
135
+ M extends MethodsType<P, S, C, R> = MethodsType<P, S, C, R>,
136
+ > {
137
+ name: string;
138
+ description: string;
139
+ props?: P;
140
+ propsDef?: PropsDef;
141
+ state?: S;
142
+ refs?: R;
143
+ methods?: M;
144
+ computed?: C;
145
+ lifecycle?: {
146
+ onMount?: (this: RenderThisType<P, S, C, R, M>) => void;
147
+ onDomMutation?: (
148
+ this: RenderThisType<P, S, C, R, M>,
149
+ mutation: MutationRecord,
150
+ ) => void;
151
+ onUnmount?: (this: RenderThisType<P, S, C, R, M>) => void;
152
+ onRender?: (this: RenderThisType<P, S, C, R, M>) => void;
153
+ onMountAnimationEnd?: (this: RenderThisType<P, S, C, R, M>) => void;
154
+ onRouterReady?: (this: RenderThisType<P, S, C, R, M>) => void;
155
+ onPropChange?: (
156
+ this: RenderThisType<P, S, C, R, M>,
157
+ propName: keyof P,
158
+ previousProp: unknown,
159
+ currentProp: unknown,
160
+ ) => void;
161
+ };
162
+ render: (this: RenderThisType<P, S, C, R, M>) => ReactNode;
163
+ }
164
+
165
+ export type ComponentReturnType<P> = FunctionComponent<P> & {
166
+ // Optional metadata used by the Southpaw dev tools
167
+ params?: ComponentParams<P, any, any, any, any>;
168
+ };
169
+
170
+ export const create_component = <
171
+ P = PropsType,
172
+ S = StateType,
173
+ C extends ComputedType<P, S> = ComputedType<P, S>,
174
+ R = RefsType,
175
+ M extends MethodsType<P, S, C, R> = MethodsType<P, S, C, R>,
176
+ >(
177
+ _params: ComponentParams<P, S, C, R, M>,
178
+ ): ComponentReturnType<P> => {
179
+ const params = _params;
180
+ params.propsDef = params.propsDef || {};
181
+ const defaultMountAnimation = {
182
+ mount_animation: MOUNT_ANIMATIONS.fade,
183
+ };
184
+ const Fn = memo((_props: P) => {
185
+ const ref = useRef<HTMLDivElement>(null);
186
+ const router = useRouter();
187
+ const [errorHtml, setErrorHtml] = useState("");
188
+ const defaultProps = (params.props || {}) as P;
189
+ const el = ref.current;
190
+
191
+ const props = {
192
+ ...defaultMountAnimation,
193
+ ...defaultProps,
194
+ ...(_props || {}),
195
+ } as P;
196
+
197
+ const refs = useMemo(
198
+ () =>
199
+ Object.keys(params.refs || {}).reduce(
200
+ (acc, refName) => {
201
+ acc[refName] = createRef();
202
+ return acc;
203
+ },
204
+ {} as Record<string, ReturnType<typeof createRef>>,
205
+ ),
206
+ [],
207
+ );
208
+ const propsRef = useRef<Record<string, unknown>>(
209
+ props as unknown as Record<string, unknown>,
210
+ );
211
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
212
+ const state = useMemo(() => {
213
+ return ObservableSlim.create(
214
+ JSON.parse(JSON.stringify(params.state || {})),
215
+ true,
216
+ function () {
217
+ forceUpdate();
218
+ },
219
+ );
220
+ }, []);
221
+
222
+ const devError = (error: ReactElement) => {
223
+ import("react-dom/server").then(({ renderToString }) => {
224
+ setErrorHtml(renderToString(error));
225
+ });
226
+ };
227
+
228
+ const computed = Object.keys(
229
+ (params.computed || {}) as Record<string, unknown>,
230
+ ).reduce(
231
+ (acc, computedName) => {
232
+ const computedFn = (
233
+ params.computed as Record<
234
+ string,
235
+ (this: {
236
+ state: S;
237
+ props: P;
238
+ router: NextRouter;
239
+ el: HTMLElement | null;
240
+ computed: any;
241
+ }) => unknown
242
+ >
243
+ )[computedName];
244
+
245
+ if (typeof computedFn === "function") {
246
+ // Runtime binding – types are validated at the edge of ComponentParams
247
+ acc[computedName as keyof C] = computedFn.bind({
248
+ state: state as unknown as S,
249
+ props,
250
+ router,
251
+ el,
252
+ computed: acc,
253
+ })() as ReturnType<C[keyof C]>;
254
+ }
255
+
256
+ return acc;
257
+ },
258
+ {} as { [KC in keyof C]: ReturnType<C[KC]> },
259
+ );
260
+
261
+ const methods = Object.keys(
262
+ (params.methods || {}) as Record<string, unknown>,
263
+ ).reduce(
264
+ (acc, methodName) => {
265
+ const method = (
266
+ params.methods as Record<string, (...args: any[]) => any>
267
+ )[methodName];
268
+
269
+ if (typeof method === "function") {
270
+ // Runtime binding – types are validated at the edge of ComponentParams
271
+ (acc as Record<string, (...args: any[]) => any>)[methodName] =
272
+ method.bind({
273
+ state,
274
+ props,
275
+ computed,
276
+ refs,
277
+ router,
278
+ methods: acc,
279
+ devError,
280
+ el,
281
+ });
282
+ }
283
+
284
+ return acc;
285
+ },
286
+ {} as Record<string, (...args: any[]) => any>,
287
+ ) as {
288
+ [K in keyof M]: (...args: Parameters<M[K]>) => ReturnType<M[K]>;
289
+ };
290
+
291
+ useEffect(() => {
292
+ const lifecycleBindings = {
293
+ methods,
294
+ state,
295
+ computed,
296
+ props,
297
+ refs,
298
+ router,
299
+ el,
300
+ };
301
+ (
302
+ params.lifecycle?.onMount as
303
+ | ((this: typeof lifecycleBindings) => void)
304
+ | undefined
305
+ )?.bind(lifecycleBindings)();
306
+ return () => {
307
+ (
308
+ params.lifecycle?.onUnmount as
309
+ | ((this: typeof lifecycleBindings) => void)
310
+ | undefined
311
+ )?.bind(lifecycleBindings)();
312
+ };
313
+ }, []);
314
+
315
+ useEffect(() => {
316
+ const previousProps = propsRef.current;
317
+ const currentProps = props as unknown as Record<string, unknown>;
318
+ for (const propName of Object.keys(currentProps)) {
319
+ const previousProp = previousProps[propName];
320
+ const currentProp = currentProps[propName];
321
+ if (!isEqual(previousProp, currentProp)) {
322
+ const lifecycleBindings = {
323
+ methods,
324
+ state,
325
+ computed,
326
+ props,
327
+ refs,
328
+ router,
329
+ devError,
330
+ el,
331
+ };
332
+ (
333
+ params.lifecycle?.onPropChange as
334
+ | ((
335
+ this: typeof lifecycleBindings,
336
+ propName: string,
337
+ previousProp: unknown,
338
+ currentProp: unknown,
339
+ ) => void)
340
+ | undefined
341
+ )?.bind(lifecycleBindings)(propName, previousProp, currentProp);
342
+ propsRef.current[propName] = currentProp;
343
+ }
344
+ }
345
+ }, [props]);
346
+
347
+ useEffect(() => {
348
+ setTimeout(() => {
349
+ const lifecycleBindings = {
350
+ methods,
351
+ state,
352
+ computed,
353
+ props,
354
+ el,
355
+ };
356
+ (
357
+ params.lifecycle?.onRender as
358
+ | ((this: typeof lifecycleBindings) => void)
359
+ | undefined
360
+ )?.bind(lifecycleBindings)();
361
+ }, 25);
362
+ }, []);
363
+
364
+ useEffect(() => {
365
+ if (router.isReady) {
366
+ const lifecycleBindings = {
367
+ methods,
368
+ state,
369
+ computed,
370
+ props,
371
+ el,
372
+ };
373
+ (
374
+ params.lifecycle?.onRouterReady as
375
+ | ((this: typeof lifecycleBindings) => void)
376
+ | undefined
377
+ )?.bind(lifecycleBindings)();
378
+ }
379
+ }, [router.isReady]);
380
+
381
+ const mutationCallback: MutationCallback = (mutationList) => {
382
+ const lifecycleBindings = {
383
+ methods,
384
+ state,
385
+ computed,
386
+ props,
387
+ refs,
388
+ router,
389
+ el,
390
+ };
391
+ for (const mutation of mutationList) {
392
+ (
393
+ params.lifecycle?.onDomMutation as
394
+ | ((
395
+ this: typeof lifecycleBindings,
396
+ mutation: MutationRecord,
397
+ ) => void)
398
+ | undefined
399
+ )?.bind(lifecycleBindings)(mutation);
400
+ }
401
+ };
402
+
403
+ useEffect(() => {
404
+ const observer = new MutationObserver(mutationCallback);
405
+ if (ref.current instanceof HTMLElement) {
406
+ observer.observe(ref.current, {
407
+ attributes: true,
408
+ childList: true,
409
+ subtree: true,
410
+ });
411
+ const propsWithAnimation = props as P & CommonProps;
412
+ if (propsWithAnimation.mount_animation) {
413
+ let animation: Animation;
414
+ switch (typeof propsWithAnimation.mount_animation) {
415
+ case "string": {
416
+ const def = MOUNT_ANIMATIONS[propsWithAnimation.mount_animation];
417
+ animation = ref.current.animate(
418
+ def.keyframes as unknown as PropertyIndexedKeyframes,
419
+ def.options,
420
+ );
421
+ break;
422
+ }
423
+
424
+ default:
425
+ animation = ref.current.animate(
426
+ propsWithAnimation.mount_animation.keyframes,
427
+ propsWithAnimation.mount_animation.options,
428
+ );
429
+ break;
430
+ }
431
+ animation.onfinish = () => {
432
+ const lifecycleBindings = {
433
+ methods,
434
+ state,
435
+ computed,
436
+ props,
437
+ refs,
438
+ router,
439
+ el,
440
+ };
441
+ (
442
+ params.lifecycle?.onMountAnimationEnd as
443
+ | ((this: typeof lifecycleBindings) => void)
444
+ | undefined
445
+ )?.bind(lifecycleBindings)();
446
+ };
447
+ }
448
+ }
449
+ return () => {
450
+ observer.disconnect();
451
+ };
452
+ }, [ref]);
453
+
454
+ const bindings = {
455
+ methods,
456
+ state,
457
+ computed,
458
+ props,
459
+ refs,
460
+ router,
461
+ devError,
462
+ el,
463
+ };
464
+
465
+ const render = params.render.bind(
466
+ bindings as RenderThisType<P, S, C, R, M>,
467
+ ) as FunctionComponent<P>;
468
+ if (errorHtml) {
469
+ return <_ErrorBoundary errorHtml={errorHtml} />;
470
+ }
471
+ const jsx = render(props);
472
+ if (!jsx) {
473
+ return null;
474
+ }
475
+ return jsx;
476
+ return cloneElement(jsx as ReactElement, {
477
+ ref,
478
+ });
479
+ }) as ComponentReturnType<P>;
480
+ Fn.displayName = params.name;
481
+ Fn.params = params;
482
+ return Fn;
483
+ };
@@ -0,0 +1,8 @@
1
+ import { NextConfig } from "next";
2
+
3
+ export const create_config = (): NextConfig => {
4
+ return {
5
+ reactStrictMode: false,
6
+ devIndicators: false,
7
+ };
8
+ };
@@ -0,0 +1,25 @@
1
+ import Dexie, { Table } from "dexie";
2
+
3
+ type StoresType = Record<string, Record<string, unknown>>;
4
+ type Params<S extends StoresType> = {
5
+ name: string;
6
+ version: number;
7
+ stores: {
8
+ [K in keyof S]: Array<keyof S[K]>;
9
+ };
10
+ };
11
+ export const create_database = <S extends StoresType>(params: Params<S>) => {
12
+ const db = new Dexie(params.name) as Dexie & { [K in keyof S]: Table<S[K]> };
13
+ const stores = Object.keys(params.stores).reduce(
14
+ (acc, storeName) => {
15
+ acc[storeName as keyof S] = ["++id", ...params.stores[storeName]].join(
16
+ ","
17
+ );
18
+ return acc;
19
+ },
20
+ {} as Record<keyof S, string>
21
+ );
22
+ db.version(params.version).stores(stores);
23
+ db.open();
24
+ return db;
25
+ };
@@ -0,0 +1,23 @@
1
+ /* eslint-disable @next/next/no-page-custom-font */
2
+ /* eslint-disable @next/next/google-font-display */
3
+ import { ColorSchemeScript } from "@mantine/core";
4
+ // eslint-disable-next-line @next/next/no-document-import-in-page
5
+ import { Html, Head, Main, NextScript } from "next/document";
6
+
7
+ export const create_document = () => {
8
+ const Fn = () => {
9
+ return (
10
+ <Html lang="en">
11
+ <Head>
12
+ <ColorSchemeScript defaultColorScheme="auto" />
13
+ </Head>
14
+ <body>
15
+ <Main />
16
+ <NextScript />
17
+ </body>
18
+ </Html>
19
+ );
20
+ };
21
+ Fn.displayName = "Document";
22
+ return Fn;
23
+ };
@@ -0,0 +1,16 @@
1
+ export const _ErrorBoundary = ({ errorHtml }: { errorHtml: string }) => {
2
+ return (
3
+ <div
4
+ style={{
5
+ width: "100%",
6
+ height: "100%",
7
+ padding: 20,
8
+ boxShadow: "inset 0px 0px 0px 5px rgba(255,0,0,0.5)",
9
+ background: "rgba(255,0,0,0.1)",
10
+ fontFamily: "monospace",
11
+ fontWeight: "bolder",
12
+ }}
13
+ dangerouslySetInnerHTML={{ __html: errorHtml }}
14
+ />
15
+ );
16
+ };
@@ -0,0 +1,92 @@
1
+ import React, { useRef, useState, useEffect, ReactNode } from "react";
2
+
3
+ interface AutoZoomContainerProps {
4
+ children: ReactNode;
5
+ initialScale?: number;
6
+ maxScale?: number;
7
+ step?: number;
8
+ }
9
+
10
+ export const AutoZoomContainer: React.FC<AutoZoomContainerProps> = ({
11
+ children,
12
+ initialScale = 0.05,
13
+ maxScale = 1,
14
+ step = 0.05,
15
+ }) => {
16
+ const containerRef = useRef<HTMLDivElement>(null);
17
+ const childRef = useRef<HTMLDivElement>(null);
18
+ const [scale, setScale] = useState<number>(initialScale);
19
+
20
+ const checkFitAndAdjust = () => {
21
+ const container = containerRef.current;
22
+ const child = childRef.current;
23
+
24
+ if (!container || !child) return;
25
+
26
+ const containerRect = container.getBoundingClientRect();
27
+ const childRect = child.getBoundingClientRect();
28
+
29
+ // Add tolerance to prevent oscillation
30
+ const tolerance = 2; // pixels
31
+ const widthFits = childRect.width <= containerRect.width + tolerance;
32
+ const heightFits = childRect.height <= containerRect.height + tolerance;
33
+ const fits = widthFits && heightFits;
34
+
35
+ // Calculate how much we're overflowing (if at all)
36
+ const widthRatio = containerRect.width / childRect.width;
37
+ const heightRatio = containerRect.height / childRect.height;
38
+ const minRatio = Math.min(widthRatio, heightRatio);
39
+
40
+ if (!fits && scale > initialScale) {
41
+ // Content doesn't fit, zoom out - but use calculated ratio for better convergence
42
+ const targetScale = Math.max(scale * minRatio * 0.95, initialScale); // 0.95 for safety margin
43
+ setScale(targetScale);
44
+ } else if (fits && scale < maxScale) {
45
+ // Content fits, try to zoom in more gradually
46
+ const nextScale = Math.min(scale + step, maxScale);
47
+ // But don't zoom in if we're very close to not fitting
48
+ if (minRatio > 1.1) {
49
+ // Only zoom in if we have comfortable margin
50
+ setScale(nextScale);
51
+ }
52
+ }
53
+ };
54
+
55
+ useEffect(() => {
56
+ // Initial check after mount
57
+ const timeoutId = setTimeout(checkFitAndAdjust, 0);
58
+ return () => clearTimeout(timeoutId);
59
+ }, []);
60
+
61
+ useEffect(() => {
62
+ // React to scale change
63
+ const id = requestAnimationFrame(checkFitAndAdjust);
64
+ return () => cancelAnimationFrame(id);
65
+ }, [scale]);
66
+
67
+ return (
68
+ <div
69
+ ref={containerRef}
70
+ style={{
71
+ width: "100%",
72
+ height: "100%",
73
+ overflow: "hidden",
74
+ position: "relative",
75
+ display: "flex",
76
+ alignItems: "center",
77
+ justifyContent: "center",
78
+ }}
79
+ >
80
+ <div
81
+ ref={childRef}
82
+ style={{
83
+ zoom: scale,
84
+ transformOrigin: "top left",
85
+ display: "inline-block",
86
+ }}
87
+ >
88
+ {children}
89
+ </div>
90
+ </div>
91
+ );
92
+ };