@bgord/ui 0.6.1 → 0.6.3

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.
@@ -1,3 +1 @@
1
- export * from "./button";
2
1
  export * from "./dialog";
3
- export * from "./revalidate-on-focus";
@@ -2,9 +2,9 @@ export * from "./use-click-outside";
2
2
  export * from "./use-client-filter";
3
3
  export * from "./use-exit-action";
4
4
  export * from "./use-field";
5
+ export * from "./use-file";
5
6
  export * from "./use-focus-shortcut";
6
7
  export * from "./use-hover";
7
- export * from "./use-language-selector";
8
8
  export * from "./use-meta-enter-submit";
9
9
  export * from "./use-scroll-lock";
10
10
  export * from "./use-shortcuts";
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  type UseExitActionAnimationType = string;
3
3
  type UseExitActionOptionsType = {
4
- actionFn: () => void;
4
+ action: () => void;
5
5
  animation: UseExitActionAnimationType;
6
6
  };
7
7
  type UseExitActionReturnType = {
@@ -0,0 +1,65 @@
1
+ type UseFileNameType = string;
2
+ export type UseFileConfigType = {
3
+ mimeTypes: string[];
4
+ maxSizeBytes?: number;
5
+ };
6
+ export declare enum UseFileState {
7
+ idle = "idle",
8
+ selected = "selected",
9
+ error = "error"
10
+ }
11
+ type UseFileLabelType = {
12
+ props: {
13
+ htmlFor: UseFileNameType;
14
+ };
15
+ };
16
+ type UseFileInputType = {
17
+ props: {
18
+ id: UseFileNameType;
19
+ name: UseFileNameType;
20
+ multiple: false;
21
+ accept: React.JSX.IntrinsicElements["input"]["accept"];
22
+ };
23
+ key: React.Key;
24
+ };
25
+ type UseFileActionsType = {
26
+ selectFile(event: React.ChangeEvent<HTMLInputElement>): File | undefined;
27
+ clearFile: VoidFunction;
28
+ };
29
+ type UseFileIdle = {
30
+ actions: UseFileActionsType;
31
+ data: null;
32
+ input: UseFileInputType;
33
+ isError: false;
34
+ isIdle: true;
35
+ isSelected: false;
36
+ label: UseFileLabelType;
37
+ matches: (states: UseFileState[]) => boolean;
38
+ state: UseFileState.idle;
39
+ };
40
+ type UseFileSelected = {
41
+ actions: UseFileActionsType;
42
+ data: File;
43
+ input: UseFileInputType;
44
+ isError: false;
45
+ isIdle: false;
46
+ isSelected: true;
47
+ label: UseFileLabelType;
48
+ matches: (states: UseFileState[]) => boolean;
49
+ preview: ReturnType<typeof URL.createObjectURL> | undefined;
50
+ state: UseFileState.selected;
51
+ };
52
+ type UseFileError = {
53
+ actions: UseFileActionsType;
54
+ data: null;
55
+ input: UseFileInputType;
56
+ isError: true;
57
+ isIdle: false;
58
+ isSelected: false;
59
+ label: UseFileLabelType;
60
+ matches: (states: UseFileState[]) => boolean;
61
+ state: UseFileState.error;
62
+ };
63
+ export type UseFileReturnType = UseFileIdle | UseFileSelected | UseFileError;
64
+ export declare function useFile(name: UseFileNameType, config: UseFileConfigType): UseFileReturnType;
65
+ export {};
@@ -5,7 +5,7 @@ export type UseHoverReturnType<T extends HTMLElement> = {
5
5
  attach: {
6
6
  ref: React.RefCallback<T | null>;
7
7
  };
8
- isHovering: boolean;
8
+ hovering: boolean;
9
9
  };
10
- export declare function useHover<T extends HTMLElement = HTMLElement>({ enabled, }?: UseHoverConfigType): UseHoverReturnType<T>;
10
+ export declare function useHover<T extends HTMLElement = HTMLElement>(config?: UseHoverConfigType): UseHoverReturnType<T>;
11
11
  export {};
@@ -1,8 +1,8 @@
1
- interface UseKeyboardShortcutsConfigType {
2
- [keybinding: string]: (event: KeyboardEvent) => void;
3
- }
4
- type UseKeyboardShortcutsOptionsType = {
1
+ type UseShortcutsConfigType = {
2
+ [key: string]: (event: KeyboardEvent) => void;
3
+ };
4
+ type UseShortcutsOptionsType = {
5
5
  enabled?: boolean;
6
6
  };
7
- export declare function useKeyboardShortcuts(config: UseKeyboardShortcutsConfigType, options?: UseKeyboardShortcutsOptionsType): void;
7
+ export declare function useShortcuts(config: UseShortcutsConfigType, options?: UseShortcutsOptionsType): void;
8
8
  export {};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{jsx as U}from"react/jsx-runtime";function he(){return U("button",{type:"button",children:"Click"})}import{useEffect as ie,useRef as me}from"react";import{useEffect as q}from"react";function g(e,t){q(()=>{if(typeof document>"u")return;function r(n){let o=e.current;if(!o)return;if(o.contains(n.target))if(n.target===o){let{left:i,right:m,top:x,bottom:p}=o.getBoundingClientRect(),a=n instanceof MouseEvent?n.clientX:n.touches[0].clientX,y=n instanceof MouseEvent?n.clientY:n.touches[0].clientY;if(a>=i&&a<=m&&y>=x&&y<=p)return}else return;t(n)}return document.addEventListener("mousedown",r),document.addEventListener("touchstart",r),()=>{document.removeEventListener("mousedown",r),document.removeEventListener("touchstart",r)}},[e,t])}import{useCallback as $,useMemo as h}from"react";class s{static emptyValue=void 0;static isEmpty(e){return e===void 0||e===""||e===null}static compare(e,t){if(s.isEmpty(e)&&s.isEmpty(t))return!0;return e===t}value=s.emptyValue;constructor(e){this.value=s.isEmpty(e)?s.emptyValue:e}get(){return this.value}isEmpty(){return s.isEmpty(this.value)}}import{useEffect as P,useState as M}from"react";import{useSearchParams as W}from"react-router";var l;((r)=>{r.params="params";r.local="local"})(l||={});function L(e){let t=e.strategy??"local",[r,n]=W(),o=new s(r.get(e.name)),i=new s(e.defaultValue),[m,x]=M(o.isEmpty()?i.get():o.get()),p=(c)=>{let k=new s(c);x(k.get())};P(()=>{let c=new s(m);if(t==="params")if(c.isEmpty())r.delete(e.name),n(r);else r.set(e.name,c.get()),n(r)},[m,r,n,e.name,t]);let a=s.isEmpty(m)?"":m,y=(c)=>p(c.currentTarget.value);return{strategy:t,defaultValue:i.get(),currentValue:m,value:a,set:p,handleChange:y,clear:()=>p(i.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:a,onChange:y}},changed:!s.compare(m,i.get()),unchanged:s.compare(m,i.get()),empty:s.isEmpty(m)}}class I{static clearAll(e){return()=>e.forEach((t)=>t.clear())}}function A(e){let t=L({...e,strategy:"local"}),r=$((i)=>{if(t.empty)return!0;return s.compare(i,t.currentValue)},[t.empty,t.currentValue]),n=h(()=>e.filterFn??r,[e.filterFn,r]),o=h(()=>Object.entries(e.enum).map(([i,m])=>({name:i,value:m})),[e.enum]);return h(()=>({...t,filterFn:n,options:o,strategy:"local"}),[t,n,o])}import D from"react";function Ue(e){let[t,r]=D.useState("idle"),n=(m)=>{if(m.preventDefault(),t==="idle")r("exiting")},o=(m)=>{if(m.animationName!==e.animation)return;e.actionFn(),r("gone")},i=t==="exiting"?{"data-animation":e.animation,onAnimationEnd:o}:void 0;return{visible:t!=="gone",attach:i,trigger:n}}import{useCallback as B,useMemo as X,useRef as J}from"react";import{useEffect as v,useMemo as K}from"react";import{tinykeys as _}from"tinykeys";function T(e,t){let r=t?.enabled??!0,n=K(()=>e,[JSON.stringify(Object.keys(e))]);v(()=>{if(!r)return;let o=_(window,n);return()=>o()},[n,r])}function De(e){let t=J(null),r=B(()=>{if(t.current)t.current.focus()},[]);return T({[e]:r}),X(()=>({ref:t}),[])}import{useCallback as N,useRef as Q}from"react";import{useState as O}from"react";function C({name:e,defaultValue:t=!1}){let[r,n]=O(t);return{on:r,off:!r,enable:()=>n(!0),disable:()=>n(!1),toggle:()=>n((a)=>!a),props:{controller:{"aria-expanded":r?"true":"false","aria-controls":e,role:"button",tabIndex:0},target:{id:e,role:"region","aria-hidden":r?"false":"true"}}}}function b(e){let{on:t,off:r,enable:n,disable:o,toggle:i,props:m,...x}=e;return{toggle:{on:t,off:r,enable:n,disable:o,toggle:i,props:m},rest:x}}function Je({enabled:e=!0}={}){let{on:t,enable:r,disable:n}=C({name:"is-hovering"}),o=Q(null),i=typeof window<"u"&&"PointerEvent"in window?"pointerenter":"mouseenter",m=typeof window<"u"&&"PointerEvent"in window?"pointerleave":"mouseleave";return{attach:{ref:N((p)=>{let a=o.current;if(a)a.removeEventListener(i,r),a.removeEventListener(m,n);if(o.current=p,p&&e)p.addEventListener(i,r),p.addEventListener(m,n)},[i,m,e,r,n])},isHovering:t&&e}}import Y from"js-cookie";import{useCallback as z,useEffect as V}from"react";import{useRevalidator as ee}from"react-router";import{createContext as Z,use as f,useCallback as j}from"react";import{polishPlurals as G}from"polish-plurals";function F(e){if(e.language==="en"){let t=e.plural??`${e.singular}s`;if(e.value===1)return e.singular;return t}if(e.language==="pl"){let t=e.value??1;if(t===1)return e.singular;return G(e.singular,String(e.plural),String(e.genitive),t)}return console.warn(`[@bgord/frontend] missing pluralization function for language: ${e.language}.`),e.singular}var E=Z({translations:{},language:"en",supportedLanguages:{en:"en"}});function je(){let e=f(E);if(e===void 0)throw Error("useTranslations must be used within the TranslationsContext");return j((r,n)=>{let o=e.translations[r];if(!o)return console.warn(`[@bgord/ui] missing translation for key: ${r}`),r;if(!n)return o;return Object.entries(n).reduce((i,[m,x])=>{let p=new RegExp(`{{${m}}}`,"g");return i.replace(p,String(x))},o)},[e.translations])}function R(){let e=f(E);if(e===void 0)throw Error("useLanguage must be used within the TranslationsContext");return e.language}function Ye(){let e=f(E);if(e===void 0)throw Error("useSupportedLanguages must be used within the TranslationsContext");return e.supportedLanguages}function ze(){let e=R();return(t)=>F({...t,language:e})}function mt(e){let t=R(),r=ee(),n=A({enum:e,defaultValue:t,name:"language"}),o=z(()=>{let i=new s(n.currentValue);if(!i.isEmpty()&&n.changed)Y.set("language",String(i.get())),r.revalidate()},[n.currentValue,n.changed]);return V(()=>{o()},[o]),n}import{useCallback as te,useMemo as re}from"react";function pt(){let e=te((t)=>{if(t.key!=="Enter"||!t.metaKey)return;t.preventDefault(),t.currentTarget.form?.requestSubmit()},[]);return re(()=>({onKeyDown:e}),[e])}import{useEffect as ne}from"react";function H(e=!0){ne(()=>{if(typeof document>"u")return;let t=document.body.style.overflow;if(e)document.body.style.overflow="hidden";return()=>{document.body.style.overflow=t}},[e])}import{jsx as se}from"react/jsx-runtime";function Ct(e){let{toggle:t,rest:r}=b(e),n=me(null);return ie(()=>{if(e.on)n.current?.showModal();else n.current?.close()},[e.on]),T({Escape:t.disable}),H(e.on),g(n,t.disable),se("dialog",{ref:n,tabIndex:0,"aria-modal":"true","data-disp":e.on?"flex":"none","data-dir":"column","data-mx":"auto","data-p":"5","data-position":"fixed","data-z":"2","data-bg":"neutral-900","data-br":"xs","data-backdrop":"stronger","data-animation":"grow-fade-in",...r})}import{useEffect as ue}from"react";import{useRevalidator as pe}from"react-router";function kt(){let e=pe();return ue(()=>{let t=()=>e.revalidate();return window.addEventListener("focus",t),()=>window.removeEventListener("focus",t)},[e]),null}import{redirect as w}from"react-router";class d{static extractFrom(e){return e.headers.get("cookie")??""}}class ae{API_URL;constructor(e){this.API_URL=`${e}/api/auth`}async getServerSession(e){let t=d.extractFrom(e),r=await fetch(`${this.API_URL}/get-session`,{headers:{cookie:t,accept:"application/json"}});if(!r.ok)return null;return await r.json()}async requireSession(e){let t=await this.getServerSession(e);if(t?.user)return t;throw w("/")}async requireNoSession(e,t="/home"){if((await this.getServerSession(e))?.user)throw w(t)}async removeSession(e,t="/login"){let r=d.extractFrom(e),n=await fetch(`${import.meta.env.VITE_API_URL}/api/auth/sign-out`,{method:"POST",headers:{cookie:r}}),o=new Headers;throw n.headers.forEach((i,m)=>{if(m.toLowerCase()==="set-cookie")o.append("set-cookie",i)}),w(t,{headers:o})}}function S(){}var xe=()=>console.warn("Copying to clipboard not supported");async function Bt(e){let t=e.onFailure??xe,r=e.onSuccess??S;if(!navigator.clipboard)t();try{await navigator.clipboard.writeText(e.text),r()}catch(n){t(n)}}var Jt={email:{inputMode:"email",autoComplete:"email",autoCapitalize:"none",spellCheck:"false"},password:{new:{autoComplete:"new-password"},current:{autoComplete:"current-password"}}};class ce{static fromRevision(e){return{"if-match":String(e)}}}function Qt(e){return function(){for(let t of e)t()}}class ye{static allUnchanged(e){return e.every((t)=>t.unchanged)}static allEmpty(e){return e.every((t)=>t.empty)}static anyEmpty(e){return e.some((t)=>t.empty)}static anyUnchanged(e){return e.some((t)=>t.unchanged)}static anyChanged(e){return e.some((t)=>t.changed)}}class Te{static inputPattern(e){let t=e.required??!0;if(e.min&&!e.max)return{pattern:`.{${e.min}}`,required:t};if(e.min&&e.max)return{pattern:`.{${e.min},${e.max}}`,required:t};if(!e.min&&e.max)return{pattern:`.{,${e.max}}`,required:t};return{pattern:void 0,required:t}}static textareaPattern(e){let t=e.required??!0;if(e.min&&!e.max)return{minLength:e.min,required:t};if(e.min&&e.max)return{minLength:e.min,maxLength:e.max,required:t};if(!e.min&&e.max)return{maxLength:e.max,required:t};return{required:t}}}function Yt(){if(typeof window>"u")return;return window}function Vt(e=12){return{times(t){let r=e*t,n={height:{height:u(r)},minHeight:{minHeight:u(r)},maxHeight:{maxHeight:u(r)},width:{width:u(r)},minWidth:{minWidth:u(r)},maxWidth:{maxWidth:u(r)},square:{height:u(r),width:u(r)}},o={height:{style:{height:u(r)}},minHeight:{style:{minHeight:u(r)}},maxHeight:{style:{maxHeight:u(r)}},width:{style:{width:u(r)}},minWidth:{style:{minWidth:u(r)}},maxWidth:{style:{maxWidth:u(r)}},square:{style:{height:u(r),width:u(r)}}};return{px:u(r),raw:r,style:o,...n}}}}function u(e){return`${e}px`}class de{static fromRevision(e){return{"if-match":`W/${e}`}}}export{je as useTranslations,C as useToggle,Ye as useSupportedLanguages,H as useScrollLock,ze as usePluralize,pt as useMetaEnterSubmit,mt as useLanguageSelector,R as useLanguage,T as useKeyboardShortcuts,Je as useHover,De as useFocusKeyboardShortcut,l as useFieldStrategyEnum,L as useField,Ue as useExitAction,A as useClientFilter,g as useClickOutside,F as pluralize,S as noop,Yt as getSafeWindow,b as extractUseToggle,Qt as exec,Bt as copyToClipboard,de as WeakETag,E as TranslationsContext,Vt as Rhythm,kt as RevalidateOnFocus,I as LocalFields,Te as Form,ye as Fields,s as Field,ce as ETag,Ct as Dialog,Jt as Credentials,d as Cookies,he as Button,ae as AuthGuard};
1
+ import{useEffect as Z,useRef as V}from"react";import{useEffect as H}from"react";function R(e,t){H(()=>{if(typeof document>"u")return;function n(r){let i=e.current;if(!i)return;if(i.contains(r.target))if(r.target===i){let{left:o,right:s,top:l,bottom:u}=i.getBoundingClientRect(),m=r instanceof MouseEvent?r.clientX:r.touches[0].clientX,d=r instanceof MouseEvent?r.clientY:r.touches[0].clientY;if(m>=o&&m<=s&&d>=l&&d<=u)return}else return;t(r)}return document.addEventListener("mousedown",n),document.addEventListener("touchstart",n),()=>{document.removeEventListener("mousedown",n),document.removeEventListener("touchstart",n)}},[e,t])}import{useCallback as q,useMemo as b}from"react";class a{static emptyValue=void 0;static isEmpty(e){return e===void 0||e===""||e===null}static compare(e,t){if(a.isEmpty(e)&&a.isEmpty(t))return!0;return e===t}value=a.emptyValue;constructor(e){this.value=a.isEmpty(e)?a.emptyValue:e}get(){return this.value}isEmpty(){return a.isEmpty(this.value)}}import{useEffect as k,useState as M}from"react";import{useSearchParams as X}from"react-router";var S;((n)=>{n.params="params";n.local="local"})(S||={});function w(e){let t=e.strategy??"local",[n,r]=X(),i=new a(n.get(e.name)),o=new a(e.defaultValue),[s,l]=M(i.isEmpty()?o.get():i.get()),u=(c)=>{let g=new a(c);l(g.get())};k(()=>{let c=new a(s);if(t==="params")if(c.isEmpty())n.delete(e.name),r(n);else n.set(e.name,c.get()),r(n)},[s,n,r,e.name,t]);let m=a.isEmpty(s)?"":s,d=(c)=>u(c.currentTarget.value);return{strategy:t,defaultValue:o.get(),currentValue:s,value:m,set:u,handleChange:d,clear:()=>u(o.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:m,onChange:d}},changed:!a.compare(s,o.get()),unchanged:a.compare(s,o.get()),empty:a.isEmpty(s)}}class O{static clearAll(e){return()=>e.forEach((t)=>t.clear())}}function Ue(e){let t=w({...e,strategy:"local"}),n=q((o)=>{if(t.empty)return!0;return a.compare(o,t.currentValue)},[t.empty,t.currentValue]),r=b(()=>e.filterFn??n,[e.filterFn,n]),i=b(()=>Object.entries(e.enum).map(([o,s])=>({name:o,value:s})),[e.enum]);return b(()=>({...t,filterFn:r,options:i,strategy:"local"}),[t,r,i])}import W from"react";function Se(e){let[t,n]=W.useState("idle"),r=(s)=>{if(s.preventDefault(),t==="idle")n("exiting")},i=(s)=>{if(s.animationName!==e.animation)return;e.action(),n("gone")},o=t==="exiting"?{"data-animation":e.animation,onAnimationEnd:i}:void 0;return{visible:t!=="gone",attach:o,trigger:r}}import{useMemo as N,useState as E}from"react";var P;((r)=>{r.idle="idle";r.selected="selected";r.error="error"})(P||={});function Ae(e,t){let n=t?.maxSizeBytes??Number.POSITIVE_INFINITY,[r,i]=E(0),[o,s]=E("idle"),[l,u]=E(null);function m(y){let f=y.currentTarget.files;if(!f?.[0])return;let T=f[0];if(T.size>n){s("error");return}if(!t.mimeTypes.includes(T.type)){s("error");return}return u(T),s("selected"),T}function d(){i((y)=>y+1),u(null),s("idle")}let c=N(()=>l?URL.createObjectURL(l):void 0,[l]);function g(y){return y.some((f)=>f===o)}let h={actions:{selectFile:m,clearFile:d},input:{props:{id:e,name:e,multiple:!1,accept:t.mimeTypes.join(",")},key:r},label:{props:{htmlFor:e}},matches:g};if(o==="idle")return{data:null,isError:!1,isIdle:!0,isSelected:!1,state:o,...h};if(o==="selected")return{data:l,isError:!1,isIdle:!1,isSelected:!0,preview:c,state:o,...h};return{data:null,isError:!0,isIdle:!1,isSelected:!1,state:o,...h}}import{useCallback as B,useMemo as K,useRef as $}from"react";import{useEffect as D}from"react";import{tinykeys as J}from"tinykeys";function x(e,t){let n=t?.enabled??!0;D(()=>{if(!n)return;let r=J(window,e);return()=>r()},[e,n])}function Xe(e){let t=$(null),n=B(()=>{if(t.current)t.current.focus()},[]);return x({[e]:n}),K(()=>({ref:t}),[])}import{useCallback as j,useRef as Y}from"react";import{useState as _}from"react";function C({name:e,defaultValue:t=!1}){let[n,r]=_(t);return{on:n,off:!n,enable:()=>r(!0),disable:()=>r(!1),toggle:()=>r((m)=>!m),props:{controller:{"aria-expanded":n?"true":"false","aria-controls":e,role:"button",tabIndex:0},target:{id:e,role:"region","aria-hidden":n?"false":"true"}}}}function A(e){let{on:t,off:n,enable:r,disable:i,toggle:o,props:s,...l}=e;return{toggle:{on:t,off:n,enable:r,disable:i,toggle:o,props:s},rest:l}}function De(e){let t=e?.enabled??!0,n=C({name:"_internal"}),r=Y(null),i=typeof window<"u"&&"PointerEvent"in window?"pointerenter":"mouseenter",o=typeof window<"u"&&"PointerEvent"in window?"pointerleave":"mouseleave";return{attach:{ref:j((l)=>{let u=r.current;if(u)u.removeEventListener(i,n.enable),u.removeEventListener(o,n.disable);if(r.current=l,l&&t)l.addEventListener(i,n.enable),l.addEventListener(o,n.disable)},[i,o,t,n.enable,n.disable])},hovering:n.on&&t}}import{useCallback as Q}from"react";function Ke(){return{onKeyDown:Q((t)=>{if(!t.metaKey||t.key!=="Enter")return;t.preventDefault(),t.currentTarget.form?.requestSubmit()},[])}}import{useEffect as z}from"react";function I(e=!0){z(()=>{if(typeof document>"u")return;let t=document.body.style.overflow;if(e)document.body.style.overflow="hidden";return()=>{document.body.style.overflow=t}},[e])}import{jsx as ee}from"react/jsx-runtime";function st(e){let{toggle:t,rest:n}=A(e),r=V(null);return Z(()=>{if(e.on)r.current?.showModal();else r.current?.close()},[e.on]),x({Escape:t.disable}),I(e.on),R(r,t.disable),ee("dialog",{ref:r,tabIndex:0,"aria-modal":"true","data-disp":e.on?"flex":"none","data-dir":"column","data-mx":"auto","data-p":"5","data-position":"fixed","data-z":"2","data-bg":"neutral-900","data-br":"xs","data-backdrop":"stronger","data-animation":"grow-fade-in",...n})}var mt={email:{inputMode:"email",autoComplete:"email",autoCapitalize:"none",spellCheck:"false"},password:{new:{autoComplete:"new-password"},current:{autoComplete:"current-password"}},off:{autoComplete:"off",spellCheck:!1}};function L(){}class te{static async copy(e){let t=e.onSuccess??L;if(!navigator.clipboard)return;await navigator.clipboard.writeText(e.text),t()}}import ne from"js-cookie";class re{static extractFrom(e){return e.headers.get("cookie")??""}static set(e,t){ne.set(e,t)}}class oe{static fromRevision(e){return{"if-match":String(e)}}}function ht(e){return function(){for(let t of e)t()}}class ie{static allUnchanged(e){return e.every((t)=>t.unchanged)}static allEmpty(e){return e.every((t)=>t.empty)}static anyEmpty(e){return e.some((t)=>t.empty)}static anyUnchanged(e){return e.some((t)=>t.unchanged)}static anyChanged(e){return e.some((t)=>t.changed)}}class se{static input(e){let t=e.required??!0;if(e.min&&!e.max)return{pattern:`.{${e.min}}`,required:t};if(e.min&&e.max)return{pattern:`.{${e.min},${e.max}}`,required:t};if(!e.min&&e.max)return{pattern:`.{,${e.max}}`,required:t};return{pattern:void 0,required:t}}static textarea(e){let t=e.required??!0;if(e.min&&!e.max)return{minLength:e.min,required:t};if(e.min&&e.max)return{minLength:e.min,maxLength:e.max,required:t};if(!e.min&&e.max)return{maxLength:e.max,required:t};return{required:t}}static exact(e){let t=e.required??!0;return{pattern:e.text,required:t}}}function Ft(){if(typeof window>"u")return;return window}import{polishPlurals as le}from"polish-plurals";function v(e){if(e.language==="en"){let t=e.plural??`${e.singular}s`;if(e.value===1)return e.singular;return t}if(e.language==="pl"){let t=e.value??1;if(t===1)return e.singular;return le(e.singular,String(e.plural),String(e.genitive),t)}return console.warn(`[@bgord/frontend] missing pluralization function for language: ${e.language}.`),e.singular}function Ct(e=12){return{times(t){let n=e*t,r={height:{height:p(n)},minHeight:{minHeight:p(n)},maxHeight:{maxHeight:p(n)},width:{width:p(n)},minWidth:{minWidth:p(n)},maxWidth:{maxWidth:p(n)},square:{height:p(n),width:p(n)}},i={height:{style:{height:p(n)}},minHeight:{style:{minHeight:p(n)}},maxHeight:{style:{maxHeight:p(n)}},width:{style:{width:p(n)}},minWidth:{style:{minWidth:p(n)}},maxWidth:{style:{maxWidth:p(n)}},square:{style:{height:p(n),width:p(n)}}};return{px:p(n),raw:n,style:i,...r}}}}function p(e){return`${e}px`}import{createContext as ae,use as U,useCallback as pe}from"react";var F=ae({translations:{},language:"en",supportedLanguages:{en:"en"}});function vt(){let e=U(F);if(e===void 0)throw Error("useTranslations must be used within the TranslationsContext");return pe((n,r)=>{let i=e.translations[n];if(!i)return console.warn(`[@bgord/ui] missing translation for key: ${n}`),n;if(!r)return i;return Object.entries(r).reduce((o,[s,l])=>{let u=new RegExp(`{{${s}}}`,"g");return o.replace(u,String(l))},i)},[e.translations])}function ue(){let e=U(F);if(e===void 0)throw Error("useLanguage must be used within the TranslationsContext");return e.language}function Ht(){let e=U(F);if(e===void 0)throw Error("useSupportedLanguages must be used within the TranslationsContext");return e.supportedLanguages}function kt(){let e=ue();return(t)=>v({...t,language:e})}class me{static fromRevision(e){return{"if-match":`W/${e}`}}}export{vt as useTranslations,C as useToggle,Ht as useSupportedLanguages,x as useShortcuts,I as useScrollLock,kt as usePluralize,Ke as useMetaEnterSubmit,ue as useLanguage,De as useHover,Xe as useFocusKeyboardShortcut,Ae as useFile,S as useFieldStrategyEnum,w as useField,Se as useExitAction,Ue as useClientFilter,R as useClickOutside,v as pluralize,L as noop,Ft as getSafeWindow,A as extractUseToggle,ht as exec,me as WeakETag,P as UseFileState,F as TranslationsContext,Ct as Rhythm,O as LocalFields,se as Form,ie as Fields,a as Field,oe as ETag,st as Dialog,re as Cookies,te as Clipboard,mt as Autocomplete};
@@ -1,9 +1,10 @@
1
- type CredentialsType = {
1
+ type AutocompleteType = {
2
2
  email: React.JSX.IntrinsicElements["input"];
3
3
  password: {
4
4
  new: React.JSX.IntrinsicElements["input"];
5
5
  current: React.JSX.IntrinsicElements["input"];
6
6
  };
7
+ off: React.JSX.IntrinsicElements["input"];
7
8
  };
8
- export declare const Credentials: CredentialsType;
9
+ export declare const Autocomplete: AutocompleteType;
9
10
  export {};
@@ -0,0 +1,7 @@
1
+ export type CopyToClipboardOptionsType = {
2
+ text: string;
3
+ onSuccess?: VoidFunction;
4
+ };
5
+ export declare class Clipboard {
6
+ static copy(options: CopyToClipboardOptionsType): Promise<void>;
7
+ }
@@ -1,3 +1,4 @@
1
1
  export declare class Cookies {
2
2
  static extractFrom(request: Request): string;
3
+ static set(name: string, value: string): void;
3
4
  }
@@ -1,45 +1,16 @@
1
- /**
2
- * Utility class for working with multiple fields
3
- * @static
4
- */
5
1
  export declare class Fields {
6
- /**
7
- * Check if all fields are unchanged
8
- * @param {Array<{unchanged: boolean}>} fields - Array of field states
9
- * @returns {boolean} True if all fields match their default values
10
- */
11
2
  static allUnchanged(fields: {
12
3
  unchanged: boolean;
13
4
  }[]): boolean;
14
- /**
15
- * Check if all fields are empty
16
- * @param {Array<{empty: boolean}>} fields - Array of field states
17
- * @returns {boolean} True if all fields are empty
18
- */
19
5
  static allEmpty(fields: {
20
6
  empty: boolean;
21
7
  }[]): boolean;
22
- /**
23
- * Check if any field is empty
24
- * @param {Array<{empty: boolean}>} fields - Array of field states
25
- * @returns {boolean} True if any field is empty
26
- */
27
8
  static anyEmpty(fields: {
28
9
  empty: boolean;
29
10
  }[]): boolean;
30
- /**
31
- * Check if any field is unchanged
32
- * @param {Array<{unchanged: boolean}>} fields - Array of field states
33
- * @returns {boolean} True if any field matches its default value
34
- */
35
11
  static anyUnchanged(fields: {
36
12
  unchanged: boolean;
37
13
  }[]): boolean;
38
- /**
39
- * Check if any field has changed
40
- * @param {Array<{changed: boolean}>} fields - Array of field states
41
- * @returns {boolean} True if any field differs from its default value
42
- */
43
14
  static anyChanged(fields: {
44
15
  changed: boolean;
45
16
  }[]): boolean;
@@ -1,11 +1,16 @@
1
1
  import type React from "react";
2
- type PatternConfigType = {
2
+ type PatternTextConfigType = {
3
3
  min?: number;
4
4
  max?: number;
5
5
  required?: React.JSX.IntrinsicElements["input"]["required"];
6
6
  };
7
+ type PatternExactConfigType = {
8
+ text: string;
9
+ required?: React.JSX.IntrinsicElements["input"]["required"];
10
+ };
7
11
  export declare class Form {
8
- static inputPattern(config: PatternConfigType): React.ComponentPropsWithoutRef<"input">;
9
- static textareaPattern(config: PatternConfigType): React.ComponentPropsWithoutRef<"textarea">;
12
+ static input(config: PatternTextConfigType): React.ComponentPropsWithoutRef<"input">;
13
+ static textarea(config: PatternTextConfigType): React.ComponentPropsWithoutRef<"textarea">;
14
+ static exact(config: PatternExactConfigType): React.ComponentPropsWithoutRef<"input">;
10
15
  }
11
16
  export {};
@@ -1,7 +1,6 @@
1
- export * from "./auth-guard";
1
+ export * from "./autocomplete";
2
+ export * from "./clipboard";
2
3
  export * from "./cookies";
3
- export * from "./copy-to-clipboard";
4
- export * from "./credentials";
5
4
  export * from "./etag";
6
5
  export * from "./exec";
7
6
  export * from "./field";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/ui",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -24,7 +24,7 @@
24
24
  "access": "public"
25
25
  },
26
26
  "devDependencies": {
27
- "@biomejs/biome": "2.2.7",
27
+ "@biomejs/biome": "2.3.0",
28
28
  "@commitlint/cli": "20.1.0",
29
29
  "@commitlint/config-conventional": "20.0.0",
30
30
  "@happy-dom/global-registrator": "20.0.8",
@@ -32,18 +32,18 @@
32
32
  "@testing-library/jest-dom": "6.9.1",
33
33
  "@testing-library/react": "16.3.0",
34
34
  "@testing-library/user-event": "14.6.1",
35
- "@types/bun": "1.3.0",
35
+ "@types/bun": "1.3.1",
36
36
  "@types/js-cookie": "3.0.6",
37
37
  "@types/react": "19.2.2",
38
38
  "@types/react-dom": "19.2.2",
39
39
  "cspell": "9.2.2",
40
- "knip": "5.66.2",
41
- "lefthook": "2.0.0",
40
+ "knip": "5.66.3",
41
+ "lefthook": "2.0.1",
42
42
  "only-allow": "1.2.1",
43
43
  "shellcheck": "4.1.0"
44
44
  },
45
45
  "dependencies": {
46
- "better-auth": "1.3.29",
46
+ "better-auth": "1.3.31",
47
47
  "js-cookie": "3.0.5",
48
48
  "polish-plurals": "1.1.0",
49
49
  "tinykeys": "3.0.0"
package/readme.md CHANGED
@@ -25,26 +25,23 @@ Run the tests
25
25
  ```
26
26
  src/
27
27
  ├── components
28
- │   ├── button.tsx
29
28
  │   ├── dialog.tsx
30
- │   └── revalidate-on-focus.tsx
31
29
  ├── hooks
32
30
  │   ├── use-click-outside.ts
33
31
  │   ├── use-client-filter.ts
34
32
  │   ├── use-exit-action.ts
35
33
  │   ├── use-field.ts
34
+ │   ├── use-file.ts
36
35
  │   ├── use-focus-shortcut.ts
37
36
  │   ├── use-hover.ts
38
- │   ├── use-language-selector.tsx
39
- │   ├── use-meta-enter-submit.tsx
37
+ │   ├── use-meta-enter-submit.ts
40
38
  │   ├── use-scroll-lock.ts
41
39
  │   ├── use-shortcuts.ts
42
40
  │   └── use-toggle.ts
43
41
  └── services
44
- ├── auth-guard.ts
42
+ ├── autocomplete.ts
43
+ ├── clipboard.ts
45
44
  ├── cookies.ts
46
- ├── copy-to-clipboard.ts
47
- ├── credentials.ts
48
45
  ├── etag.ts
49
46
  ├── exec.ts
50
47
  ├── field.ts
@@ -1 +0,0 @@
1
- export declare function Button(): import("react/jsx-runtime").JSX.Element;
@@ -1 +0,0 @@
1
- export declare function RevalidateOnFocus(): null;
@@ -1,4 +0,0 @@
1
- import { type useClientFilterReturnType } from "./use-client-filter";
2
- type LanguageType = string;
3
- export declare function useLanguageSelector(supportedLanguages: Record<LanguageType, LanguageType>): useClientFilterReturnType<LanguageType>;
4
- export {};
@@ -1,9 +0,0 @@
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
- }
@@ -1,10 +0,0 @@
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 {};