@bgord/ui 0.6.4 → 0.7.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.
@@ -1,10 +1,12 @@
1
1
  export * from "./use-click-outside";
2
+ export * from "./use-date-field";
2
3
  export * from "./use-exit-action";
3
- export * from "./use-field";
4
4
  export * from "./use-file";
5
5
  export * from "./use-focus-shortcut";
6
6
  export * from "./use-hover";
7
7
  export * from "./use-meta-enter-submit";
8
+ export * from "./use-number-field";
8
9
  export * from "./use-scroll-lock";
9
10
  export * from "./use-shortcuts";
11
+ export * from "./use-text-field";
10
12
  export * from "./use-toggle";
@@ -0,0 +1,34 @@
1
+ import { type DateFieldValueType } from "../services/date-field";
2
+ type DateFieldNameType = string;
3
+ export type DateFieldElementType = HTMLInputElement;
4
+ export type UseDateFieldConfigType<T extends string = string> = {
5
+ name: DateFieldNameType;
6
+ defaultValue?: DateFieldValueType<T>;
7
+ };
8
+ export type UseDateFieldReturnType<T extends string = string> = {
9
+ defaultValue: DateFieldValueType<T>;
10
+ value: DateFieldValueType<T>;
11
+ set: (value: DateFieldValueType<T>) => void;
12
+ handleChange: (event: React.ChangeEvent<DateFieldElementType>) => void;
13
+ clear: () => void;
14
+ label: {
15
+ props: {
16
+ htmlFor: DateFieldNameType;
17
+ };
18
+ };
19
+ input: {
20
+ props: {
21
+ id: DateFieldNameType;
22
+ name: DateFieldNameType;
23
+ value: string;
24
+ min?: string;
25
+ max?: string;
26
+ onChange: (event: React.ChangeEvent<DateFieldElementType>) => void;
27
+ };
28
+ };
29
+ changed: boolean;
30
+ unchanged: boolean;
31
+ empty: boolean;
32
+ };
33
+ export declare function useDateField<T extends string = string>(config: UseDateFieldConfigType<T>): UseDateFieldReturnType<T>;
34
+ export {};
@@ -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 $,useRef as J}from"react";import{useEffect as C}from"react";function R(e,t){C(()=>{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:a,top:s,bottom:p}=o.getBoundingClientRect(),m=n instanceof MouseEvent?n.clientX:n.touches[0].clientX,f=n instanceof MouseEvent?n.clientY:n.touches[0].clientY;if(m>=i&&m<=a&&f>=s&&f<=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 I from"react";function le(e){let[t,r]=I.useState("idle"),n=(a)=>{if(a.preventDefault(),t==="idle")r("exiting")},o=(a)=>{if(a.animationName!==e.animation)return;e.action(),r("gone")},i=t==="exiting"?{"data-animation":e.animation,onAnimationEnd:o}:void 0;return{visible:t!=="gone",attach:i,trigger:n}}import{useState as A}from"react";class c{static emptyValue=void 0;static isEmpty(e){return e===void 0||e===""||e===null}static compare(e,t){if(c.isEmpty(e)&&c.isEmpty(t))return!0;return e===t}value=c.emptyValue;constructor(e){this.value=c.isEmpty(e)?c.emptyValue:e}get(){return this.value}isEmpty(){return c.isEmpty(this.value)}}function de(e){let t=new c(e.defaultValue),[r,n]=A(t.get()),o=c.isEmpty(r)?"":r,i=(s)=>n(new c(s).get()),a=(s)=>i(s.currentTarget.value);return{defaultValue:t.get(),value:o,set:i,handleChange:a,clear:()=>i(t.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:o,onChange:a}},changed:!c.compare(r,t.get()),unchanged:c.compare(r,t.get()),empty:c.isEmpty(r)}}import{useMemo as k,useState as h}from"react";var H;((n)=>{n.idle="idle";n.selected="selected";n.error="error"})(H||={});function xe(e,t){let r=t?.maxSizeBytes??Number.POSITIVE_INFINITY,[n,o]=h(0),[i,a]=h("idle"),[s,p]=h(null);function m(u){let y=u.currentTarget.files;if(!y?.[0])return;let x=y[0];if(x.size>r){a("error");return}if(!t.mimeTypes.includes(x.type)){a("error");return}return p(x),a("selected"),x}function f(){o((u)=>u+1),p(null),a("idle")}let U=k(()=>s?URL.createObjectURL(s):void 0,[s]);function v(u){return u.some((y)=>y===i)}let g={actions:{selectFile:m,clearFile:f},input:{props:{id:e,name:e,multiple:!1,accept:t.mimeTypes.join(",")},key:n},label:{props:{htmlFor:e}},matches:v};if(i==="idle")return{data:null,isError:!1,isIdle:!0,isSelected:!1,state:i,...g};if(i==="selected")return{data:s,isError:!1,isIdle:!1,isSelected:!0,preview:U,state:i,...g};return{data:null,isError:!0,isIdle:!1,isSelected:!1,state:i,...g}}import{useCallback as P,useMemo as W,useRef as q}from"react";import{useEffect as M}from"react";import{tinykeys as O}from"tinykeys";function T(e,t){let r=t?.enabled??!0;M(()=>{if(!r)return;let n=O(window,e);return()=>n()},[e,r])}function Re(e){let t=q(null),r=P(()=>{if(t.current)t.current.focus()},[]);return T({[e]:r}),W(()=>({ref:t}),[])}import{useCallback as D,useRef as N}from"react";import{useState as X}from"react";function S({name:e,defaultValue:t=!1}){let[r,n]=X(t);return{on:r,off:!r,enable:()=>n(!0),disable:()=>n(!1),toggle:()=>n((m)=>!m),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 F(e){let{on:t,off:r,enable:n,disable:o,toggle:i,props:a,...s}=e;return{toggle:{on:t,off:r,enable:n,disable:o,toggle:i,props:a},rest:s}}function Ce(e){let t=e?.enabled??!0,r=S({name:"_internal"}),n=N(null),o=typeof window<"u"&&"PointerEvent"in window?"pointerenter":"mouseenter",i=typeof window<"u"&&"PointerEvent"in window?"pointerleave":"mouseleave";return{attach:{ref:D((s)=>{let p=n.current;if(p)p.removeEventListener(o,r.enable),p.removeEventListener(i,r.disable);if(n.current=s,s&&t)s.addEventListener(o,r.enable),s.addEventListener(i,r.disable)},[o,i,t,r.enable,r.disable])},hovering:r.on&&t}}import{useCallback as B}from"react";function ke(){return{onKeyDown:B((t)=>{if(!t.metaKey||t.key!=="Enter")return;t.preventDefault(),t.currentTarget.form?.requestSubmit()},[])}}import{useEffect as K}from"react";function w(e=!0){K(()=>{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])}function d(){}import{jsx as Y}from"react/jsx-runtime";function Ve(e){let t=e.locked??!1,{toggle:r,rest:n}=F(e),o=J(null);return $(()=>{if(e.on)o.current?.showModal();else o.current?.close()},[e.on]),T({Escape:t?d:r.disable}),w(e.on),R(o,t?d:r.disable),Y("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",...n})}var et={email:{inputMode:"email",autoComplete:"email",autoCapitalize:"none",spellCheck:"false"},password:{new:{autoComplete:"new-password"},current:{autoComplete:"current-password"}},off:{autoComplete:"off",spellCheck:!1}};class j{static async copy(e){let t=e.onSuccess??d;if(!navigator.clipboard)return;await navigator.clipboard.writeText(e.text),t()}}import V from"js-cookie";class z{static extractFrom(e){return e.headers.get("cookie")??""}static set(e,t){V.set(e,t)}}class G{static fromRevision(e){return{"if-match":String(e)}}}function at(e){return function(){for(let t of e)t()}}class Q{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 Z{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 mt(){if(typeof window>"u")return;return window}import{polishPlurals as ee}from"polish-plurals";function L(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 ee(e.singular,String(e.plural),String(e.genitive),t)}return console.warn(`[@bgord/frontend] missing pluralization function for language: ${e.language}.`),e.singular}function yt(e=12){return{times(t){let r=e*t,n={height:{height:l(r)},minHeight:{minHeight:l(r)},maxHeight:{maxHeight:l(r)},width:{width:l(r)},minWidth:{minWidth:l(r)},maxWidth:{maxWidth:l(r)},square:{height:l(r),width:l(r)}},o={height:{style:{height:l(r)}},minHeight:{style:{minHeight:l(r)}},maxHeight:{style:{maxHeight:l(r)}},width:{style:{width:l(r)}},minWidth:{style:{minWidth:l(r)}},maxWidth:{style:{maxWidth:l(r)}},square:{style:{height:l(r),width:l(r)}}};return{px:l(r),raw:r,style:o,...n}}}}function l(e){return`${e}px`}import{createContext as te,use as E,useCallback as re}from"react";var b=te({translations:{},language:"en",supportedLanguages:{en:"en"}});function ht(){let e=E(b);if(e===void 0)throw Error("useTranslations must be used within the TranslationsContext");return re((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,[a,s])=>{let p=new RegExp(`{{${a}}}`,"g");return i.replace(p,String(s))},o)},[e.translations])}function ne(){let e=E(b);if(e===void 0)throw Error("useLanguage must be used within the TranslationsContext");return e.language}function Et(){let e=E(b);if(e===void 0)throw Error("useSupportedLanguages must be used within the TranslationsContext");return e.supportedLanguages}function bt(){let e=ne();return(t)=>L({...t,language:e})}class oe{static fromRevision(e){return{"if-match":`W/${e}`}}}export{ht as useTranslations,S as useToggle,Et as useSupportedLanguages,T as useShortcuts,w as useScrollLock,bt as usePluralize,ke as useMetaEnterSubmit,ne as useLanguage,Ce as useHover,Re as useFocusKeyboardShortcut,xe as useFile,de as useField,le as useExitAction,R as useClickOutside,L as pluralize,d as noop,mt as getSafeWindow,F as extractUseToggle,at as exec,oe as WeakETag,H as UseFileState,b as TranslationsContext,yt as Rhythm,Z as Form,Q as Fields,c as Field,G as ETag,Ve as Dialog,z as Cookies,j as Clipboard,et as Autocomplete};
1
+ import{useEffect as z,useRef as G}from"react";import{useEffect as A}from"react";function S(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:a,top:s,bottom:l}=o.getBoundingClientRect(),p=r instanceof MouseEvent?r.clientX:r.touches[0].clientX,y=r instanceof MouseEvent?r.clientY:r.touches[0].clientY;if(p>=i&&p<=a&&y>=s&&y<=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{useState as w}from"react";class d{static EMPTY=void 0;value=d.EMPTY;constructor(e){this.value=d.isEmpty(e)?d.EMPTY:e}get(){return this.value}isEmpty(){return d.isEmpty(this.value)}static isEmpty(e){return e===void 0||e===null||e===""}static compare(e,t){if(d.isEmpty(e)&&d.isEmpty(t))return!0;return e===t}}function ye(e){let t=new d(e.defaultValue),[n,r]=w(t.get()),[o,i]=w(d.isEmpty(t.get())?"":t.get()),a=(l)=>{let p=new d(l).get();r(p),i(d.isEmpty(p)?"":String(p))},s=(l)=>{let p=l.currentTarget,y=p.value;if(i(y),y==="")return r(d.EMPTY);if(!/^\d{4}-\d{2}-\d{2}$/.test(y))return;if(!p.validity.valid)return;r(y)};return{defaultValue:t.get(),value:n,set:a,handleChange:s,clear:()=>a(t.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:o,onChange:s}},changed:!d.compare(n,t.get()),unchanged:d.compare(n,t.get()),empty:d.isEmpty(n)}}import M from"react";function ge(e){let[t,n]=M.useState("idle"),r=(a)=>{if(a.preventDefault(),t==="idle")n("exiting")},o=(a)=>{if(a.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 H,useState as F}from"react";var k;((r)=>{r.idle="idle";r.selected="selected";r.error="error"})(k||={});function Ee(e,t){let n=t?.maxSizeBytes??Number.POSITIVE_INFINITY,[r,o]=F(0),[i,a]=F("idle"),[s,l]=F(null);function p(f){let x=f.currentTarget.files;if(!x?.[0])return;let h=x[0];if(h.size>n){a("error");return}if(!t.mimeTypes.includes(h.type)){a("error");return}return l(h),a("selected"),h}function y(){o((f)=>f+1),l(null),a("idle")}let T=H(()=>s?URL.createObjectURL(s):void 0,[s]);function L(f){return f.some((x)=>x===i)}let U={actions:{selectFile:p,clearFile:y},input:{props:{id:e,name:e,multiple:!1,accept:t.mimeTypes.join(",")},key:r},label:{props:{htmlFor:e}},matches:L};if(i==="idle")return{data:null,isError:!1,isIdle:!0,isSelected:!1,state:i,...U};if(i==="selected")return{data:s,isError:!1,isIdle:!1,isSelected:!0,preview:T,state:i,...U};return{data:null,isError:!0,isIdle:!1,isSelected:!1,state:i,...U}}import{useCallback as W,useMemo as q,useRef as X}from"react";import{useEffect as O}from"react";import{tinykeys as P}from"tinykeys";function E(e,t){let n=t?.enabled??!0;O(()=>{if(!n)return;let r=P(window,e);return()=>r()},[e,n])}function De(e){let t=X(null),n=W(()=>{if(t.current)t.current.focus()},[]);return E({[e]:n}),q(()=>({ref:t}),[])}import{useCallback as K,useRef as Y}from"react";import{useState as B}from"react";function D({name:e,defaultValue:t=!1}){let[n,r]=B(t);return{on:n,off:!n,enable:()=>r(!0),disable:()=>r(!1),toggle:()=>r((p)=>!p),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 C(e){let{on:t,off:n,enable:r,disable:o,toggle:i,props:a,...s}=e;return{toggle:{on:t,off:n,enable:r,disable:o,toggle:i,props:a},rest:s}}function Ae(e){let t=e?.enabled??!0,n=D({name:"_internal"}),r=Y(null),o=typeof window<"u"&&"PointerEvent"in window?"pointerenter":"mouseenter",i=typeof window<"u"&&"PointerEvent"in window?"pointerleave":"mouseleave";return{attach:{ref:K((s)=>{let l=r.current;if(l)l.removeEventListener(o,n.enable),l.removeEventListener(i,n.disable);if(r.current=s,s&&t)s.addEventListener(o,n.enable),s.addEventListener(i,n.disable)},[o,i,t,n.enable,n.disable])},hovering:n.on&&t}}import{useCallback as $}from"react";function ke(){return{onKeyDown:$((t)=>{if(!t.metaKey||t.key!=="Enter")return;t.preventDefault(),t.currentTarget.form?.requestSubmit()},[])}}import{useState as v}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===null||Number.isNaN(e)}static compare(e,t){if(m.isEmpty(e)&&m.isEmpty(t))return!0;return e===t}}function Xe(e){let t=new m(e.defaultValue),[n,r]=v(t.get()),[o,i]=v(m.isEmpty(t.get())?"":String(t.get())),a=(l)=>{let p=new m(l).get();r(p),i(m.isEmpty(p)?"":String(p))},s=(l)=>{let p=l.currentTarget,y=p.value;if(i(y),y==="")return r(m.EMPTY);let T=p.valueAsNumber;if(!Number.isFinite(T))return;if(!l.currentTarget.validity.valid)return;r(T)};return{defaultValue:t.get(),value:n,set:a,handleChange:s,clear:()=>a(t.get()),label:{props:{htmlFor:e.name}},input:{props:{id:e.name,name:e.name,value:o,onChange:s}},changed:!m.compare(n,t.get()),unchanged:m.compare(n,t.get()),empty:m.isEmpty(n)}}import{useEffect as _}from"react";function V(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 J}from"react";class u{static EMPTY=void 0;value=u.EMPTY;constructor(e){this.value=u.isEmpty(e)?u.EMPTY:e}get(){return this.value}isEmpty(){return u.isEmpty(this.value)}static isEmpty(e){return e===void 0||e===""||e===null}static compare(e,t){if(u.isEmpty(e)&&u.isEmpty(t))return!0;return e===t}}function je(e){let t=new u(e.defaultValue),[n,r]=J(t.get()),o=(a)=>r(new u(a).get()),i=(a)=>o(a.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:u.isEmpty(n)?"":n,onChange:i}},changed:!u.compare(n,t.get()),unchanged:u.compare(n,t.get()),empty:u.isEmpty(n)}}function g(){}import{jsx as N}from"react/jsx-runtime";function dt(e){let t=e.locked??!1,{toggle:n,rest:r}=C(e),o=G(null);return z(()=>{if(e.on)o.current?.showModal();else o.current?.close()},[e.on]),E({Escape:t?g:n.disable}),V(e.on),S(o,t?g:n.disable),N("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 ft={email:{inputMode:"email",autoComplete:"email",autoCapitalize:"none",spellCheck:"false"},password:{new:{autoComplete:"new-password"},current:{autoComplete:"current-password"}},off:{autoComplete:"off",spellCheck:!1}};class Q{static async copy(e){let t=e.onSuccess??g;if(!navigator.clipboard)return;await navigator.clipboard.writeText(e.text),t()}}import Z from"js-cookie";class ee{static extractFrom(e){return e.headers.get("cookie")??""}static set(e,t){Z.set(e,t)}}class te{static fromRevision(e){return{"if-match":String(e)}}}function bt(e){return function(){for(let t of e)t()}}class ne{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 re{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],tomorrow:()=>{let e=new Date;return new Date(e.setDate(e.getDate()+1)).toISOString().split("T")[0]},yesterday:()=>{let e=new Date;return new Date(e.setDate(e.getDate()-1)).toISOString().split("T")[0]}},max:{today:()=>new Date().toISOString().split("T")[0],tomorrow:()=>{let e=new Date;return new Date(e.setDate(e.getDate()+1)).toISOString().split("T")[0]},yesterday:()=>{let e=new Date;return new Date(e.setDate(e.getDate()-1)).toISOString().split("T")[0]}}}}function Dt(){if(typeof window>"u")return;return window}import{polishPlurals as oe}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 oe(e.singular,String(e.plural),String(e.genitive),t)}return console.warn(`[@bgord/frontend] missing pluralization function for language: ${e.language}.`),e.singular}function It(e=12){return{times(t){let n=e*t,r={height:{height:c(n)},minHeight:{minHeight:c(n)},maxHeight:{maxHeight:c(n)},width:{width:c(n)},minWidth:{minWidth:c(n)},maxWidth:{maxWidth:c(n)},square:{height:c(n),width:c(n)}},o={height:{style:{height:c(n)}},minHeight:{style:{minHeight:c(n)}},maxHeight:{style:{maxHeight:c(n)}},width:{style:{width:c(n)}},minWidth:{style:{minWidth:c(n)}},maxWidth:{style:{maxWidth:c(n)}},square:{style:{height:c(n),width:c(n)}}};return{px:c(n),raw:n,style:o,...r}}}}function c(e){return`${e}px`}import{createContext as ie,use as b,useCallback as ae}from"react";var R=ie({translations:{},language:"en",supportedLanguages:{en:"en"}});function Ht(){let e=b(R);if(e===void 0)throw Error("useTranslations must be used within the TranslationsContext");return ae((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,[a,s])=>{let l=new RegExp(`{{${a}}}`,"g");return i.replace(l,String(s))},o)},[e.translations])}function se(){let e=b(R);if(e===void 0)throw Error("useLanguage must be used within the TranslationsContext");return e.language}function kt(){let e=b(R);if(e===void 0)throw Error("useSupportedLanguages must be used within the TranslationsContext");return e.supportedLanguages}function Ot(){let e=se();return(t)=>I({...t,language:e})}class le{static fromRevision(e){return{"if-match":`W/${e}`}}}export{Ht as useTranslations,D as useToggle,je as useTextField,kt as useSupportedLanguages,E as useShortcuts,V as useScrollLock,Ot as usePluralize,Xe as useNumberField,ke as useMetaEnterSubmit,se as useLanguage,Ae as useHover,De as useFocusKeyboardShortcut,Ee as useFile,ge as useExitAction,ye as useDateField,S as useClickOutside,I as pluralize,g as noop,Dt as getSafeWindow,C as extractUseToggle,bt as exec,le as WeakETag,k as UseFileState,R as TranslationsContext,u as TextField,It as Rhythm,m as NumberField,re as Form,ne as Fields,te as ETag,dt as Dialog,d as DateField,ee as Cookies,Q as Clipboard,ft as Autocomplete};
@@ -0,0 +1,10 @@
1
+ export type DateFieldValueType<T extends string = string> = T | undefined;
2
+ export declare class DateField<T extends string = string> {
3
+ static readonly EMPTY: undefined;
4
+ private readonly value;
5
+ constructor(value: DateFieldValueType<T>);
6
+ get(): DateFieldValueType<T>;
7
+ isEmpty(): boolean;
8
+ static isEmpty<V extends string>(value: DateFieldValueType<V> | null): boolean;
9
+ static compare<V extends string>(one: DateFieldValueType<V>, another: DateFieldValueType<V>): boolean;
10
+ }
@@ -15,9 +15,13 @@ export declare class Form {
15
15
  static date: {
16
16
  min: {
17
17
  today: () => string;
18
+ tomorrow: () => string;
19
+ yesterday: () => string;
18
20
  };
19
21
  max: {
20
22
  today: () => string;
23
+ tomorrow: () => string;
24
+ yesterday: () => string;
21
25
  };
22
26
  };
23
27
  }
@@ -1,14 +1,16 @@
1
1
  export * from "./autocomplete";
2
2
  export * from "./clipboard";
3
3
  export * from "./cookies";
4
+ export * from "./date-field";
4
5
  export * from "./etag";
5
6
  export * from "./exec";
6
- export * from "./field";
7
7
  export * from "./fields";
8
8
  export * from "./form";
9
9
  export * from "./get-safe-window";
10
10
  export * from "./noop";
11
+ export * from "./number-field";
11
12
  export * from "./pluralize";
12
13
  export * from "./rhythm";
14
+ export * from "./text-field";
13
15
  export * from "./translations";
14
16
  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.4",
3
+ "version": "0.7.2",
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,28 +28,32 @@ src/
28
28
  │   ├── dialog.tsx
29
29
  ├── hooks
30
30
  │   ├── use-click-outside.ts
31
+ │   ├── use-date-field.ts
31
32
  │   ├── use-exit-action.ts
32
- │   ├── use-field.ts
33
33
  │   ├── use-file.ts
34
34
  │   ├── use-focus-shortcut.ts
35
35
  │   ├── use-hover.ts
36
36
  │   ├── use-meta-enter-submit.ts
37
+ │   ├── use-number-field.ts
37
38
  │   ├── use-scroll-lock.ts
38
39
  │   ├── use-shortcuts.ts
40
+ │   ├── use-text-field.ts
39
41
  │   └── use-toggle.ts
40
42
  └── services
41
43
  ├── autocomplete.ts
42
44
  ├── clipboard.ts
43
45
  ├── cookies.ts
46
+ ├── date-field.ts
44
47
  ├── etag.ts
45
48
  ├── exec.ts
46
- ├── field.ts
47
49
  ├── fields.ts
48
50
  ├── form.ts
49
51
  ├── get-safe-window.ts
50
52
  ├── noop.ts
53
+ ├── number-field.ts
51
54
  ├── pluralize.ts
52
55
  ├── rhythm.ts
56
+ ├── text-field.ts
53
57
  ├── translations.tsx
54
58
  └── weak-etag.ts
55
59
  ```
@@ -1,32 +0,0 @@
1
- import { type FieldValueAllowedTypes } from "../services/field";
2
- type FieldNameType = string;
3
- export type FieldElementType = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
4
- export type useFieldConfigType<T extends FieldValueAllowedTypes> = {
5
- name: FieldNameType;
6
- defaultValue?: T;
7
- };
8
- export type useFieldReturnType<T extends FieldValueAllowedTypes> = {
9
- defaultValue: T;
10
- value: NonNullable<T>;
11
- set: (value: T) => void;
12
- handleChange: (event: React.ChangeEvent<FieldElementType>) => void;
13
- clear: () => void;
14
- label: {
15
- props: {
16
- htmlFor: FieldNameType;
17
- };
18
- };
19
- input: {
20
- props: {
21
- id: FieldNameType;
22
- name: FieldNameType;
23
- value: NonNullable<T>;
24
- onChange: (event: React.ChangeEvent<FieldElementType>) => void;
25
- };
26
- };
27
- changed: boolean;
28
- unchanged: boolean;
29
- empty: boolean;
30
- };
31
- export declare function useField<T extends FieldValueAllowedTypes>(config: useFieldConfigType<T>): useFieldReturnType<T>;
32
- 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
- }