@bgord/ui 0.6.3 → 0.7.1

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,4 +1,6 @@
1
1
  import type React from "react";
2
2
  import * as hooks from "../hooks";
3
- export type DialogPropsType = hooks.UseToggleReturnType & React.JSX.IntrinsicElements["dialog"];
3
+ export type DialogPropsType = hooks.UseToggleReturnType & React.JSX.IntrinsicElements["dialog"] & {
4
+ locked?: boolean;
5
+ };
4
6
  export declare function Dialog(props: DialogPropsType): import("react/jsx-runtime").JSX.Element;
@@ -1,11 +1,11 @@
1
1
  export * from "./use-click-outside";
2
- export * from "./use-client-filter";
3
2
  export * from "./use-exit-action";
4
- export * from "./use-field";
5
3
  export * from "./use-file";
6
4
  export * from "./use-focus-shortcut";
7
5
  export * from "./use-hover";
8
6
  export * from "./use-meta-enter-submit";
7
+ export * from "./use-number-field";
9
8
  export * from "./use-scroll-lock";
10
9
  export * from "./use-shortcuts";
10
+ export * from "./use-text-field";
11
11
  export * from "./use-toggle";
@@ -0,0 +1,32 @@
1
+ import { type NumberFieldValueType } from "../services/number-field";
2
+ type NumberFieldNameType = string;
3
+ export type NumberFieldElementType = HTMLInputElement;
4
+ export type UseNumberFieldConfigType<T extends number = number> = {
5
+ name: NumberFieldNameType;
6
+ defaultValue?: NumberFieldValueType<T>;
7
+ };
8
+ export type UseNumberFieldReturnType<T extends number = number> = {
9
+ defaultValue: NumberFieldValueType<T>;
10
+ value: NumberFieldValueType<T>;
11
+ set: (value: NumberFieldValueType<T>) => void;
12
+ handleChange: (event: React.ChangeEvent<NumberFieldElementType>) => void;
13
+ clear: () => void;
14
+ label: {
15
+ props: {
16
+ htmlFor: NumberFieldNameType;
17
+ };
18
+ };
19
+ input: {
20
+ props: {
21
+ id: NumberFieldNameType;
22
+ name: NumberFieldNameType;
23
+ value: string;
24
+ onChange: (event: React.ChangeEvent<NumberFieldElementType>) => void;
25
+ };
26
+ };
27
+ changed: boolean;
28
+ unchanged: boolean;
29
+ empty: boolean;
30
+ };
31
+ export declare function useNumberField<T extends number = number>(config: UseNumberFieldConfigType<T>): UseNumberFieldReturnType<T>;
32
+ export {};
@@ -0,0 +1,32 @@
1
+ import { type TextFieldValueType } from "../services/text-field";
2
+ type TextFieldNameType = string;
3
+ export type TextFieldElementType = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
4
+ export type UseTextFieldConfigType<T extends string = string> = {
5
+ name: TextFieldNameType;
6
+ defaultValue?: TextFieldValueType<T>;
7
+ };
8
+ export type UseTextFieldReturnType<T extends string = string> = {
9
+ defaultValue: TextFieldValueType<T>;
10
+ value: TextFieldValueType<T>;
11
+ set: (value: TextFieldValueType<T>) => void;
12
+ handleChange: (event: React.ChangeEvent<TextFieldElementType>) => void;
13
+ clear: () => void;
14
+ label: {
15
+ props: {
16
+ htmlFor: TextFieldNameType;
17
+ };
18
+ };
19
+ input: {
20
+ props: {
21
+ id: TextFieldNameType;
22
+ name: TextFieldNameType;
23
+ value: string;
24
+ onChange: (event: React.ChangeEvent<TextFieldElementType>) => void;
25
+ };
26
+ };
27
+ changed: boolean;
28
+ unchanged: boolean;
29
+ empty: boolean;
30
+ };
31
+ export declare function useTextField<T extends string = string>(config: UseTextFieldConfigType<T>): UseTextFieldReturnType<T>;
32
+ export {};
package/dist/index.js CHANGED
@@ -1 +1 @@
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
+ import{useEffect as Y,useRef as j}from"react";import{useEffect as A}from"react";function F(e,t){A(()=>{if(typeof document>"u")return;function n(r){let o=e.current;if(!o)return;if(o.contains(r.target))if(r.target===o){let{left:i,right:s,top:a,bottom:l}=o.getBoundingClientRect(),u=r instanceof MouseEvent?r.clientX:r.touches[0].clientX,f=r instanceof MouseEvent?r.clientY:r.touches[0].clientY;if(u>=i&&u<=s&&f>=a&&f<=l)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 H from"react";function ce(e){let[t,n]=H.useState("idle"),r=(s)=>{if(s.preventDefault(),t==="idle")n("exiting")},o=(s)=>{if(s.animationName!==e.animation)return;e.action(),n("gone")},i=t==="exiting"?{"data-animation":e.animation,onAnimationEnd:o}:void 0;return{visible:t!=="gone",attach:i,trigger:r}}import{useMemo as k,useState as E}from"react";var M;((r)=>{r.idle="idle";r.selected="selected";r.error="error"})(M||={});function de(e,t){let n=t?.maxSizeBytes??Number.POSITIVE_INFINITY,[r,o]=E(0),[i,s]=E("idle"),[a,l]=E(null);function u(d){let T=d.currentTarget.files;if(!T?.[0])return;let x=T[0];if(x.size>n){s("error");return}if(!t.mimeTypes.includes(x.type)){s("error");return}return l(x),s("selected"),x}function f(){o((d)=>d+1),l(null),s("idle")}let R=k(()=>a?URL.createObjectURL(a):void 0,[a]);function v(d){return d.some((T)=>T===i)}let h={actions:{selectFile:u,clearFile:f},input:{props:{id:e,name:e,multiple:!1,accept:t.mimeTypes.join(",")},key:r},label:{props:{htmlFor:e}},matches:v};if(i==="idle")return{data:null,isError:!1,isIdle:!0,isSelected:!1,state:i,...h};if(i==="selected")return{data:a,isError:!1,isIdle:!1,isSelected:!0,preview:R,state:i,...h};return{data:null,isError:!0,isIdle:!1,isSelected:!1,state:i,...h}}import{useCallback as W,useMemo as q,useRef as V}from"react";import{useEffect as O}from"react";import{tinykeys as P}from"tinykeys";function g(e,t){let n=t?.enabled??!0;O(()=>{if(!n)return;let r=P(window,e);return()=>r()},[e,n])}function Ee(e){let t=V(null),n=W(()=>{if(t.current)t.current.focus()},[]);return g({[e]:n}),q(()=>({ref:t}),[])}import{useCallback as D,useRef as B}from"react";import{useState as X}from"react";function S({name:e,defaultValue:t=!1}){let[n,r]=X(t);return{on:n,off:!n,enable:()=>r(!0),disable:()=>r(!1),toggle:()=>r((u)=>!u),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 w(e){let{on:t,off:n,enable:r,disable:o,toggle:i,props:s,...a}=e;return{toggle:{on:t,off:n,enable:r,disable:o,toggle:i,props:s},rest:a}}function we(e){let t=e?.enabled??!0,n=S({name:"_internal"}),r=B(null),o=typeof window<"u"&&"PointerEvent"in window?"pointerenter":"mouseenter",i=typeof window<"u"&&"PointerEvent"in window?"pointerleave":"mouseleave";return{attach:{ref:D((a)=>{let l=r.current;if(l)l.removeEventListener(o,n.enable),l.removeEventListener(i,n.disable);if(r.current=a,a&&t)a.addEventListener(o,n.enable),a.addEventListener(i,n.disable)},[o,i,t,n.enable,n.disable])},hovering:n.on&&t}}import{useCallback as K}from"react";function Ie(){return{onKeyDown:K((t)=>{if(!t.metaKey||t.key!=="Enter")return;t.preventDefault(),t.currentTarget.form?.requestSubmit()},[])}}import{useState as C}from"react";class c{static EMPTY=void 0;value=c.EMPTY;constructor(e){this.value=c.isEmpty(e)?c.EMPTY:e}get(){return this.value}isEmpty(){return c.isEmpty(this.value)}static isEmpty(e){return e===void 0||e===null||Number.isNaN(e)}static compare(e,t){if(c.isEmpty(e)&&c.isEmpty(t))return!0;return e===t}}function Me(e){let t=new c(e.defaultValue),[n,r]=C(t.get()),[o,i]=C(c.isEmpty(t.get())?"":String(t.get())),s=(l)=>{let u=new c(l).get();r(u),i(c.isEmpty(u)?"":String(u))},a=(l)=>{if(i(l.currentTarget.value),l.currentTarget.value==="")return r(c.EMPTY);let u=l.currentTarget.valueAsNumber;if(Number.isFinite(u))r(u)};return{defaultValue:t.get(),value:n,set:s,handleChange:a,clear:()=>s(t.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:o,onChange:a}},changed:!c.compare(n,t.get()),unchanged:c.compare(n,t.get()),empty:c.isEmpty(n)}}import{useEffect as _}from"react";function L(e=!0){_(()=>{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{useState as $}from"react";class m{static EMPTY=void 0;value=m.EMPTY;constructor(e){this.value=m.isEmpty(e)?m.EMPTY:e}get(){return this.value}isEmpty(){return m.isEmpty(this.value)}static isEmpty(e){return e===void 0||e===""||e===null}static compare(e,t){if(m.isEmpty(e)&&m.isEmpty(t))return!0;return e===t}}function De(e){let t=new m(e.defaultValue),[n,r]=$(t.get()),o=(s)=>r(new m(s).get()),i=(s)=>o(s.currentTarget.value);return{defaultValue:t.get(),value:n,set:o,handleChange:i,clear:()=>o(t.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:m.isEmpty(n)?"":n,onChange:i}},changed:!m.compare(n,t.get()),unchanged:m.compare(n,t.get()),empty:m.isEmpty(n)}}function y(){}import{jsx as z}from"react/jsx-runtime";function rt(e){let t=e.locked??!1,{toggle:n,rest:r}=w(e),o=j(null);return Y(()=>{if(e.on)o.current?.showModal();else o.current?.close()},[e.on]),g({Escape:t?y:n.disable}),L(e.on),F(o,t?y:n.disable),z("dialog",{ref:o,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})}var lt={email:{inputMode:"email",autoComplete:"email",autoCapitalize:"none",spellCheck:"false"},password:{new:{autoComplete:"new-password"},current:{autoComplete:"current-password"}},off:{autoComplete:"off",spellCheck:!1}};class G{static async copy(e){let t=e.onSuccess??y;if(!navigator.clipboard)return;await navigator.clipboard.writeText(e.text),t()}}import Q from"js-cookie";class Z{static extractFrom(e){return e.headers.get("cookie")??""}static set(e,t){Q.set(e,t)}}class N{static fromRevision(e){return{"if-match":String(e)}}}function ft(e){return function(){for(let t of e)t()}}class ee{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 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}}static date={min:{today:()=>new Date().toISOString().split("T")[0]},max:{today:()=>new Date().toISOString().split("T")[0]}}}function ht(){if(typeof window>"u")return;return window}import{polishPlurals as ne}from"polish-plurals";function I(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 ne(e.singular,String(e.plural),String(e.genitive),t)}return console.warn(`[@bgord/frontend] missing pluralization function for language: ${e.language}.`),e.singular}function Rt(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)}},o={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:o,...r}}}}function p(e){return`${e}px`}import{createContext as re,use as U,useCallback as oe}from"react";var b=re({translations:{},language:"en",supportedLanguages:{en:"en"}});function Ct(){let e=U(b);if(e===void 0)throw Error("useTranslations must be used within the TranslationsContext");return oe((n,r)=>{let o=e.translations[n];if(!o)return console.warn(`[@bgord/ui] missing translation for key: ${n}`),n;if(!r)return o;return Object.entries(r).reduce((i,[s,a])=>{let l=new RegExp(`{{${s}}}`,"g");return i.replace(l,String(a))},o)},[e.translations])}function ie(){let e=U(b);if(e===void 0)throw Error("useLanguage must be used within the TranslationsContext");return e.language}function Lt(){let e=U(b);if(e===void 0)throw Error("useSupportedLanguages must be used within the TranslationsContext");return e.supportedLanguages}function It(){let e=ie();return(t)=>I({...t,language:e})}class se{static fromRevision(e){return{"if-match":`W/${e}`}}}export{Ct as useTranslations,S as useToggle,De as useTextField,Lt as useSupportedLanguages,g as useShortcuts,L as useScrollLock,It as usePluralize,Me as useNumberField,Ie as useMetaEnterSubmit,ie as useLanguage,we as useHover,Ee as useFocusKeyboardShortcut,de as useFile,ce as useExitAction,F as useClickOutside,I as pluralize,y as noop,ht as getSafeWindow,w as extractUseToggle,ft as exec,se as WeakETag,M as UseFileState,b as TranslationsContext,m as TextField,Rt as Rhythm,c as NumberField,te as Form,ee as Fields,N as ETag,rt as Dialog,Z as Cookies,G as Clipboard,lt as Autocomplete};
@@ -1,10 +1,20 @@
1
- type AutocompleteType = {
2
- email: React.JSX.IntrinsicElements["input"];
1
+ export declare const Autocomplete: {
2
+ email: {
3
+ inputMode: string;
4
+ autoComplete: string;
5
+ autoCapitalize: string;
6
+ spellCheck: string;
7
+ };
3
8
  password: {
4
- new: React.JSX.IntrinsicElements["input"];
5
- current: React.JSX.IntrinsicElements["input"];
9
+ new: {
10
+ autoComplete: string;
11
+ };
12
+ current: {
13
+ autoComplete: string;
14
+ };
15
+ };
16
+ off: {
17
+ autoComplete: string;
18
+ spellCheck: boolean;
6
19
  };
7
- off: React.JSX.IntrinsicElements["input"];
8
20
  };
9
- export declare const Autocomplete: AutocompleteType;
10
- export {};
@@ -12,5 +12,13 @@ export declare class Form {
12
12
  static input(config: PatternTextConfigType): React.ComponentPropsWithoutRef<"input">;
13
13
  static textarea(config: PatternTextConfigType): React.ComponentPropsWithoutRef<"textarea">;
14
14
  static exact(config: PatternExactConfigType): React.ComponentPropsWithoutRef<"input">;
15
+ static date: {
16
+ min: {
17
+ today: () => string;
18
+ };
19
+ max: {
20
+ today: () => string;
21
+ };
22
+ };
15
23
  }
16
24
  export {};
@@ -3,12 +3,13 @@ export * from "./clipboard";
3
3
  export * from "./cookies";
4
4
  export * from "./etag";
5
5
  export * from "./exec";
6
- export * from "./field";
7
6
  export * from "./fields";
8
7
  export * from "./form";
9
8
  export * from "./get-safe-window";
10
9
  export * from "./noop";
10
+ export * from "./number-field";
11
11
  export * from "./pluralize";
12
12
  export * from "./rhythm";
13
+ export * from "./text-field";
13
14
  export * from "./translations";
14
15
  export * from "./weak-etag";
@@ -0,0 +1,10 @@
1
+ export type NumberFieldValueType<T extends number = number> = T | undefined;
2
+ export declare class NumberField<T extends number = number> {
3
+ static readonly EMPTY: undefined;
4
+ private readonly value;
5
+ constructor(value: NumberFieldValueType<T>);
6
+ get(): NumberFieldValueType<T>;
7
+ isEmpty(): boolean;
8
+ static isEmpty<V extends number>(value: NumberFieldValueType<V> | null): boolean;
9
+ static compare<V extends number>(one: NumberFieldValueType<V>, another: NumberFieldValueType<V>): boolean;
10
+ }
@@ -0,0 +1,10 @@
1
+ export type TextFieldValueType<T extends string = string> = T | undefined;
2
+ export declare class TextField<T extends string = string> {
3
+ static readonly EMPTY: undefined;
4
+ private readonly value;
5
+ constructor(value: TextFieldValueType<T>);
6
+ get(): TextFieldValueType<T>;
7
+ isEmpty(): boolean;
8
+ static isEmpty<V extends string>(value: TextFieldValueType<V>): boolean;
9
+ static compare<V extends string>(one: TextFieldValueType<V>, another: TextFieldValueType<V>): boolean;
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bgord/ui",
3
- "version": "0.6.3",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -13,8 +13,7 @@
13
13
  ],
14
14
  "peerDependencies": {
15
15
  "react": "19.2.0",
16
- "react-dom": "19.2.0",
17
- "react-router": "7.9.4"
16
+ "react-dom": "19.2.0"
18
17
  },
19
18
  "scripts": {
20
19
  "build": "bash scripts/package-build.sh",
@@ -43,7 +42,6 @@
43
42
  "shellcheck": "4.1.0"
44
43
  },
45
44
  "dependencies": {
46
- "better-auth": "1.3.31",
47
45
  "js-cookie": "3.0.5",
48
46
  "polish-plurals": "1.1.0",
49
47
  "tinykeys": "3.0.0"
package/readme.md CHANGED
@@ -28,15 +28,15 @@ src/
28
28
  │   ├── dialog.tsx
29
29
  ├── hooks
30
30
  │   ├── use-click-outside.ts
31
- │   ├── use-client-filter.ts
32
31
  │   ├── use-exit-action.ts
33
- │   ├── use-field.ts
34
32
  │   ├── use-file.ts
35
33
  │   ├── use-focus-shortcut.ts
36
34
  │   ├── use-hover.ts
37
35
  │   ├── use-meta-enter-submit.ts
36
+ │   ├── use-number-field.ts
38
37
  │   ├── use-scroll-lock.ts
39
38
  │   ├── use-shortcuts.ts
39
+ │   ├── use-text-field.ts
40
40
  │   └── use-toggle.ts
41
41
  └── services
42
42
  ├── autocomplete.ts
@@ -44,13 +44,14 @@ src/
44
44
  ├── cookies.ts
45
45
  ├── etag.ts
46
46
  ├── exec.ts
47
- ├── field.ts
48
47
  ├── fields.ts
49
48
  ├── form.ts
50
49
  ├── get-safe-window.ts
51
50
  ├── noop.ts
51
+ ├── number-field.ts
52
52
  ├── pluralize.ts
53
53
  ├── rhythm.ts
54
+ ├── text-field.ts
54
55
  ├── translations.tsx
55
56
  └── weak-etag.ts
56
57
  ```
@@ -1,20 +0,0 @@
1
- import { type FieldValueAllowedTypes } from "../services/field";
2
- import { type useFieldConfigType, type useFieldReturnType, useFieldStrategyEnum } from "./use-field";
3
- export type useClientFilterQueryType = string | undefined;
4
- type useClientFilterConfigType<T extends FieldValueAllowedTypes> = Omit<useFieldConfigType<T>, "strategy"> & {
5
- enum: {
6
- [key: string]: useClientFilterQueryType;
7
- };
8
- filterFn?: (value: T) => boolean;
9
- };
10
- export type useClientFilterReturnType<T extends FieldValueAllowedTypes> = useFieldReturnType<T> & {
11
- filterFn: NonNullable<useClientFilterConfigType<T>["filterFn"]>;
12
- options: {
13
- name: string;
14
- value: useClientFilterConfigType<T>["enum"][0];
15
- }[];
16
- } & {
17
- strategy: useFieldStrategyEnum.local;
18
- };
19
- export declare function useClientFilter<T extends FieldValueAllowedTypes>(config: useClientFilterConfigType<T>): useClientFilterReturnType<T>;
20
- export {};
@@ -1,127 +0,0 @@
1
- import { type FieldValueAllowedTypes } from "../services/field";
2
- /** Type for field names */
3
- type NewFieldNameType = string;
4
- /** Valid HTML elements that can be used as field inputs */
5
- export type FieldElementType = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
6
- /**
7
- * Defines the strategy for field value persistence
8
- * @enum {string}
9
- */
10
- export declare enum useFieldStrategyEnum {
11
- /** Store field value in URL parameters */
12
- params = "params",
13
- /** Store field value in local state */
14
- local = "local"
15
- }
16
- /**
17
- * Configuration options for the useField hook
18
- * @template T - Type of the field value
19
- */
20
- export type useFieldConfigType<T extends FieldValueAllowedTypes> = {
21
- /** Unique identifier for the field */
22
- name: NewFieldNameType;
23
- /** Initial value for the field */
24
- defaultValue?: T;
25
- /** Strategy for value persistence */
26
- strategy?: useFieldStrategyEnum;
27
- };
28
- /**
29
- * Return type for the useField hook
30
- * @template T - Type of the field value
31
- */
32
- export type useFieldReturnType<T extends FieldValueAllowedTypes> = {
33
- /** Current persistence strategy */
34
- strategy: useFieldStrategyEnum;
35
- /** Initial field value */
36
- defaultValue: T;
37
- /** Current field value */
38
- currentValue: T;
39
- /** Non-nullable field value, empty string for empty values */
40
- value: NonNullable<T>;
41
- /** Function to set field value */
42
- set: (value: T) => void;
43
- /** Change event handler for controlled components */
44
- handleChange: (event: React.ChangeEvent<FieldElementType>) => void;
45
- /** Reset field to default value */
46
- clear: () => void;
47
- /** Props for field label */
48
- label: {
49
- props: {
50
- htmlFor: NewFieldNameType;
51
- };
52
- };
53
- /** Props for field input */
54
- input: {
55
- props: {
56
- id: NewFieldNameType;
57
- name: NewFieldNameType;
58
- value: NonNullable<T>;
59
- onChange: (event: React.ChangeEvent<FieldElementType>) => void;
60
- };
61
- };
62
- /** Whether field value differs from default */
63
- changed: boolean;
64
- /** Whether field value equals default */
65
- unchanged: boolean;
66
- /** Whether field is empty */
67
- empty: boolean;
68
- };
69
- /**
70
- * Hook for managing form field state with URL parameters or local state
71
- *
72
- * @template T - Type of the field value
73
- * @param {useFieldConfigType<T>} config - Field configuration
74
- * @returns {useFieldReturnType<T>} Field state and handlers
75
- *
76
- * @example
77
- * ```tsx
78
- * // Using local strategy
79
- * function NameField() {
80
- * const field = useField({
81
- * name: "username",
82
- * defaultValue: "",
83
- * strategy: useFieldStrategyEnum.local
84
- * });
85
- *
86
- * return (
87
- * <div>
88
- * <label {...field.label.props}>Username:</label>
89
- * <input
90
- * {...field.input.props}
91
- * type="text"
92
- * value={field.value}
93
- * onChange={field.handleChange}
94
- * />
95
- * </div>
96
- * );
97
- * }
98
- *
99
- * // Using URL parameters strategy
100
- * function SearchField() {
101
- * const field = useField({
102
- * name: "q",
103
- * strategy: useFieldStrategyEnum.params
104
- * });
105
- *
106
- * return (
107
- * <input
108
- * type="search"
109
- * {...field.input.props}
110
- * value={field.value}
111
- * onChange={field.handleChange}
112
- * />
113
- * );
114
- * }
115
- * ```
116
- */
117
- export declare function useField<T extends FieldValueAllowedTypes>(config: useFieldConfigType<T>): useFieldReturnType<T>;
118
- /**
119
- * Utility class for working with local fields
120
- * @static
121
- */
122
- export declare class LocalFields {
123
- static clearAll(fields: {
124
- clear: VoidFunction;
125
- }[]): () => void;
126
- }
127
- export {};
@@ -1,10 +0,0 @@
1
- export type FieldValueAllowedTypes = string | number | undefined | null;
2
- export declare class Field<T extends FieldValueAllowedTypes> {
3
- static readonly emptyValue: undefined;
4
- static isEmpty(value: FieldValueAllowedTypes): boolean;
5
- static compare(one: FieldValueAllowedTypes, another: FieldValueAllowedTypes): boolean;
6
- private readonly value;
7
- constructor(value: FieldValueAllowedTypes);
8
- get(): T;
9
- isEmpty(): boolean;
10
- }