akanjs 2.2.7-rc.3 → 2.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.
- package/CHANGELOG.md +3 -5
- package/client/makePageProto.tsx +2 -5
- package/common/fileUpload.ts +1 -1
- package/common/index.ts +1 -5
- package/constant/getDefault.ts +1 -1
- package/fetch/client/fetchClient.ts +11 -3
- package/fetch/serializer/fetch.serializer.ts +0 -1
- package/package.json +5 -1
- package/service/injectInfo.ts +12 -49
- package/service/predefinedAdaptor/cache.adaptor.ts +0 -13
- package/service/predefinedAdaptor/database.adaptor.ts +16 -74
- package/service/predefinedAdaptor/solidCache.adaptor.ts +0 -23
- package/signal/serializer/fetch.serializer.ts +0 -1
- package/signal/types.ts +0 -3
- package/store/action.ts +3 -15
- package/types/common/fileUpload.d.ts +1 -1
- package/types/common/index.d.ts +1 -1
- package/types/service/injectInfo.d.ts +2 -8
- package/types/service/predefinedAdaptor/cache.adaptor.d.ts +0 -6
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -3
- package/types/service/predefinedAdaptor/solidCache.adaptor.d.ts +0 -3
- package/types/signal/types.d.ts +0 -3
- package/types/ui/Dialog/Modal.d.ts +1 -1
- package/types/ui/Dialog/index.d.ts +1 -1
- package/types/ui/Modal.d.ts +12 -1
- package/types/ui/System/Client.d.ts +4 -4
- package/ui/Dialog/Modal.tsx +70 -181
- package/ui/Dialog/Provider.tsx +6 -3
- package/ui/Modal.tsx +44 -0
- package/ui/System/Client.tsx +62 -20
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# akanjs
|
|
2
2
|
|
|
3
|
-
## 2.2.
|
|
3
|
+
## 2.2.7
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
- 5cdb05e: reverse dependency of file upload api
|
|
10
|
-
- a7da50e: remove dependency from radix dialog
|
|
7
|
+
- fix: base dictionary translation failed in some cases
|
|
8
|
+
- fix: file upload contract workaround on shared Field.Img component
|
|
11
9
|
|
|
12
10
|
## 2.2.3
|
|
13
11
|
|
package/client/makePageProto.tsx
CHANGED
|
@@ -25,11 +25,8 @@ const getPageInfo = (): { locale: string; path: string } => {
|
|
|
25
25
|
const localeSet = new Set(locales);
|
|
26
26
|
if (getEnv().side !== "server") {
|
|
27
27
|
const [, firstSegment = "", ...rest] = window.location.pathname.split("/");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const activeLocale = Translator.getActiveLocale();
|
|
31
|
-
const locale = activeLocale ?? (hasLocalePrefix ? firstSegment : defaultLocale);
|
|
32
|
-
return { locale, path: hasLocalePrefix ? `/${rest.join("/")}` : window.location.pathname };
|
|
28
|
+
if (localeSet.has(firstSegment)) return { locale: firstSegment, path: `/${rest.join("/")}` };
|
|
29
|
+
return { locale: defaultLocale, path: window.location.pathname };
|
|
33
30
|
}
|
|
34
31
|
const h = headers();
|
|
35
32
|
|
package/common/fileUpload.ts
CHANGED
package/common/index.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
export { applyMixins } from "./applyMixins";
|
|
2
2
|
export { capitalize } from "./capitalize";
|
|
3
3
|
export { deepObjectify } from "./deepObjectify";
|
|
4
|
-
export
|
|
5
|
-
type FileUploadCapability,
|
|
6
|
-
fileUploadContract,
|
|
7
|
-
resolveFileUploadCapability,
|
|
8
|
-
} from "./fileUpload";
|
|
4
|
+
export * from "./fileUpload";
|
|
9
5
|
export { formatNumber } from "./formatNumber";
|
|
10
6
|
export { formatPhone } from "./formatPhone";
|
|
11
7
|
export { getAllPropertyDescriptors } from "./getAllPropertyDescriptors";
|
package/constant/getDefault.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const getDefault = <T>(fieldObj: FieldObject): DefaultOf<T> => {
|
|
|
6
6
|
const result: Record<string, unknown> = {};
|
|
7
7
|
for (const [key, field] of Object.entries(fieldObj)) {
|
|
8
8
|
if (field.fieldType === "hidden") result[key] = null;
|
|
9
|
-
else if (field.default
|
|
9
|
+
else if (field.default) {
|
|
10
10
|
if (typeof field.default === "function") result[key] = (field.default as () => object)();
|
|
11
11
|
else result[key] = field.default as object;
|
|
12
12
|
} else if (field.isArray) result[key] = [];
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { DataList, getEnv, PrimitiveRegistry, type PromiseOrObject } from "akanjs/base";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
capitalize,
|
|
4
|
+
type FetchPolicy,
|
|
5
|
+
type FileUploadSerializedSignal,
|
|
6
|
+
fileUploadContract,
|
|
7
|
+
Logger,
|
|
8
|
+
resolveFileUploadCapability,
|
|
9
|
+
} from "akanjs/common";
|
|
3
10
|
import { type BaseInsight, type BaseObject, ConstantRegistry, deserialize, serialize } from "akanjs/constant";
|
|
4
11
|
import type {
|
|
5
12
|
DatabaseSignal,
|
|
@@ -472,7 +479,7 @@ export class FetchClient {
|
|
|
472
479
|
names.addModelFiles,
|
|
473
480
|
() =>
|
|
474
481
|
(async (fileList: FileList, parentId?: string, option?: FetchPolicy) => {
|
|
475
|
-
const cap = resolveFileUploadCapability(this.serializedSignal);
|
|
482
|
+
const cap = resolveFileUploadCapability(this.serializedSignal as Record<string, FileUploadSerializedSignal>);
|
|
476
483
|
const endpoint = cap ? this.serializedSignal[cap.refName]?.endpoint[cap.endpointKey] : undefined;
|
|
477
484
|
if (!cap || !endpoint)
|
|
478
485
|
throw new Error(
|
|
@@ -691,7 +698,8 @@ export class FetchClient {
|
|
|
691
698
|
const sig = {} as any;
|
|
692
699
|
Object.entries(serializedSignal).forEach(([refName, serializedSignal]) => {
|
|
693
700
|
if (!serializedSignal.slice) return;
|
|
694
|
-
const cnst = ConstantRegistry.getDatabase(refName);
|
|
701
|
+
const cnst = ConstantRegistry.getDatabase(refName, { allowEmpty: true });
|
|
702
|
+
if (!cnst) return;
|
|
695
703
|
const slices = Object.entries(serializedSignal.slice).map(([suffix, serializedSlice]) => {
|
|
696
704
|
const sliceName = `${refName}${capitalize(suffix)}`;
|
|
697
705
|
proxy.slice[sliceName] = { refName, sliceName, argLength: serializedSlice.args.length };
|
|
@@ -67,7 +67,6 @@ export class FetchSerializer {
|
|
|
67
67
|
...(endpointInfo.signalOption.globalPrefix !== undefined
|
|
68
68
|
? { globalPrefix: endpointInfo.signalOption.globalPrefix }
|
|
69
69
|
: {}),
|
|
70
|
-
...(endpointInfo.signalOption.fileUpload ? { fileUpload: true } : {}),
|
|
71
70
|
...(guards?.length ? { guards } : {}),
|
|
72
71
|
};
|
|
73
72
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akanjs",
|
|
3
|
-
"version": "2.2.7
|
|
3
|
+
"version": "2.2.7",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -175,6 +175,7 @@
|
|
|
175
175
|
"@capgo/capacitor-updater": "^8.46.1",
|
|
176
176
|
"@libsql/client": "^0.17.3",
|
|
177
177
|
"@playwright/test": "^1.60.0",
|
|
178
|
+
"@radix-ui/react-dialog": "^1.1.15",
|
|
178
179
|
"@react-spring/web": "^10.1.0",
|
|
179
180
|
"@use-gesture/react": "^10.3.1",
|
|
180
181
|
"bullmq": "^5.76.10",
|
|
@@ -249,6 +250,9 @@
|
|
|
249
250
|
"@playwright/test": {
|
|
250
251
|
"optional": true
|
|
251
252
|
},
|
|
253
|
+
"@radix-ui/react-dialog": {
|
|
254
|
+
"optional": true
|
|
255
|
+
},
|
|
252
256
|
"@react-spring/web": {
|
|
253
257
|
"optional": true
|
|
254
258
|
},
|
package/service/injectInfo.ts
CHANGED
|
@@ -254,47 +254,20 @@ export class InjectInfo<
|
|
|
254
254
|
enumerable: true,
|
|
255
255
|
});
|
|
256
256
|
} else if (injectInfo.isMap) {
|
|
257
|
-
const topic = `akan:memory:${injectInfo.parentRefName}`;
|
|
258
|
-
const getter = injectInfo.get as unknown as (value: unknown) => unknown;
|
|
259
|
-
const setter = injectInfo.set as unknown as (value: unknown) => string | number | Buffer;
|
|
260
|
-
const get = async (key: string) => {
|
|
261
|
-
const value = await cacheAdaptor.hget(topic, propKey, key);
|
|
262
|
-
return value === undefined || value === null ? undefined : getter(value);
|
|
263
|
-
};
|
|
264
|
-
const set = async (key: string, value: unknown) => {
|
|
265
|
-
const setValue = setter(value);
|
|
266
|
-
await cacheAdaptor.hset(topic, propKey, key, setValue);
|
|
267
|
-
};
|
|
268
257
|
Object.defineProperty(instance, propKey, {
|
|
269
258
|
value: {
|
|
270
|
-
get
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
},
|
|
275
|
-
getOrInsert: async (key: string, value: unknown) => {
|
|
276
|
-
const existingValue = await get(key);
|
|
277
|
-
if (existingValue !== undefined) return existingValue;
|
|
278
|
-
await set(key, value);
|
|
279
|
-
return value;
|
|
280
|
-
},
|
|
281
|
-
getOrInsertComputed: async (key: string, compute: (key: string) => unknown | Promise<unknown>) => {
|
|
282
|
-
const existingValue = await get(key);
|
|
283
|
-
if (existingValue !== undefined) return existingValue;
|
|
284
|
-
const value = await compute(key);
|
|
285
|
-
await set(key, value);
|
|
286
|
-
return value;
|
|
287
|
-
},
|
|
288
|
-
keys: async () => await cacheAdaptor.hkeys(topic, propKey),
|
|
289
|
-
entries: async () => {
|
|
290
|
-
const entries = await cacheAdaptor.hentries(topic, propKey);
|
|
291
|
-
return entries.map(([key, value]) => [key, getter(value)]);
|
|
259
|
+
get: async (key: string) => {
|
|
260
|
+
const getter = injectInfo.get as unknown as (value: unknown) => unknown;
|
|
261
|
+
const value = await cacheAdaptor.hget(`akan:memory:${injectInfo.parentRefName}`, propKey, key);
|
|
262
|
+
return value === null ? value : getter(value);
|
|
292
263
|
},
|
|
293
|
-
|
|
294
|
-
|
|
264
|
+
set: async (key: string, value: unknown) => {
|
|
265
|
+
const setter = injectInfo.set as unknown as (value: unknown) => string | number | Buffer;
|
|
266
|
+
const setValue = setter(value);
|
|
267
|
+
await cacheAdaptor.hset(`akan:memory:${injectInfo.parentRefName}`, propKey, key, setValue);
|
|
295
268
|
},
|
|
296
|
-
|
|
297
|
-
await cacheAdaptor.
|
|
269
|
+
delete: async (key: string) => {
|
|
270
|
+
await cacheAdaptor.hdelete(`akan:memory:${injectInfo.parentRefName}`, propKey, key);
|
|
298
271
|
},
|
|
299
272
|
},
|
|
300
273
|
});
|
|
@@ -373,7 +346,6 @@ export const injectionBuilder = (parentRefName: string) => ({
|
|
|
373
346
|
const isMap = modelRef === Map;
|
|
374
347
|
if (isMap && !opts.of) throw new Error("of should be provided when modelRef is Map");
|
|
375
348
|
type FieldValue = never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>;
|
|
376
|
-
type MapFieldValue = never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>;
|
|
377
349
|
type IsNullable = DefaultValue extends never ? true : false;
|
|
378
350
|
type UseValue = IsNullable extends true ? FieldValue | null : FieldValue;
|
|
379
351
|
return new InjectInfo<
|
|
@@ -384,18 +356,9 @@ export const injectionBuilder = (parentRefName: string) => ({
|
|
|
384
356
|
: UseValue
|
|
385
357
|
: MapConstructor extends ValueRef
|
|
386
358
|
? {
|
|
387
|
-
get: (key: string) => Promise<
|
|
388
|
-
set: (key: string, value:
|
|
359
|
+
get: (key: string) => Promise<FieldToValue<MapValue>>;
|
|
360
|
+
set: (key: string, value: FieldToValue<MapValue>) => Promise<void>;
|
|
389
361
|
delete: (key: string) => Promise<void>;
|
|
390
|
-
getOrInsert: (key: string, value: MapFieldValue) => Promise<MapFieldValue>;
|
|
391
|
-
getOrInsertComputed: (
|
|
392
|
-
key: string,
|
|
393
|
-
compute: (key: string) => MapFieldValue | Promise<MapFieldValue>,
|
|
394
|
-
) => Promise<MapFieldValue>;
|
|
395
|
-
keys: () => Promise<string[]>;
|
|
396
|
-
entries: () => Promise<[string, MapFieldValue][]>;
|
|
397
|
-
forEach: (callback: (value: MapFieldValue, key: string) => void | Promise<void>) => Promise<void>;
|
|
398
|
-
clear: () => Promise<void>;
|
|
399
362
|
}
|
|
400
363
|
: { get: () => Promise<UseValue>; set: (value: UseValue) => Promise<void>; delete: () => Promise<void> },
|
|
401
364
|
never,
|
|
@@ -16,9 +16,6 @@ export interface CacheAdaptor {
|
|
|
16
16
|
): Promise<void>;
|
|
17
17
|
hget<T extends string | number | Buffer>(topic: string, key: string, subKey: string): Promise<T | undefined>;
|
|
18
18
|
hdelete(topic: string, key: string, subKey: string): Promise<void>;
|
|
19
|
-
hkeys(topic: string, key: string): Promise<string[]>;
|
|
20
|
-
hentries<T extends string | number | Buffer>(topic: string, key: string): Promise<[string, T][]>;
|
|
21
|
-
hclear(topic: string, key: string): Promise<void>;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
interface RedisEnv extends BaseEnv {
|
|
@@ -99,16 +96,6 @@ export class RedisCache
|
|
|
99
96
|
async hdelete(topic: string, key: string, subKey: string): Promise<void> {
|
|
100
97
|
await this.redis.hdel(`${topic}:${key}`, subKey);
|
|
101
98
|
}
|
|
102
|
-
async hkeys(topic: string, key: string): Promise<string[]> {
|
|
103
|
-
return await this.redis.hkeys(`${topic}:${key}`);
|
|
104
|
-
}
|
|
105
|
-
async hentries<T extends string | number | Buffer>(topic: string, key: string): Promise<[string, T][]> {
|
|
106
|
-
const values = await this.redis.hgetall(`${topic}:${key}`);
|
|
107
|
-
return Object.entries(values) as [string, T][];
|
|
108
|
-
}
|
|
109
|
-
async hclear(topic: string, key: string): Promise<void> {
|
|
110
|
-
await this.redis.del(`${topic}:${key}`);
|
|
111
|
-
}
|
|
112
99
|
getClient(): Redis {
|
|
113
100
|
return this.redis;
|
|
114
101
|
}
|
|
@@ -4,7 +4,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import type { InArgs, InValue, Client as LibsqlClient } from "@libsql/client";
|
|
6
6
|
import { type BaseEnv, dayjs, FIELD_META, type PromiseOrObject } from "akanjs/base";
|
|
7
|
-
import {
|
|
7
|
+
import type { ConstantModel } from "akanjs/constant";
|
|
8
8
|
import {
|
|
9
9
|
createDocumentId,
|
|
10
10
|
type DatabaseModel,
|
|
@@ -300,13 +300,6 @@ const jsonPath = (path: string) =>
|
|
|
300
300
|
.map((part) => part.replaceAll('"', '\\"'))
|
|
301
301
|
.join(".")}`;
|
|
302
302
|
const encodeSqlValue = (value: unknown) => encodeDocumentValue(value);
|
|
303
|
-
|
|
304
|
-
const decodeDateValue = (value: unknown) => {
|
|
305
|
-
if (value === null || value === undefined) return value;
|
|
306
|
-
if (typeof value === "number") return dayjs(value);
|
|
307
|
-
const epoch = Number(value);
|
|
308
|
-
return Number.isNaN(epoch) ? dayjs(value as never) : dayjs(epoch);
|
|
309
|
-
};
|
|
310
303
|
const QUERY_OPERATOR_KEYS = new Set([
|
|
311
304
|
"eq",
|
|
312
305
|
"ne",
|
|
@@ -780,9 +773,6 @@ export class SqliteDocumentStore {
|
|
|
780
773
|
} else {
|
|
781
774
|
doc[key] = value;
|
|
782
775
|
}
|
|
783
|
-
if (doc[key] !== undefined && doc[key] !== null) {
|
|
784
|
-
doc[key] = this.normalizeWriteValue(doc[key], props);
|
|
785
|
-
}
|
|
786
776
|
if (props.enum && doc[key] !== undefined && doc[key] !== null) {
|
|
787
777
|
const values = Array.isArray(doc[key]) ? doc[key] : [doc[key]];
|
|
788
778
|
const fieldEnum = props.enum as { has: (value: unknown) => boolean } | undefined;
|
|
@@ -968,28 +958,12 @@ export class SqliteDocumentStore {
|
|
|
968
958
|
|
|
969
959
|
private decodeDocumentPayload(payload: Record<string, unknown>) {
|
|
970
960
|
const fields = this.database.doc[FIELD_META] as unknown as FieldMap;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
const def = props.default;
|
|
978
|
-
if (def !== undefined && def !== null) {
|
|
979
|
-
result[key] = typeof def === "function" ? (def as (data: unknown) => unknown)(payload) : def;
|
|
980
|
-
} else if (props.nullable) {
|
|
981
|
-
result[key] = null;
|
|
982
|
-
}
|
|
983
|
-
} else {
|
|
984
|
-
result[key] = this.decodeFieldValue(value, props);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
for (const [key, value] of Object.entries(payload)) {
|
|
988
|
-
if (key in result || BASE_COLUMNS.has(key)) continue;
|
|
989
|
-
const props = fields[key]?.getProps?.();
|
|
990
|
-
result[key] = props ? this.decodeFieldValue(value, props) : value;
|
|
991
|
-
}
|
|
992
|
-
return result;
|
|
961
|
+
return Object.fromEntries(
|
|
962
|
+
Object.entries(payload).map(([key, value]) => {
|
|
963
|
+
const props = fields[key]?.getProps?.();
|
|
964
|
+
return [key, props ? this.decodeFieldValue(value, props) : value];
|
|
965
|
+
}),
|
|
966
|
+
);
|
|
993
967
|
}
|
|
994
968
|
|
|
995
969
|
private decodeFieldValue(value: unknown, props: Record<string, unknown>): unknown {
|
|
@@ -999,8 +973,8 @@ export class SqliteDocumentStore {
|
|
|
999
973
|
return new Map(entries.map(([key, item]) => [key, this.decodeMapValue(item, props)]));
|
|
1000
974
|
}
|
|
1001
975
|
if (props.modelRef === Date) {
|
|
1002
|
-
if (Array.isArray(value)) return value.map((item) => (item === null ? item :
|
|
1003
|
-
return
|
|
976
|
+
if (Array.isArray(value)) return value.map((item) => (item === null ? item : dayjs(Number(item))));
|
|
977
|
+
return dayjs(Number(value));
|
|
1004
978
|
}
|
|
1005
979
|
if (Array.isArray(value)) return value.map((item) => this.decodeNestedValue(item, props));
|
|
1006
980
|
return this.decodeNestedValue(value, props);
|
|
@@ -1008,7 +982,7 @@ export class SqliteDocumentStore {
|
|
|
1008
982
|
|
|
1009
983
|
private decodeMapValue(value: unknown, props: Record<string, unknown>) {
|
|
1010
984
|
if (value === undefined || value === null) return value;
|
|
1011
|
-
if (props.of === Date) return
|
|
985
|
+
if (props.of === Date) return dayjs(Number(value));
|
|
1012
986
|
return value;
|
|
1013
987
|
}
|
|
1014
988
|
|
|
@@ -1017,44 +991,12 @@ export class SqliteDocumentStore {
|
|
|
1017
991
|
if (!props.isClass || !props.isScalar) return value;
|
|
1018
992
|
const scalarFields = (props.modelRef as { [FIELD_META]?: FieldMap } | undefined)?.[FIELD_META];
|
|
1019
993
|
if (!scalarFields) return value;
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
result[key] = nested === undefined ? defaults[key] : this.decodeFieldValue(nested, nestedProps);
|
|
1027
|
-
}
|
|
1028
|
-
for (const [key, nested] of Object.entries(source)) {
|
|
1029
|
-
if (!(key in result)) result[key] = nested;
|
|
1030
|
-
}
|
|
1031
|
-
return result;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
private normalizeWriteValue(value: unknown, props: Record<string, unknown>): unknown {
|
|
1035
|
-
if (value === undefined || value === null) return value;
|
|
1036
|
-
if (props.modelRef === Date) {
|
|
1037
|
-
if (Array.isArray(value))
|
|
1038
|
-
return value.map((item) => (item === null || item === undefined ? item : dayjs(item as never)));
|
|
1039
|
-
return dayjs(value as never);
|
|
1040
|
-
}
|
|
1041
|
-
if (!props.isClass || !props.isScalar) return value;
|
|
1042
|
-
if (Array.isArray(value)) return value.map((item) => this.fillScalarDefaults(item, props));
|
|
1043
|
-
return this.fillScalarDefaults(value, props);
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
private fillScalarDefaults(value: unknown, props: Record<string, unknown>): unknown {
|
|
1047
|
-
if (!value || typeof value !== "object") return value;
|
|
1048
|
-
const scalarFields = (props.modelRef as { [FIELD_META]?: FieldMap } | undefined)?.[FIELD_META];
|
|
1049
|
-
if (!scalarFields) return value;
|
|
1050
|
-
const defaults = getDefault(scalarFields as never) as Record<string, unknown>;
|
|
1051
|
-
const result = { ...(value as Record<string, unknown>) };
|
|
1052
|
-
for (const [key, fieldMeta] of Object.entries(scalarFields)) {
|
|
1053
|
-
const nestedProps = fieldMeta.getProps();
|
|
1054
|
-
if (result[key] === undefined) result[key] = defaults[key];
|
|
1055
|
-
else result[key] = this.normalizeWriteValue(result[key], nestedProps);
|
|
1056
|
-
}
|
|
1057
|
-
return result;
|
|
994
|
+
return Object.fromEntries(
|
|
995
|
+
Object.entries(value as Record<string, unknown>).map(([key, nested]) => {
|
|
996
|
+
const nestedProps = scalarFields[key]?.getProps?.();
|
|
997
|
+
return [key, nestedProps ? this.decodeFieldValue(nested, nestedProps) : nested];
|
|
998
|
+
}),
|
|
999
|
+
);
|
|
1058
1000
|
}
|
|
1059
1001
|
|
|
1060
1002
|
hydrate(data: DocumentRecord, originalData: DocumentRecord = data) {
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
} from "./solidSqlite";
|
|
15
15
|
|
|
16
16
|
type CacheRow = { value: string | Buffer | null; valueType: SolidValueType; expiresAt: number | null };
|
|
17
|
-
type CacheEntryRow = CacheRow & { subKey: string };
|
|
18
17
|
|
|
19
18
|
export class SolidCache
|
|
20
19
|
extends adapt("solidCache", ({ env }) => ({
|
|
@@ -136,28 +135,6 @@ export class SolidCache
|
|
|
136
135
|
.run(topic, key, subKey);
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
async hkeys(topic: string, key: string): Promise<string[]> {
|
|
140
|
-
this.#cleanup();
|
|
141
|
-
const rows = this.#db
|
|
142
|
-
.query(`SELECT "subKey" FROM "_akan_solid_cache_hash" WHERE "topic" = ? AND "key" = ? ORDER BY "subKey" ASC`)
|
|
143
|
-
.all(topic, key) as { subKey: string }[];
|
|
144
|
-
return rows.map((row) => row.subKey);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async hentries<T extends string | number | Buffer>(topic: string, key: string): Promise<[string, T][]> {
|
|
148
|
-
this.#cleanup();
|
|
149
|
-
const rows = this.#db
|
|
150
|
-
.query(
|
|
151
|
-
`SELECT "subKey", "value", "valueType", "expiresAt" FROM "_akan_solid_cache_hash" WHERE "topic" = ? AND "key" = ? ORDER BY "subKey" ASC`,
|
|
152
|
-
)
|
|
153
|
-
.all(topic, key) as CacheEntryRow[];
|
|
154
|
-
return rows.map((row) => [row.subKey, decodeSolidValue<T>(row.valueType, row.value) as T]);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async hclear(topic: string, key: string): Promise<void> {
|
|
158
|
-
this.#db.query(`DELETE FROM "_akan_solid_cache_hash" WHERE "topic" = ? AND "key" = ?`).run(topic, key);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
138
|
#cleanup() {
|
|
162
139
|
const now = Date.now();
|
|
163
140
|
this.#db.query(`DELETE FROM "_akan_solid_cache" WHERE "expiresAt" IS NOT NULL AND "expiresAt" <= ?`).run(now);
|
|
@@ -64,7 +64,6 @@ export class FetchSerializer {
|
|
|
64
64
|
args: endpointInfo.args.map(FetchSerializer.#serializeArg),
|
|
65
65
|
returns: FetchSerializer.#serializeReturns(endpointInfo),
|
|
66
66
|
...(endpointInfo.signalOption.path ? { path: endpointInfo.signalOption.path } : {}),
|
|
67
|
-
...(endpointInfo.signalOption.fileUpload ? { fileUpload: true } : {}),
|
|
68
67
|
...(guards?.length ? { guards } : {}),
|
|
69
68
|
};
|
|
70
69
|
}
|
package/signal/types.ts
CHANGED
|
@@ -70,8 +70,6 @@ export interface SignalOption<Response = any, Nullable extends boolean = false,
|
|
|
70
70
|
middlewares?: MiddlewareCls[];
|
|
71
71
|
prefix?: false | string;
|
|
72
72
|
globalPrefix?: false;
|
|
73
|
-
/** Marks this mutation as the framework file-upload endpoint (see resolveFileUploadCapability). */
|
|
74
|
-
fileUpload?: boolean;
|
|
75
73
|
|
|
76
74
|
scheduleType?: "init" | "destroy" | "cron" | "interval" | "timeout";
|
|
77
75
|
scheduleCron?: string;
|
|
@@ -86,7 +84,6 @@ interface SerializedSignalOption {
|
|
|
86
84
|
prefix?: false | string;
|
|
87
85
|
globalPrefix?: false;
|
|
88
86
|
guards?: string[];
|
|
89
|
-
fileUpload?: boolean;
|
|
90
87
|
}
|
|
91
88
|
export interface SerializedSlice extends SerializedSignalOption {}
|
|
92
89
|
|
package/store/action.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { DataList, type Dayjs, FIELD_META, type GetStateObject, type SLICE_META } from "akanjs/base";
|
|
2
|
-
import {
|
|
3
|
-
capitalize,
|
|
4
|
-
deepObjectify,
|
|
5
|
-
type FetchPolicy,
|
|
6
|
-
isQueryEqual,
|
|
7
|
-
Logger,
|
|
8
|
-
lowerlize,
|
|
9
|
-
pathSet,
|
|
10
|
-
resolveFileUploadCapability,
|
|
11
|
-
} from "akanjs/common";
|
|
2
|
+
import { capitalize, deepObjectify, type FetchPolicy, isQueryEqual, Logger, lowerlize, pathSet } from "akanjs/common";
|
|
12
3
|
import {
|
|
13
4
|
type BaseInsight,
|
|
14
5
|
type BaseObject,
|
|
@@ -286,7 +277,6 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
286
277
|
type Light = BaseObject;
|
|
287
278
|
const [fieldName, className] = [refName, capitalize(refName)];
|
|
288
279
|
const modelRef = ConstantRegistry.getDatabase(refName).full;
|
|
289
|
-
const fileUploadRefName = resolveFileUploadCapability(fetch.serializedSignal)?.refName;
|
|
290
280
|
|
|
291
281
|
const names = {
|
|
292
282
|
model: fieldName,
|
|
@@ -366,7 +356,7 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
366
356
|
},
|
|
367
357
|
}
|
|
368
358
|
: {}),
|
|
369
|
-
...(field.isClass &&
|
|
359
|
+
...(field.isClass && ConstantRegistry.getRefName(field.modelRef) === "file"
|
|
370
360
|
? {
|
|
371
361
|
[namesOfField.uploadFieldOnModel]: async function (this: SetGet, fileList: FileList, index?: number) {
|
|
372
362
|
const form = (this.get() as { [key: string]: any })[names.modelForm] as { [key: string]: any };
|
|
@@ -393,9 +383,7 @@ export const makeFormSetter = (refName: string, fetch: FetchProxy<any>) => {
|
|
|
393
383
|
const intervalKey = setInterval(() => {
|
|
394
384
|
void (async () => {
|
|
395
385
|
const currentFile = await (
|
|
396
|
-
(fetch as { [key: string]: any })
|
|
397
|
-
id: string,
|
|
398
|
-
) => Promise<ProtoFile>
|
|
386
|
+
(fetch as { [key: string]: any }).file as (id: string) => Promise<ProtoFile>
|
|
399
387
|
)(file.id);
|
|
400
388
|
if (field.isArray)
|
|
401
389
|
this.set((state: { [key: string]: { [key: string]: ProtoFile[] } }) => {
|
package/types/common/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { applyMixins } from "./applyMixins.d.ts";
|
|
2
2
|
export { capitalize } from "./capitalize.d.ts";
|
|
3
3
|
export { deepObjectify } from "./deepObjectify.d.ts";
|
|
4
|
-
export
|
|
4
|
+
export * from "./fileUpload.d.ts";
|
|
5
5
|
export { formatNumber } from "./formatNumber.d.ts";
|
|
6
6
|
export { formatPhone } from "./formatPhone.d.ts";
|
|
7
7
|
export { getAllPropertyDescriptors } from "./getAllPropertyDescriptors.d.ts";
|
|
@@ -76,15 +76,9 @@ export declare const injectionBuilder: (parentRefName: string) => {
|
|
|
76
76
|
get?: GetFn;
|
|
77
77
|
set?: (value: ReturnType<GetFn>) => GetFieldValue<ValueRef, ExplicitType>;
|
|
78
78
|
}) => InjectInfo<"memory", Local extends true ? MapConstructor extends ValueRef ? Map<string, FieldToValue<MapValue>> : (DefaultValue extends never ? true : false) extends true ? (never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>) | null : never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn> : MapConstructor extends ValueRef ? {
|
|
79
|
-
get: (key: string) => Promise<
|
|
80
|
-
set: (key: string, value:
|
|
79
|
+
get: (key: string) => Promise<FieldToValue<MapValue>>;
|
|
80
|
+
set: (key: string, value: FieldToValue<MapValue>) => Promise<void>;
|
|
81
81
|
delete: (key: string) => Promise<void>;
|
|
82
|
-
getOrInsert: (key: string, value: never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>) => Promise<never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>>;
|
|
83
|
-
getOrInsertComputed: (key: string, compute: (key: string) => (never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>) | Promise<never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>>) => Promise<never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>>;
|
|
84
|
-
keys: () => Promise<string[]>;
|
|
85
|
-
entries: () => Promise<[string, never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>][]>;
|
|
86
|
-
forEach: (callback: (value: never extends GetFn ? FieldToValue<MapValue> : ReturnType<GetFn>, key: string) => void | Promise<void>) => Promise<void>;
|
|
87
|
-
clear: () => Promise<void>;
|
|
88
82
|
} : {
|
|
89
83
|
get: () => Promise<(DefaultValue extends never ? true : false) extends true ? (never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>) | null : never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>>;
|
|
90
84
|
set: (value: (DefaultValue extends never ? true : false) extends true ? (never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>) | null : never extends GetFn ? GetFieldValue<ValueRef, ExplicitType, MapValue> : ReturnType<GetFn>) => Promise<void>;
|
|
@@ -12,9 +12,6 @@ export interface CacheAdaptor {
|
|
|
12
12
|
}): Promise<void>;
|
|
13
13
|
hget<T extends string | number | Buffer>(topic: string, key: string, subKey: string): Promise<T | undefined>;
|
|
14
14
|
hdelete(topic: string, key: string, subKey: string): Promise<void>;
|
|
15
|
-
hkeys(topic: string, key: string): Promise<string[]>;
|
|
16
|
-
hentries<T extends string | number | Buffer>(topic: string, key: string): Promise<[string, T][]>;
|
|
17
|
-
hclear(topic: string, key: string): Promise<void>;
|
|
18
15
|
}
|
|
19
16
|
interface RedisEnv extends BaseEnv {
|
|
20
17
|
redis?: {
|
|
@@ -37,9 +34,6 @@ export declare class RedisCache extends RedisCache_base implements CacheAdaptor
|
|
|
37
34
|
}): Promise<void>;
|
|
38
35
|
hget<T extends string | number | Buffer>(topic: string, key: string, subKey: string): Promise<T | undefined>;
|
|
39
36
|
hdelete(topic: string, key: string, subKey: string): Promise<void>;
|
|
40
|
-
hkeys(topic: string, key: string): Promise<string[]>;
|
|
41
|
-
hentries<T extends string | number | Buffer>(topic: string, key: string): Promise<[string, T][]>;
|
|
42
|
-
hclear(topic: string, key: string): Promise<void>;
|
|
43
37
|
getClient(): Redis;
|
|
44
38
|
onDestroy(): Promise<void>;
|
|
45
39
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
import type { Client as LibsqlClient } from "@libsql/client";
|
|
3
3
|
import { type BaseEnv, type PromiseOrObject } from "akanjs/base";
|
|
4
|
-
import {
|
|
4
|
+
import type { ConstantModel } from "akanjs/constant";
|
|
5
5
|
import { type DatabaseModel, type DocumentQuery, type DocumentSchema, type DocumentUpdate, type DocumentUpdateOptions, type SchemaOf } from "akanjs/document";
|
|
6
6
|
import type { Sql } from "postgres";
|
|
7
7
|
export interface SqliteDatabaseConfig {
|
|
@@ -256,8 +256,6 @@ export declare class SqliteDocumentStore {
|
|
|
256
256
|
private decodeFieldValue;
|
|
257
257
|
private decodeMapValue;
|
|
258
258
|
private decodeNestedValue;
|
|
259
|
-
private normalizeWriteValue;
|
|
260
|
-
private fillScalarDefaults;
|
|
261
259
|
hydrate(data: DocumentRecord, originalData?: DocumentRecord): any;
|
|
262
260
|
private runHooks;
|
|
263
261
|
private insertStmt;
|
|
@@ -18,8 +18,5 @@ export declare class SolidCache extends SolidCache_base implements CacheAdaptor
|
|
|
18
18
|
}): Promise<void>;
|
|
19
19
|
hget<T extends string | number | Buffer>(topic: string, key: string, subKey: string): Promise<T | undefined>;
|
|
20
20
|
hdelete(topic: string, key: string, subKey: string): Promise<void>;
|
|
21
|
-
hkeys(topic: string, key: string): Promise<string[]>;
|
|
22
|
-
hentries<T extends string | number | Buffer>(topic: string, key: string): Promise<[string, T][]>;
|
|
23
|
-
hclear(topic: string, key: string): Promise<void>;
|
|
24
21
|
}
|
|
25
22
|
export {};
|
package/types/signal/types.d.ts
CHANGED
|
@@ -60,8 +60,6 @@ export interface SignalOption<Response = any, Nullable extends boolean = false,
|
|
|
60
60
|
middlewares?: MiddlewareCls[];
|
|
61
61
|
prefix?: false | string;
|
|
62
62
|
globalPrefix?: false;
|
|
63
|
-
/** Marks this mutation as the framework file-upload endpoint (see resolveFileUploadCapability). */
|
|
64
|
-
fileUpload?: boolean;
|
|
65
63
|
scheduleType?: "init" | "destroy" | "cron" | "interval" | "timeout";
|
|
66
64
|
scheduleCron?: string;
|
|
67
65
|
scheduleTime?: number;
|
|
@@ -74,7 +72,6 @@ interface SerializedSignalOption {
|
|
|
74
72
|
prefix?: false | string;
|
|
75
73
|
globalPrefix?: false;
|
|
76
74
|
guards?: string[];
|
|
77
|
-
fileUpload?: boolean;
|
|
78
75
|
}
|
|
79
76
|
export interface SerializedSlice extends SerializedSignalOption {
|
|
80
77
|
}
|
|
@@ -6,4 +6,4 @@ export interface ModalProps {
|
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
onCancel?: () => void;
|
|
8
8
|
}
|
|
9
|
-
export declare const Modal: ({ className, bodyClassName, confirmClose, children, onCancel }: ModalProps) => import("react").
|
|
9
|
+
export declare const Modal: ({ className, bodyClassName, confirmClose, children, onCancel }: ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ProviderProps } from "./Provider.d.ts";
|
|
2
2
|
export declare const Dialog: {
|
|
3
3
|
({ children, ...props }: ProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
4
|
-
Modal: ({ className, bodyClassName, confirmClose, children, onCancel }: import("./Modal.d.ts").ModalProps) => import("react").
|
|
4
|
+
Modal: ({ className, bodyClassName, confirmClose, children, onCancel }: import("./Modal.d.ts").ModalProps) => import("react/jsx-runtime").JSX.Element;
|
|
5
5
|
Title: ({ children }: import("./Title.d.ts").TitleProps) => null;
|
|
6
6
|
Action: ({ children }: import("./Action.d.ts").ActionProps) => null;
|
|
7
7
|
Trigger: ({ className, children }: import("./Trigger.d.ts").TriggerProps) => import("react/jsx-runtime").JSX.Element;
|
package/types/ui/Modal.d.ts
CHANGED
|
@@ -16,4 +16,15 @@ export interface ModalProps {
|
|
|
16
16
|
/** Ask for close confirmation before dismissing. */
|
|
17
17
|
confirmClose?: boolean;
|
|
18
18
|
}
|
|
19
|
-
export declare const Modal:
|
|
19
|
+
export declare const Modal: {
|
|
20
|
+
({ className, title, action, open, onCancel, bodyClassName, children, confirmClose, }: ModalProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
Window: ({ open, onCancel, title, children }: WindowProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
22
|
+
};
|
|
23
|
+
interface WindowProps {
|
|
24
|
+
open: boolean;
|
|
25
|
+
onCancel: () => void;
|
|
26
|
+
title: ReactNode;
|
|
27
|
+
children: ReactNode;
|
|
28
|
+
}
|
|
29
|
+
export declare const Window: ({ open, onCancel, title, children }: WindowProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
30
|
+
export {};
|
|
@@ -6,9 +6,9 @@ import { type HTMLAttributes, type ReactNode, type RefObject } from "react";
|
|
|
6
6
|
export declare const Client: {
|
|
7
7
|
(): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
Wrapper: ({ children, theme, lang, dictionary, signals, reconnect, }: ClientWrapperProps) => import("react/jsx-runtime").JSX.Element;
|
|
9
|
-
Bridge: ({ env, lang, theme, prefix, gaTrackingId }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
|
|
9
|
+
Bridge: ({ env, lang, theme, prefix, gaTrackingId, }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
|
|
10
10
|
Inner: () => import("react/jsx-runtime").JSX.Element;
|
|
11
|
-
SsrBridge: ({ lang, prefix }: ClientSsrBridgeProps) => null;
|
|
11
|
+
SsrBridge: ({ lang, prefix, }: ClientSsrBridgeProps) => null;
|
|
12
12
|
};
|
|
13
13
|
interface ClientWrapperProps {
|
|
14
14
|
children: ReactNode;
|
|
@@ -37,11 +37,11 @@ interface ClientBridgeProps {
|
|
|
37
37
|
prefix?: string;
|
|
38
38
|
gaTrackingId?: string;
|
|
39
39
|
}
|
|
40
|
-
export declare const ClientBridge: ({ env, lang, theme, prefix, gaTrackingId }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
|
|
40
|
+
export declare const ClientBridge: ({ env, lang, theme, prefix, gaTrackingId, }: ClientBridgeProps) => "" | import("react/jsx-runtime").JSX.Element | undefined;
|
|
41
41
|
export declare const ClientInner: () => import("react/jsx-runtime").JSX.Element;
|
|
42
42
|
interface ClientSsrBridgeProps {
|
|
43
43
|
lang: string;
|
|
44
44
|
prefix?: string;
|
|
45
45
|
}
|
|
46
|
-
export declare const ClientSsrBridge: ({ lang, prefix }: ClientSsrBridgeProps) => null;
|
|
46
|
+
export declare const ClientSsrBridge: ({ lang, prefix, }: ClientSsrBridgeProps) => null;
|
|
47
47
|
export {};
|
package/ui/Dialog/Modal.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import * as Dialog from "@radix-ui/react-dialog";
|
|
2
3
|
import { useDrag } from "@use-gesture/react";
|
|
3
4
|
import { clsx, usePage } from "akanjs/client";
|
|
4
5
|
import { animated } from "akanjs/ui";
|
|
5
|
-
import { type ReactNode,
|
|
6
|
-
import { createPortal } from "react-dom";
|
|
6
|
+
import { type ReactNode, useContext, useEffect, useRef, useState } from "react";
|
|
7
7
|
import { BiX } from "react-icons/bi";
|
|
8
8
|
import { config, useSpring } from "react-spring";
|
|
9
9
|
|
|
@@ -11,8 +11,6 @@ import { DialogContext } from "./context";
|
|
|
11
11
|
|
|
12
12
|
const MODAL_MARGIN = 0;
|
|
13
13
|
const OPACITY = { START: 0, END: 1 };
|
|
14
|
-
let bodyScrollLockCount = 0;
|
|
15
|
-
let previousBodyOverflow = "";
|
|
16
14
|
|
|
17
15
|
const interpolate = (o: number, i: number, t: number) => {
|
|
18
16
|
return o + (i - o) * t;
|
|
@@ -27,80 +25,41 @@ export interface ModalProps {
|
|
|
27
25
|
}
|
|
28
26
|
export const Modal = ({ className, bodyClassName, confirmClose, children, onCancel }: ModalProps) => {
|
|
29
27
|
const { open, setOpen, title, action } = useContext(DialogContext);
|
|
28
|
+
const openRef = useRef<boolean>(open);
|
|
30
29
|
const { l } = usePage();
|
|
31
30
|
const ref = useRef<HTMLDivElement>(null);
|
|
32
|
-
const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
33
|
-
const closingRef = useRef(false);
|
|
34
|
-
const focusedElementRef = useRef<HTMLElement | null>(null);
|
|
35
|
-
const titleId = useId();
|
|
36
|
-
const contentId = useId();
|
|
37
31
|
const [{ translate }, api] = useSpring(() => ({ translate: 1 }));
|
|
38
|
-
const [portalElement, setPortalElement] = useState<HTMLElement | null>(null);
|
|
39
|
-
const [isMounted, setIsMounted] = useState(open);
|
|
40
32
|
const [showBackground, setShowBackground] = useState(false);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
confirmClose,
|
|
59
|
-
notifyCancel = true,
|
|
60
|
-
}: {
|
|
61
|
-
velocity?: number;
|
|
62
|
-
confirmClose?: boolean;
|
|
63
|
-
notifyCancel?: boolean;
|
|
64
|
-
}) => {
|
|
65
|
-
if (closingRef.current) return;
|
|
66
|
-
if (confirmClose && !window.confirm(l("base.confirmClose"))) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
closingRef.current = true;
|
|
71
|
-
if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
|
|
72
|
-
closeTimerRef.current = setTimeout(() => {
|
|
73
|
-
setShowBackground(false);
|
|
74
|
-
}, 100);
|
|
75
|
-
await Promise.all(api.start({ translate: 1, immediate: false, config: { ...config.stiff, velocity } }));
|
|
76
|
-
setIsMounted(false);
|
|
77
|
-
setOpen(false);
|
|
78
|
-
if (notifyCancel) onCancel?.();
|
|
79
|
-
closingRef.current = false;
|
|
80
|
-
},
|
|
81
|
-
[api, l, onCancel, setOpen],
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const requestClose = useCallback(
|
|
85
|
-
(options?: { velocity?: number }) => {
|
|
86
|
-
void closeModal({ velocity: options?.velocity, confirmClose });
|
|
87
|
-
},
|
|
88
|
-
[closeModal, confirmClose],
|
|
89
|
-
);
|
|
90
|
-
|
|
33
|
+
const openModal = async ({ canceled }: { canceled?: boolean } = {}) => {
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
setShowBackground(true);
|
|
36
|
+
}, 100);
|
|
37
|
+
await Promise.all(api.start({ translate: 0, immediate: false, config: canceled ? config.wobbly : config.stiff }));
|
|
38
|
+
};
|
|
39
|
+
const closeModal = async ({ velocity = 0, confirmClose }: { velocity?: number; confirmClose?: boolean }) => {
|
|
40
|
+
if (confirmClose && !window.confirm(l("base.confirmClose"))) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
setShowBackground(false);
|
|
45
|
+
}, 100);
|
|
46
|
+
await Promise.all(api.start({ translate: 1, immediate: false, config: { ...config.stiff, velocity } }));
|
|
47
|
+
setOpen(false);
|
|
48
|
+
onCancel?.();
|
|
49
|
+
};
|
|
91
50
|
const bind = useDrag(
|
|
92
51
|
({ last, velocity: [, vy], direction: [, dy], offset: [, oy], movement: [, my], cancel, canceled }) => {
|
|
93
52
|
if (!ref.current) return;
|
|
94
|
-
const height =
|
|
53
|
+
const height = (ref.current.clientHeight || MODAL_MARGIN) - MODAL_MARGIN;
|
|
95
54
|
if (my > 70) cancel();
|
|
96
55
|
if (last) {
|
|
97
|
-
if (my > height * 0.5 || (vy > 0.5 && dy > 0))
|
|
56
|
+
if (my > height * 0.5 || (vy > 0.5 && dy > 0))
|
|
57
|
+
void closeModal({ velocity: vy / height, confirmClose: confirmClose });
|
|
98
58
|
else void openModal({ canceled });
|
|
99
59
|
} else void api.start({ translate: oy / height, immediate: true });
|
|
100
60
|
},
|
|
101
61
|
{ from: () => [0, translate.get()], filterTaps: true, bounds: { top: 0 }, rubberband: true },
|
|
102
62
|
);
|
|
103
|
-
|
|
104
63
|
const opacity = translate.to((t) => {
|
|
105
64
|
return interpolate(OPACITY.END, OPACITY.START, t);
|
|
106
65
|
});
|
|
@@ -109,104 +68,33 @@ export const Modal = ({ className, bodyClassName, confirmClose, children, onCanc
|
|
|
109
68
|
});
|
|
110
69
|
|
|
111
70
|
useEffect(() => {
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (!open && isMounted) {
|
|
124
|
-
void closeModal({ notifyCancel: false });
|
|
125
|
-
}
|
|
126
|
-
}, [closeModal, isMounted, open]);
|
|
127
|
-
|
|
128
|
-
useEffect(() => {
|
|
129
|
-
if (!isMounted || typeof document === "undefined") return;
|
|
130
|
-
|
|
131
|
-
bodyScrollLockCount += 1;
|
|
132
|
-
if (bodyScrollLockCount === 1) {
|
|
133
|
-
previousBodyOverflow = document.body.style.overflow;
|
|
134
|
-
document.body.style.overflow = "hidden";
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return () => {
|
|
138
|
-
bodyScrollLockCount -= 1;
|
|
139
|
-
if (bodyScrollLockCount === 0) {
|
|
140
|
-
document.body.style.overflow = previousBodyOverflow;
|
|
141
|
-
previousBodyOverflow = "";
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
}, [isMounted]);
|
|
145
|
-
|
|
146
|
-
useEffect(() => {
|
|
147
|
-
if (!isMounted || !portalElement || typeof document === "undefined") return;
|
|
148
|
-
|
|
149
|
-
focusedElementRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
150
|
-
queueMicrotask(() => {
|
|
151
|
-
ref.current?.focus();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
return () => {
|
|
155
|
-
if (focusedElementRef.current && document.contains(focusedElementRef.current)) {
|
|
156
|
-
focusedElementRef.current.focus();
|
|
157
|
-
}
|
|
158
|
-
focusedElementRef.current = null;
|
|
159
|
-
};
|
|
160
|
-
}, [isMounted, portalElement]);
|
|
161
|
-
|
|
162
|
-
useEffect(() => {
|
|
163
|
-
if (!isMounted) return;
|
|
164
|
-
|
|
165
|
-
const onKeyDown = (event: KeyboardEvent) => {
|
|
166
|
-
if (event.key !== "Escape") return;
|
|
167
|
-
event.preventDefault();
|
|
168
|
-
requestClose();
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
window.addEventListener("keydown", onKeyDown);
|
|
172
|
-
return () => {
|
|
173
|
-
window.removeEventListener("keydown", onKeyDown);
|
|
174
|
-
};
|
|
175
|
-
}, [isMounted, requestClose]);
|
|
176
|
-
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
return () => {
|
|
179
|
-
if (closeTimerRef.current) clearTimeout(closeTimerRef.current);
|
|
180
|
-
};
|
|
181
|
-
}, []);
|
|
182
|
-
|
|
183
|
-
if (!isMounted || !portalElement) return null;
|
|
184
|
-
|
|
185
|
-
return createPortal(
|
|
186
|
-
<>
|
|
187
|
-
<div
|
|
188
|
-
className={clsx("fixed inset-0 z-10", showBackground && "animate-fadeIn bg-black/50 backdrop-blur-md")}
|
|
189
|
-
onClick={(event) => {
|
|
190
|
-
if (event.target !== event.currentTarget) return;
|
|
191
|
-
requestClose();
|
|
71
|
+
if (openRef.current === open) return;
|
|
72
|
+
openRef.current = open;
|
|
73
|
+
if (open) void openModal();
|
|
74
|
+
else void closeModal({});
|
|
75
|
+
}, [open]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Dialog.Portal>
|
|
79
|
+
<Dialog.Overlay
|
|
80
|
+
onClick={() => {
|
|
81
|
+
void closeModal({ confirmClose });
|
|
192
82
|
}}
|
|
193
|
-
|
|
194
|
-
|
|
83
|
+
>
|
|
84
|
+
{showBackground ? (
|
|
85
|
+
<div className={"fixed inset-0 z-10 bg-base-content/50 backdrop-blur-md data-[state=open]:animate-fadeIn"} />
|
|
86
|
+
) : null}
|
|
87
|
+
</Dialog.Overlay>
|
|
88
|
+
<Dialog.Content
|
|
89
|
+
className="fixed top-1/2 left-1/2 flex -translate-x-1/2 -translate-y-1/2 items-center justify-center"
|
|
90
|
+
asChild
|
|
91
|
+
forceMount
|
|
92
|
+
>
|
|
195
93
|
<div className="z-10">
|
|
196
|
-
<animated.div
|
|
197
|
-
ref={ref}
|
|
198
|
-
style={{ translateY, opacity }}
|
|
199
|
-
role="dialog"
|
|
200
|
-
aria-modal="true"
|
|
201
|
-
aria-labelledby={title ? titleId : undefined}
|
|
202
|
-
aria-describedby={contentId}
|
|
203
|
-
tabIndex={-1}
|
|
204
|
-
>
|
|
94
|
+
<animated.div ref={ref} style={{ translateY, opacity }}>
|
|
205
95
|
<button
|
|
206
|
-
type="button"
|
|
207
|
-
aria-label="Close"
|
|
208
96
|
className="btn btn-circle btn-sm absolute top-[-16px] right-0 z-20 md:top-[-40px]"
|
|
209
|
-
onClick={() =>
|
|
97
|
+
onClick={() => void closeModal({ confirmClose })}
|
|
210
98
|
>
|
|
211
99
|
<BiX className="text-3xl" />
|
|
212
100
|
</button>
|
|
@@ -216,33 +104,34 @@ export const Modal = ({ className, bodyClassName, confirmClose, children, onCanc
|
|
|
216
104
|
className,
|
|
217
105
|
)}
|
|
218
106
|
>
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
107
|
+
<Dialog.Title asChild>
|
|
108
|
+
<animated.div
|
|
109
|
+
{...bind()}
|
|
110
|
+
className="relative z-10 flex w-full animate-fadeIn cursor-pointer touch-pan-y flex-col items-center justify-center px-4 pt-1"
|
|
111
|
+
>
|
|
112
|
+
<div className="flex w-full cursor-pointer items-center justify-center pt-1 opacity-50">
|
|
113
|
+
<div className="h-1 w-24 rounded-full bg-gray-500" />
|
|
114
|
+
</div>
|
|
115
|
+
<div className="flex w-full items-center justify-start">
|
|
116
|
+
<div className="w-full text-start font-bold text-lg">{title}</div>
|
|
117
|
+
</div>
|
|
118
|
+
</animated.div>
|
|
119
|
+
</Dialog.Title>
|
|
120
|
+
<Dialog.Description asChild>
|
|
121
|
+
<div
|
|
122
|
+
className={clsx(
|
|
123
|
+
"scrollbar-none relative m-2 flex size-full min-w-[90vw] overflow-x-hidden overflow-y-scroll border-base-content/30 border-t-[0.1px] p-4 sm:p-4 md:min-w-[384px] md:px-8 lg:min-w-[576px] xl:min-w-[768px]",
|
|
124
|
+
bodyClassName,
|
|
125
|
+
)}
|
|
126
|
+
>
|
|
127
|
+
{children}
|
|
229
128
|
</div>
|
|
230
|
-
</
|
|
231
|
-
<div
|
|
232
|
-
id={contentId}
|
|
233
|
-
className={clsx(
|
|
234
|
-
"scrollbar-none relative m-2 flex size-full min-w-[90vw] overflow-x-hidden overflow-y-scroll border-base-content/30 border-t-[0.1px] p-4 sm:p-4 md:min-w-[384px] md:px-8 lg:min-w-[576px] xl:min-w-[768px]",
|
|
235
|
-
bodyClassName,
|
|
236
|
-
)}
|
|
237
|
-
>
|
|
238
|
-
{children}
|
|
239
|
-
</div>
|
|
129
|
+
</Dialog.Description>
|
|
240
130
|
{action ? <div className="w-full">{action}</div> : null}
|
|
241
131
|
</div>
|
|
242
132
|
</animated.div>
|
|
243
133
|
</div>
|
|
244
|
-
</
|
|
245
|
-
|
|
246
|
-
portalElement,
|
|
134
|
+
</Dialog.Content>
|
|
135
|
+
</Dialog.Portal>
|
|
247
136
|
);
|
|
248
137
|
};
|
package/ui/Dialog/Provider.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import * as Dialog from "@radix-ui/react-dialog";
|
|
2
3
|
import { clsx } from "akanjs/client";
|
|
3
4
|
import { type ReactNode, useEffect, useState } from "react";
|
|
4
5
|
|
|
@@ -22,9 +23,11 @@ export const Provider = ({ className, defaultOpen = false, open = defaultOpen, c
|
|
|
22
23
|
}, [open]);
|
|
23
24
|
return (
|
|
24
25
|
<DialogContext.Provider value={{ open: openState, setOpen: setOpenState, title, setTitle, action, setAction }}>
|
|
25
|
-
<
|
|
26
|
-
{
|
|
27
|
-
|
|
26
|
+
<Dialog.Root open={openState}>
|
|
27
|
+
<div data-open={openState} className={clsx("group/dialog", className)}>
|
|
28
|
+
{children}
|
|
29
|
+
</div>
|
|
30
|
+
</Dialog.Root>
|
|
28
31
|
</DialogContext.Provider>
|
|
29
32
|
);
|
|
30
33
|
};
|
package/ui/Modal.tsx
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import * as RadixDialog from "@radix-ui/react-dialog";
|
|
2
3
|
import type { ReactNode } from "react";
|
|
4
|
+
import { BiX } from "react-icons/bi";
|
|
3
5
|
|
|
4
6
|
import { Dialog } from "./Dialog";
|
|
5
7
|
|
|
@@ -41,3 +43,45 @@ export const Modal = ({
|
|
|
41
43
|
</Dialog>
|
|
42
44
|
);
|
|
43
45
|
};
|
|
46
|
+
|
|
47
|
+
interface WindowProps {
|
|
48
|
+
open: boolean;
|
|
49
|
+
onCancel: () => void;
|
|
50
|
+
title: ReactNode;
|
|
51
|
+
children: ReactNode;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const Window = ({ open, onCancel, title, children }: WindowProps) => {
|
|
55
|
+
if (!open) return null;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<RadixDialog.Root open={open}>
|
|
59
|
+
<RadixDialog.Portal>
|
|
60
|
+
<RadixDialog.Overlay className="fixed inset-0 bg-black/40" />
|
|
61
|
+
<RadixDialog.Content
|
|
62
|
+
className="fixed top-1/2 left-1/2 z-[2] w-[90%] min-w-auto -translate-x-1/2 -translate-y-1/2 animate-fadeIn rounded-[10px] border-[3px] border-black text-black backdrop-blur-lg md:w-fit"
|
|
63
|
+
style={{
|
|
64
|
+
background: `rgba(255, 255, 255, 0.3)`,
|
|
65
|
+
width: "406px",
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<RadixDialog.Title className="height-[36px] relative overflow-hidden rounded-t-[6px] border-black border-b-2 bg-white/60 text-center">
|
|
69
|
+
<div className="m-0 text-[22px]">{title}</div>
|
|
70
|
+
<RadixDialog.Close
|
|
71
|
+
onClick={() => {
|
|
72
|
+
onCancel();
|
|
73
|
+
}}
|
|
74
|
+
className="absolute top-0 right-0 flex h-[34px] w-[40px] cursor-pointer items-center justify-center border-black border-l-2"
|
|
75
|
+
>
|
|
76
|
+
<BiX className="text-[32px]" />
|
|
77
|
+
</RadixDialog.Close>
|
|
78
|
+
</RadixDialog.Title>
|
|
79
|
+
<RadixDialog.Description className="overflow-y-hidden rounded-b-[10px] p-2">
|
|
80
|
+
{children}
|
|
81
|
+
</RadixDialog.Description>
|
|
82
|
+
</RadixDialog.Content>
|
|
83
|
+
</RadixDialog.Portal>
|
|
84
|
+
</RadixDialog.Root>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
Modal.Window = Window;
|
package/ui/System/Client.tsx
CHANGED
|
@@ -55,10 +55,8 @@ export const ClientWrapper = ({
|
|
|
55
55
|
reconnect = true,
|
|
56
56
|
}: ClientWrapperProps) => {
|
|
57
57
|
|
|
58
|
-
if (dictionary)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (typeof window !== "undefined") Translator.setActiveLocale(lang);
|
|
58
|
+
if (dictionary) Translator.seed(lang, dictionary);
|
|
59
|
+
if (getEnv().renderMode === "ssr") {
|
|
62
60
|
}
|
|
63
61
|
useLayoutEffect(() => {
|
|
64
62
|
Logger.rawLog(logo);
|
|
@@ -73,7 +71,8 @@ export const ClientWrapper = ({
|
|
|
73
71
|
};
|
|
74
72
|
Client.Wrapper = ClientWrapper;
|
|
75
73
|
|
|
76
|
-
interface ClientPathWrapperProps
|
|
74
|
+
interface ClientPathWrapperProps
|
|
75
|
+
extends Omit<HTMLAttributes<HTMLDivElement>, "style"> {
|
|
77
76
|
bind?: () => HTMLAttributes<HTMLDivElement>;
|
|
78
77
|
wrapperRef?: RefObject<HTMLDivElement | null> | null;
|
|
79
78
|
pageType?: "current" | "prev" | "cached";
|
|
@@ -94,12 +93,18 @@ export const ClientPathWrapper = ({
|
|
|
94
93
|
layoutStyle = "web",
|
|
95
94
|
...props
|
|
96
95
|
}: ClientPathWrapperProps) => {
|
|
97
|
-
const href =
|
|
98
|
-
|
|
96
|
+
const href =
|
|
97
|
+
location?.href ??
|
|
98
|
+
(typeof window !== "undefined" ? window.location.href : "");
|
|
99
|
+
const hash =
|
|
100
|
+
location?.hash ??
|
|
101
|
+
(typeof window !== "undefined" ? window.location.hash : "");
|
|
99
102
|
const pathname = location?.pathname ?? "/";
|
|
100
103
|
const params = location?.params ?? {};
|
|
101
104
|
const searchParams = location?.searchParams ?? {};
|
|
102
|
-
const search =
|
|
105
|
+
const search =
|
|
106
|
+
location?.search ??
|
|
107
|
+
(typeof window !== "undefined" ? window.location.search : "");
|
|
103
108
|
const lang = params.lang;
|
|
104
109
|
const firstPath = pathname.split("/")[2];
|
|
105
110
|
const pathRoute: PathRoute = location?.pathRoute ?? {
|
|
@@ -132,7 +137,9 @@ export const ClientPathWrapper = ({
|
|
|
132
137
|
}}
|
|
133
138
|
>
|
|
134
139
|
<animated.div
|
|
135
|
-
{...(bind && pathRoute.pageState.gesture && gestureEnabled
|
|
140
|
+
{...(bind && pathRoute.pageState.gesture && gestureEnabled
|
|
141
|
+
? bind()
|
|
142
|
+
: {})}
|
|
136
143
|
className={clsx("group/path", className)}
|
|
137
144
|
ref={wrapperRef}
|
|
138
145
|
{...props}
|
|
@@ -154,7 +161,13 @@ interface ClientBridgeProps {
|
|
|
154
161
|
gaTrackingId?: string;
|
|
155
162
|
}
|
|
156
163
|
|
|
157
|
-
export const ClientBridge = ({
|
|
164
|
+
export const ClientBridge = ({
|
|
165
|
+
env,
|
|
166
|
+
lang,
|
|
167
|
+
theme,
|
|
168
|
+
prefix,
|
|
169
|
+
gaTrackingId,
|
|
170
|
+
}: ClientBridgeProps) => {
|
|
158
171
|
const uiOperation = st.use.uiOperation();
|
|
159
172
|
const pathname = st.use.pathname();
|
|
160
173
|
const params = st.use.params();
|
|
@@ -221,18 +234,26 @@ function applyThemePolicy(theme: AkanTheme): void {
|
|
|
221
234
|
}
|
|
222
235
|
if (theme === "system") {
|
|
223
236
|
const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
224
|
-
document.documentElement.setAttribute(
|
|
237
|
+
document.documentElement.setAttribute(
|
|
238
|
+
"data-theme",
|
|
239
|
+
dark ? "dark" : "light",
|
|
240
|
+
);
|
|
225
241
|
return;
|
|
226
242
|
}
|
|
227
243
|
document.documentElement.setAttribute("data-theme", theme);
|
|
228
244
|
}
|
|
229
245
|
|
|
230
|
-
function buildSearchParams(
|
|
246
|
+
function buildSearchParams(
|
|
247
|
+
entries: Iterable<[string, string]>,
|
|
248
|
+
): Record<string, string | string[]> {
|
|
231
249
|
const params: Record<string, string | string[]> = {};
|
|
232
250
|
for (const [key, value] of entries) {
|
|
233
251
|
const current = params[key];
|
|
234
252
|
if (current === undefined) params[key] = value;
|
|
235
|
-
else
|
|
253
|
+
else
|
|
254
|
+
params[key] = Array.isArray(current)
|
|
255
|
+
? [...current, value]
|
|
256
|
+
: [current, value];
|
|
236
257
|
}
|
|
237
258
|
return params;
|
|
238
259
|
}
|
|
@@ -252,7 +273,10 @@ interface ClientSsrBridgeProps {
|
|
|
252
273
|
lang: string;
|
|
253
274
|
prefix?: string;
|
|
254
275
|
}
|
|
255
|
-
export const ClientSsrBridge = ({
|
|
276
|
+
export const ClientSsrBridge = ({
|
|
277
|
+
lang,
|
|
278
|
+
prefix = "",
|
|
279
|
+
}: ClientSsrBridgeProps) => {
|
|
256
280
|
useEffect(() => {
|
|
257
281
|
const visiblePrefix = getEnv().operationMode === "local" ? prefix : "";
|
|
258
282
|
const navigateRscWithFallback = (
|
|
@@ -266,13 +290,19 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
|
|
|
266
290
|
return;
|
|
267
291
|
}
|
|
268
292
|
void navigation.catch((error) => {
|
|
269
|
-
Logger.warn(
|
|
293
|
+
Logger.warn(
|
|
294
|
+
`RSC navigation failed, falling back to document navigation: ${String(error)}`,
|
|
295
|
+
);
|
|
270
296
|
fallback();
|
|
271
297
|
});
|
|
272
298
|
};
|
|
273
299
|
const syncHref = (href: string) => {
|
|
274
300
|
const url = new URL(href, window.location.origin);
|
|
275
|
-
const { path } = getPathInfo(
|
|
301
|
+
const { path } = getPathInfo(
|
|
302
|
+
`${url.pathname}${url.search}${url.hash}`,
|
|
303
|
+
lang,
|
|
304
|
+
visiblePrefix,
|
|
305
|
+
);
|
|
276
306
|
const searchParams = buildSearchParams(url.searchParams.entries());
|
|
277
307
|
st.set({ pathname: url.pathname, path, searchParams });
|
|
278
308
|
};
|
|
@@ -284,11 +314,17 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
|
|
|
284
314
|
router: {
|
|
285
315
|
push: (href, routeOptions) => {
|
|
286
316
|
syncHref(href);
|
|
287
|
-
navigateRscWithFallback(href, routeOptions, () =>
|
|
317
|
+
navigateRscWithFallback(href, routeOptions, () =>
|
|
318
|
+
window.location.assign(href),
|
|
319
|
+
);
|
|
288
320
|
},
|
|
289
321
|
replace: (href, routeOptions) => {
|
|
290
322
|
syncHref(href);
|
|
291
|
-
navigateRscWithFallback(
|
|
323
|
+
navigateRscWithFallback(
|
|
324
|
+
href,
|
|
325
|
+
{ ...routeOptions, replace: true },
|
|
326
|
+
() => window.location.replace(href),
|
|
327
|
+
);
|
|
292
328
|
},
|
|
293
329
|
back: () => {
|
|
294
330
|
window.history.back();
|
|
@@ -310,8 +346,14 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
|
|
|
310
346
|
const visiblePrefix = getEnv().operationMode === "local" ? prefix : "";
|
|
311
347
|
const sync = () => {
|
|
312
348
|
const { pathname, search, hash } = window.location;
|
|
313
|
-
const { path } = getPathInfo(
|
|
314
|
-
|
|
349
|
+
const { path } = getPathInfo(
|
|
350
|
+
`${pathname}${search}${hash}`,
|
|
351
|
+
lang,
|
|
352
|
+
visiblePrefix,
|
|
353
|
+
);
|
|
354
|
+
const searchParams = buildSearchParams(
|
|
355
|
+
new URLSearchParams(search).entries(),
|
|
356
|
+
);
|
|
315
357
|
st.set({ pathname: window.location.pathname, path, searchParams });
|
|
316
358
|
};
|
|
317
359
|
sync();
|