@bgord/ui 0.5.1 → 0.5.2
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/dist/components/dialog.d.ts +4 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/use-click-outside.d.ts +2 -0
- package/dist/hooks/use-scroll-lock.d.ts +1 -0
- package/dist/index.js +169 -20
- package/dist/services/auth-guard.d.ts +9 -0
- package/dist/services/cookies.d.ts +3 -0
- package/dist/services/copy-to-clipboard.d.ts +10 -0
- package/dist/services/credentials.d.ts +9 -0
- package/dist/services/index.d.ts +5 -1
- package/dist/services/noop.d.ts +1 -0
- package/package.json +12 -11
- package/readme.md +8 -1
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from "./use-click-outside";
|
|
1
2
|
export * from "./use-client-filter";
|
|
2
3
|
export * from "./use-exit-action";
|
|
3
4
|
export * from "./use-field";
|
|
@@ -5,5 +6,6 @@ export * from "./use-focus-shortcut";
|
|
|
5
6
|
export * from "./use-hover";
|
|
6
7
|
export * from "./use-language-selector";
|
|
7
8
|
export * from "./use-meta-enter-submit";
|
|
9
|
+
export * from "./use-scroll-lock";
|
|
8
10
|
export * from "./use-shortcuts";
|
|
9
11
|
export * from "./use-toggle";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useScrollLock(enabled?: boolean): void;
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,41 @@ function Button() {
|
|
|
6
6
|
children: "Click"
|
|
7
7
|
}, undefined, false, undefined, this);
|
|
8
8
|
}
|
|
9
|
+
// src/components/dialog.tsx
|
|
10
|
+
import { useEffect as useEffect6, useRef as useRef3 } from "react";
|
|
11
|
+
|
|
12
|
+
// src/hooks/use-click-outside.ts
|
|
13
|
+
import { useEffect } from "react";
|
|
14
|
+
function useClickOutside(ref, handler) {
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (typeof document === "undefined")
|
|
17
|
+
return;
|
|
18
|
+
function listener(event) {
|
|
19
|
+
const el = ref.current;
|
|
20
|
+
if (!el)
|
|
21
|
+
return;
|
|
22
|
+
if (el.contains(event.target)) {
|
|
23
|
+
if (event.target === el) {
|
|
24
|
+
const { left, right, top, bottom } = el.getBoundingClientRect();
|
|
25
|
+
const clientX = event instanceof MouseEvent ? event.clientX : event.touches[0].clientX;
|
|
26
|
+
const clientY = event instanceof MouseEvent ? event.clientY : event.touches[0].clientY;
|
|
27
|
+
const isInRect = clientX >= left && clientX <= right && clientY >= top && clientY <= bottom;
|
|
28
|
+
if (isInRect)
|
|
29
|
+
return;
|
|
30
|
+
} else {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
handler(event);
|
|
35
|
+
}
|
|
36
|
+
document.addEventListener("mousedown", listener);
|
|
37
|
+
document.addEventListener("touchstart", listener);
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener("mousedown", listener);
|
|
40
|
+
document.removeEventListener("touchstart", listener);
|
|
41
|
+
};
|
|
42
|
+
}, [ref, handler]);
|
|
43
|
+
}
|
|
9
44
|
// src/hooks/use-client-filter.ts
|
|
10
45
|
import { useCallback, useMemo } from "react";
|
|
11
46
|
|
|
@@ -34,7 +69,7 @@ class Field {
|
|
|
34
69
|
}
|
|
35
70
|
|
|
36
71
|
// src/hooks/use-field.ts
|
|
37
|
-
import { useEffect, useState } from "react";
|
|
72
|
+
import { useEffect as useEffect2, useState } from "react";
|
|
38
73
|
import { useSearchParams } from "react-router";
|
|
39
74
|
var useFieldStrategyEnum;
|
|
40
75
|
((useFieldStrategyEnum2) => {
|
|
@@ -51,7 +86,7 @@ function useField(config) {
|
|
|
51
86
|
const candidate = new Field(value2);
|
|
52
87
|
_setCurrentValue(candidate.get());
|
|
53
88
|
};
|
|
54
|
-
|
|
89
|
+
useEffect2(() => {
|
|
55
90
|
const current = new Field(currentValue);
|
|
56
91
|
if (strategy === "params" /* params */) {
|
|
57
92
|
if (current.isEmpty()) {
|
|
@@ -130,12 +165,12 @@ function useExitAction(options) {
|
|
|
130
165
|
import { useCallback as useCallback2, useMemo as useMemo3, useRef } from "react";
|
|
131
166
|
|
|
132
167
|
// src/hooks/use-shortcuts.ts
|
|
133
|
-
import { useEffect as
|
|
168
|
+
import { useEffect as useEffect3, useMemo as useMemo2 } from "react";
|
|
134
169
|
import { tinykeys } from "tinykeys";
|
|
135
170
|
function useKeyboardShortcuts(config, options) {
|
|
136
171
|
const enabled = options?.enabled ?? true;
|
|
137
172
|
const memoizedConfig = useMemo2(() => config, [JSON.stringify(Object.keys(config))]);
|
|
138
|
-
|
|
173
|
+
useEffect3(() => {
|
|
139
174
|
if (!enabled)
|
|
140
175
|
return;
|
|
141
176
|
const unsubscribe = tinykeys(window, memoizedConfig);
|
|
@@ -215,7 +250,7 @@ function useHover({
|
|
|
215
250
|
}
|
|
216
251
|
// src/hooks/use-language-selector.tsx
|
|
217
252
|
import Cookies from "js-cookie";
|
|
218
|
-
import { useCallback as useCallback5, useEffect as
|
|
253
|
+
import { useCallback as useCallback5, useEffect as useEffect4 } from "react";
|
|
219
254
|
import { useRevalidator } from "react-router";
|
|
220
255
|
|
|
221
256
|
// src/services/translations.tsx
|
|
@@ -293,7 +328,7 @@ function useLanguageSelector(supportedLanguages) {
|
|
|
293
328
|
revalidator.revalidate();
|
|
294
329
|
}
|
|
295
330
|
}, [field.currentValue, field.changed]);
|
|
296
|
-
|
|
331
|
+
useEffect4(() => {
|
|
297
332
|
handleLanguageChange();
|
|
298
333
|
}, [handleLanguageChange]);
|
|
299
334
|
return field;
|
|
@@ -309,19 +344,126 @@ function useMetaEnterSubmit() {
|
|
|
309
344
|
}, []);
|
|
310
345
|
return useMemo4(() => ({ onKeyDown: handleMetaEnterSubmit }), [handleMetaEnterSubmit]);
|
|
311
346
|
}
|
|
312
|
-
// src/
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
347
|
+
// src/hooks/use-scroll-lock.ts
|
|
348
|
+
import { useEffect as useEffect5 } from "react";
|
|
349
|
+
function useScrollLock(enabled = true) {
|
|
350
|
+
useEffect5(() => {
|
|
351
|
+
if (typeof document === "undefined")
|
|
352
|
+
return;
|
|
353
|
+
const originalOverflow = document.body.style.overflow;
|
|
354
|
+
if (enabled) {
|
|
355
|
+
document.body.style.overflow = "hidden";
|
|
356
|
+
}
|
|
357
|
+
return () => {
|
|
358
|
+
document.body.style.overflow = originalOverflow;
|
|
359
|
+
};
|
|
360
|
+
}, [enabled]);
|
|
361
|
+
}
|
|
362
|
+
// src/components/dialog.tsx
|
|
363
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
364
|
+
function Dialog(props) {
|
|
365
|
+
const { toggle: dialog, rest } = extractUseToggle(props);
|
|
366
|
+
const ref = useRef3(null);
|
|
367
|
+
useEffect6(() => {
|
|
368
|
+
if (props.on) {
|
|
369
|
+
ref.current?.showModal();
|
|
370
|
+
} else {
|
|
371
|
+
ref.current?.close();
|
|
372
|
+
}
|
|
373
|
+
}, [props.on]);
|
|
374
|
+
useKeyboardShortcuts({ Escape: dialog.disable });
|
|
375
|
+
useScrollLock(props.on);
|
|
376
|
+
useClickOutside(ref, dialog.disable);
|
|
377
|
+
return /* @__PURE__ */ jsxDEV2("dialog", {
|
|
378
|
+
ref,
|
|
379
|
+
tabIndex: 0,
|
|
380
|
+
"aria-modal": "true",
|
|
381
|
+
"data-disp": props.on ? "flex" : "none",
|
|
382
|
+
"data-dir": "column",
|
|
383
|
+
"data-mx": "auto",
|
|
384
|
+
"data-p": "5",
|
|
385
|
+
"data-position": "fixed",
|
|
386
|
+
"data-z": "2",
|
|
387
|
+
"data-bg": "neutral-900",
|
|
388
|
+
"data-br": "xs",
|
|
389
|
+
"data-backdrop": "stronger",
|
|
390
|
+
"data-animation": "grow-fade-in",
|
|
391
|
+
...rest
|
|
392
|
+
}, undefined, false, undefined, this);
|
|
393
|
+
}
|
|
394
|
+
// src/services/auth-guard.ts
|
|
395
|
+
import { redirect } from "react-router";
|
|
396
|
+
|
|
397
|
+
// src/services/cookies.ts
|
|
398
|
+
class Cookies2 {
|
|
399
|
+
static extractFrom(request) {
|
|
400
|
+
return request.headers.get("cookie") ?? "";
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// src/services/auth-guard.ts
|
|
405
|
+
class AuthGuard {
|
|
406
|
+
API_URL;
|
|
407
|
+
constructor(BASE_URL) {
|
|
408
|
+
this.API_URL = `${BASE_URL}/api/auth`;
|
|
409
|
+
}
|
|
410
|
+
async getServerSession(request) {
|
|
411
|
+
const cookie = Cookies2.extractFrom(request);
|
|
412
|
+
const res = await fetch(`${this.API_URL}/get-session`, {
|
|
413
|
+
headers: { cookie, accept: "application/json" }
|
|
414
|
+
});
|
|
415
|
+
if (!res.ok)
|
|
416
|
+
return null;
|
|
417
|
+
const session = await res.json();
|
|
418
|
+
return session;
|
|
419
|
+
}
|
|
420
|
+
async requireSession(request) {
|
|
421
|
+
const session = await this.getServerSession(request);
|
|
422
|
+
if (session?.user)
|
|
423
|
+
return session;
|
|
424
|
+
throw redirect("/");
|
|
425
|
+
}
|
|
426
|
+
async requireNoSession(request, target = "/home") {
|
|
427
|
+
const session = await this.getServerSession(request);
|
|
428
|
+
if (session?.user)
|
|
429
|
+
throw redirect(target);
|
|
430
|
+
}
|
|
431
|
+
async removeSession(request, target = "/login") {
|
|
432
|
+
const cookie = Cookies2.extractFrom(request);
|
|
433
|
+
const res = await fetch(`${import.meta.env.VITE_API_URL}/api/auth/sign-out`, {
|
|
434
|
+
method: "POST",
|
|
435
|
+
headers: { cookie }
|
|
436
|
+
});
|
|
437
|
+
const headers = new Headers;
|
|
438
|
+
res.headers.forEach((value, key) => {
|
|
439
|
+
if (key.toLowerCase() === "set-cookie")
|
|
440
|
+
headers.append("set-cookie", value);
|
|
441
|
+
});
|
|
442
|
+
throw redirect(target, { headers });
|
|
443
|
+
}
|
|
324
444
|
}
|
|
445
|
+
// src/services/noop.ts
|
|
446
|
+
function noop() {}
|
|
447
|
+
|
|
448
|
+
// src/services/copy-to-clipboard.ts
|
|
449
|
+
var defaultOnCopyToClipboardFailure = () => console.warn("Copying to clipboard not supported");
|
|
450
|
+
async function copyToClipboard(options) {
|
|
451
|
+
const onFailure = options.onFailure ?? defaultOnCopyToClipboardFailure;
|
|
452
|
+
const onSuccess = options.onSuccess ?? noop;
|
|
453
|
+
if (!navigator.clipboard)
|
|
454
|
+
onFailure();
|
|
455
|
+
try {
|
|
456
|
+
await navigator.clipboard.writeText(options.text);
|
|
457
|
+
onSuccess();
|
|
458
|
+
} catch (error) {
|
|
459
|
+
onFailure(error);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
// src/services/credentials.ts
|
|
463
|
+
var Credentials = {
|
|
464
|
+
email: { inputMode: "email", autoComplete: "email", autoCapitalize: "none", spellCheck: "false" },
|
|
465
|
+
password: { new: { autoComplete: "new-password" }, current: { autoComplete: "current-password" } }
|
|
466
|
+
};
|
|
325
467
|
// src/services/etag.ts
|
|
326
468
|
class ETag {
|
|
327
469
|
static fromRevision(revision) {
|
|
@@ -423,6 +565,7 @@ class WeakETag {
|
|
|
423
565
|
export {
|
|
424
566
|
useTranslations,
|
|
425
567
|
useToggle,
|
|
568
|
+
useScrollLock,
|
|
426
569
|
usePluralize,
|
|
427
570
|
useMetaEnterSubmit,
|
|
428
571
|
useLanguageSelector,
|
|
@@ -434,10 +577,13 @@ export {
|
|
|
434
577
|
useField,
|
|
435
578
|
useExitAction,
|
|
436
579
|
useClientFilter,
|
|
580
|
+
useClickOutside,
|
|
437
581
|
pluralize,
|
|
582
|
+
noop,
|
|
438
583
|
getSafeWindow,
|
|
439
584
|
extractUseToggle,
|
|
440
585
|
exec,
|
|
586
|
+
copyToClipboard,
|
|
441
587
|
WeakETag,
|
|
442
588
|
TranslationsContext,
|
|
443
589
|
Rhythm,
|
|
@@ -446,6 +592,9 @@ export {
|
|
|
446
592
|
Fields,
|
|
447
593
|
Field,
|
|
448
594
|
ETag,
|
|
449
|
-
|
|
450
|
-
|
|
595
|
+
Dialog,
|
|
596
|
+
Credentials,
|
|
597
|
+
Cookies2 as Cookies,
|
|
598
|
+
Button,
|
|
599
|
+
AuthGuard
|
|
451
600
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { createAuthClient } from "better-auth/react";
|
|
2
|
+
export declare class AuthGuard<T extends ReturnType<typeof createAuthClient>["$Infer"]["Session"]> {
|
|
3
|
+
private readonly API_URL;
|
|
4
|
+
constructor(BASE_URL: string);
|
|
5
|
+
getServerSession(request: Request): Promise<T | null>;
|
|
6
|
+
requireSession(request: Request): Promise<T | null>;
|
|
7
|
+
requireNoSession(request: Request, target?: string): Promise<void>;
|
|
8
|
+
removeSession(request: Request, target?: string): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type CopyToClipboardTextType = string;
|
|
2
|
+
type OnCopyToClipboardFailureType = (error?: unknown) => void;
|
|
3
|
+
type OnCopyToClipboardSuccessType = VoidFunction;
|
|
4
|
+
export type CopyToClipboardOptionsType = {
|
|
5
|
+
text: CopyToClipboardTextType;
|
|
6
|
+
onFailure?: OnCopyToClipboardFailureType;
|
|
7
|
+
onSuccess?: OnCopyToClipboardSuccessType;
|
|
8
|
+
};
|
|
9
|
+
export declare function copyToClipboard(options: CopyToClipboardOptionsType): Promise<void>;
|
|
10
|
+
export {};
|
package/dist/services/index.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./auth-guard";
|
|
2
|
+
export * from "./cookies";
|
|
3
|
+
export * from "./copy-to-clipboard";
|
|
4
|
+
export * from "./credentials";
|
|
2
5
|
export * from "./etag";
|
|
3
6
|
export * from "./exec";
|
|
4
7
|
export * from "./field";
|
|
5
8
|
export * from "./fields";
|
|
6
9
|
export * from "./form";
|
|
7
10
|
export * from "./get-safe-window";
|
|
11
|
+
export * from "./noop";
|
|
8
12
|
export * from "./pluralize";
|
|
9
13
|
export * from "./rhythm";
|
|
10
14
|
export * from "./translations";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function noop(): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bgord/ui",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
"dist"
|
|
13
13
|
],
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"react": "19.1.
|
|
16
|
-
"react-dom": "19.1.
|
|
17
|
-
"react-router": "7.
|
|
15
|
+
"react": "19.1.1",
|
|
16
|
+
"react-dom": "19.1.1",
|
|
17
|
+
"react-router": "7.7.1"
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build:js": "bun build src/index.ts --format esm --outdir dist --packages external --external react --external react-dom --external react/jsx-runtime --external react-router",
|
|
@@ -29,21 +29,22 @@
|
|
|
29
29
|
"@commitlint/cli": "19.8.1",
|
|
30
30
|
"@commitlint/config-conventional": "19.8.1",
|
|
31
31
|
"@happy-dom/global-registrator": "18.0.1",
|
|
32
|
-
"@testing-library/dom": "10.4.
|
|
33
|
-
"@testing-library/jest-dom": "6.6.
|
|
32
|
+
"@testing-library/dom": "10.4.1",
|
|
33
|
+
"@testing-library/jest-dom": "6.6.4",
|
|
34
34
|
"@testing-library/react": "16.3.0",
|
|
35
35
|
"@testing-library/user-event": "14.6.1",
|
|
36
|
-
"@types/bun": "1.2.
|
|
36
|
+
"@types/bun": "1.2.19",
|
|
37
37
|
"@types/js-cookie": "^3.0.6",
|
|
38
|
-
"@types/react": "19.1.
|
|
39
|
-
"@types/react-dom": "19.1.
|
|
40
|
-
"cspell": "9.
|
|
41
|
-
"knip": "5.
|
|
38
|
+
"@types/react": "19.1.9",
|
|
39
|
+
"@types/react-dom": "19.1.7",
|
|
40
|
+
"cspell": "9.2.0",
|
|
41
|
+
"knip": "5.62.0",
|
|
42
42
|
"lefthook": "1.12.2",
|
|
43
43
|
"only-allow": "1.2.1",
|
|
44
44
|
"shellcheck": "3.1.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"better-auth": "1.3.4",
|
|
47
48
|
"js-cookie": "3.0.5",
|
|
48
49
|
"polish-plurals": "1.1.0",
|
|
49
50
|
"tinykeys": "3.0.0"
|
package/readme.md
CHANGED
|
@@ -26,7 +26,9 @@ Run the tests
|
|
|
26
26
|
src/
|
|
27
27
|
├── components
|
|
28
28
|
│ ├── button.tsx
|
|
29
|
+
│ ├── dialog.tsx
|
|
29
30
|
├── hooks
|
|
31
|
+
│ ├── use-click-outside.ts
|
|
30
32
|
│ ├── use-client-filter.ts
|
|
31
33
|
│ ├── use-exit-action.ts
|
|
32
34
|
│ ├── use-field.ts
|
|
@@ -34,16 +36,21 @@ src/
|
|
|
34
36
|
│ ├── use-hover.ts
|
|
35
37
|
│ ├── use-language-selector.tsx
|
|
36
38
|
│ ├── use-meta-enter-submit.tsx
|
|
39
|
+
│ ├── use-scroll-lock.ts
|
|
37
40
|
│ ├── use-shortcuts.ts
|
|
38
41
|
│ └── use-toggle.ts
|
|
39
42
|
└── services
|
|
40
|
-
├──
|
|
43
|
+
├── auth-guard.ts
|
|
44
|
+
├── cookies.ts
|
|
45
|
+
├── copy-to-clipboard.ts
|
|
46
|
+
├── credentials.ts
|
|
41
47
|
├── etag.ts
|
|
42
48
|
├── exec.ts
|
|
43
49
|
├── field.ts
|
|
44
50
|
├── fields.ts
|
|
45
51
|
├── form.ts
|
|
46
52
|
├── get-safe-window.ts
|
|
53
|
+
├── noop.ts
|
|
47
54
|
├── pluralize.ts
|
|
48
55
|
├── rhythm.ts
|
|
49
56
|
├── translations.tsx
|