@elcrm/form 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +162 -204
- package/dist/Form.d.ts +21 -3
- package/dist/assets/styles/Check.module.css +1 -1
- package/dist/assets/styles/Code.module.css +1 -1
- package/dist/assets/styles/Color.module.css +1 -1
- package/dist/assets/styles/Drag.module.css +1 -1
- package/dist/assets/styles/Field.module.css +1 -1
- package/dist/assets/styles/FieldGroup.module.css +1 -0
- package/dist/assets/styles/File.module.css +1 -1
- package/dist/assets/styles/Form.module.css +1 -1
- package/dist/assets/styles/Radio.module.css +1 -1
- package/dist/assets/styles/Rating.module.css +1 -1
- package/dist/assets/styles/Rich.module.css +1 -1
- package/dist/assets/styles/Select.module.css +1 -1
- package/dist/assets/styles/Tabs.module.css +1 -0
- package/dist/assets/styles/Tags.module.css +1 -1
- package/dist/assets/styles/Time.module.css +1 -1
- package/dist/core/Field.d.ts +38 -10
- package/dist/core/Field.js +2 -2
- package/dist/dark.css +34 -11
- package/dist/fields/Card.d.ts +26 -0
- package/dist/fields/Card.js +1 -0
- package/dist/fields/Check.d.ts +20 -2
- package/dist/fields/Check.js +1 -1
- package/dist/fields/Code.d.ts +26 -4
- package/dist/fields/Code.js +1 -1
- package/dist/fields/Color.d.ts +21 -13
- package/dist/fields/Color.js +1 -1
- package/dist/fields/ColorPanel.d.ts +20 -0
- package/dist/fields/ColorPanel.js +1 -0
- package/dist/fields/Date.d.ts +16 -2
- package/dist/fields/Date.js +1 -1
- package/dist/fields/DateCalendar.d.ts +26 -0
- package/dist/fields/DateCalendar.js +1 -0
- package/dist/fields/Display.d.ts +15 -5
- package/dist/fields/Display.js +2 -1
- package/dist/fields/DragDrop.d.ts +27 -4
- package/dist/fields/DragDrop.js +1 -1
- package/dist/fields/Email.d.ts +18 -3
- package/dist/fields/Email.js +1 -1
- package/dist/fields/FieldGroup.d.ts +127 -0
- package/dist/fields/FieldGroup.js +1 -0
- package/dist/fields/File.d.ts +14 -1
- package/dist/fields/File.js +1 -1
- package/dist/fields/Hidden.d.ts +7 -2
- package/dist/fields/Hidden.js +1 -1
- package/dist/fields/Input.d.ts +10 -5
- package/dist/fields/Input.js +1 -1
- package/dist/fields/Mask.d.ts +14 -1
- package/dist/fields/Mask.js +1 -1
- package/dist/fields/Modal.d.ts +20 -3
- package/dist/fields/Modal.js +1 -1
- package/dist/fields/Money.d.ts +12 -1
- package/dist/fields/Money.js +1 -1
- package/dist/fields/NativeTextField.d.ts +9 -5
- package/dist/fields/NativeTextField.js +1 -1
- package/dist/fields/Numeric.d.ts +10 -8
- package/dist/fields/Numeric.js +1 -1
- package/dist/fields/Options.d.ts +24 -5
- package/dist/fields/Options.js +1 -1
- package/dist/fields/Password.d.ts +11 -0
- package/dist/fields/Password.js +1 -1
- package/dist/fields/Percent.d.ts +10 -6
- package/dist/fields/Percent.js +1 -1
- package/dist/fields/Phone.d.ts +13 -1
- package/dist/fields/Phone.js +1 -1
- package/dist/fields/Radio.d.ts +17 -1
- package/dist/fields/Radio.js +1 -1
- package/dist/fields/Range.d.ts +19 -6
- package/dist/fields/Range.js +1 -1
- package/dist/fields/Rating.d.ts +13 -1
- package/dist/fields/Rating.js +1 -1
- package/dist/fields/RichText.d.ts +10 -2
- package/dist/fields/RichText.js +1 -1
- package/dist/fields/Select.d.ts +21 -0
- package/dist/fields/Select.js +1 -1
- package/dist/fields/Tabs.d.ts +45 -0
- package/dist/fields/Tabs.js +1 -0
- package/dist/fields/Tags.d.ts +13 -1
- package/dist/fields/Tags.js +1 -1
- package/dist/fields/Textarea.d.ts +10 -1
- package/dist/fields/Textarea.js +1 -1
- package/dist/fields/Time.d.ts +13 -4
- package/dist/fields/Time.js +1 -1
- package/dist/fields/TimePicker.d.ts +24 -0
- package/dist/fields/TimePicker.js +1 -0
- package/dist/fields/Url.d.ts +11 -1
- package/dist/fields/cardUtils.d.ts +37 -0
- package/dist/fields/cardUtils.js +1 -0
- package/dist/fields/colorUtils.d.ts +29 -0
- package/dist/fields/colorUtils.js +1 -0
- package/dist/fields/dateUtils.d.ts +32 -0
- package/dist/fields/dateUtils.js +1 -0
- package/dist/fields/emailUtils.d.ts +4 -0
- package/dist/fields/emailUtils.js +1 -0
- package/dist/fields/timeUtils.d.ts +23 -0
- package/dist/fields/timeUtils.js +1 -0
- package/dist/fields/type.d.ts +97 -26
- package/dist/hooks/use.d.ts +32 -13
- package/dist/index.d.ts +21 -9
- package/dist/index.js +2 -2
- package/dist/index.umd.js +3 -2
- package/dist/light.css +35 -12
- package/dist/package.js +1 -1
- package/dist/style.css +17 -13
- package/dist/styles/Code.module.js +1 -1
- package/dist/styles/Color.module.js +1 -1
- package/dist/styles/Field.module.js +1 -1
- package/dist/styles/FieldGroup.module.js +1 -0
- package/dist/styles/Tabs.module.js +1 -0
- package/package.json +1 -1
package/dist/fields/Color.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{
|
|
1
|
+
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{createFieldEmit as r}from"./emit.js";import i from"../styles/Color.module.js";import{DEFAULT_COLOR_PALETTE as a,formatHexColor as o,isEditablePasteTarget as s,isEmptyColor as c,normalizeHexCompare as l,parseClipboardColor as ee,parseHexColor as u}from"./colorUtils.js";import te from"./ColorPanel.js";import d from"react";import{Fragment as ne,jsx as f,jsxs as p}from"react/jsx-runtime";import{createPortal as re}from"react-dom";var m={h:0,s:1,v:.76,a:1};function h({value:h=``,form:g,onValue:_,onBlur:ie,name:v=``,placeholder:y=`#000000`,label:b=``,error:x=``,hidden:S=!1,disabled:C=!1,className:ae=``,after:w=``,before:T=``,colors:E,alpha:D=!0,inlinePalette:O=!1}){let{value:k}=n(g,v,h),A=typeof k==`string`?k:k==null?``:String(k),[j,M]=d.useState(!1),[oe,N]=d.useState(null),P=d.useRef(null),[F,I]=d.useState(()=>u(A)??{...m}),[L,R]=d.useState(()=>o(u(A)??m,D)),z=d.useCallback(()=>{let e=P.current;if(!e)return;let t=e.getBoundingClientRect();N(e=>e&&e.top===t.top&&e.left===t.left&&e.width===t.width&&e.height===t.height&&e.bottom===t.bottom?e:t)},[]);d.useEffect(()=>{if(!j)return;z();let e=()=>z();return window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,e),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,e)}},[j,z]);let B=E?.length?E:a,V=u(A),H=V??(c(A)?u(y):null),U=V?o(V,D):``,W=H?o(H,D):`transparent`;d.useEffect(()=>{let e=u(A);e&&(I(e),R(o(e,D)))},[A,D]);let G=d.useCallback((e,t)=>{let n=o(e,D);r(g,v,_)(n,t===`commit`),R(n)},[D,g,v,_]),[K,q]=d.useState(null),[se,J]=d.useState(!1),Y=d.useRef(null),X=d.useRef(null);d.useEffect(()=>()=>{Y.current&&clearTimeout(Y.current),X.current&&clearTimeout(X.current)},[]);let ce=e=>{if(C)return;let t=u(e);if(!t)return;let n={...t,a:D?t.a:1};I(n),G(n,`commit`)},le=e=>{if(C||K)return;let t=l(e);Z&&t===Z||(q(e),Y.current&&clearTimeout(Y.current),Y.current=setTimeout(()=>{ce(e),q(null),J(!0),X.current&&clearTimeout(X.current),X.current=setTimeout(()=>{J(!1)},280)},220))},Z=V?l(o(V,D)):``,ue=d.useMemo(()=>{if(!O)return[];let e=K?l(K):``;return B.filter(t=>{let n=l(t);return e&&n===e?!0:!(Z&&n===Z)})},[O,B,Z,K]),Q=e=>{if(C)return;let t=u(A)??{...F};I(t),R(o(t,D)),N(e.getBoundingClientRect()),M(!0)},$=()=>{M(!1),ie?.({value:o(F,D),name:v||``})};d.useEffect(()=>{if(!j||C)return;let e=``,t=t=>{let n=ee(t);if(!n)return!1;let r={...n,a:D?n.a:1},i=o(r,D);return i===e?!0:(e=i,I(r),G(r,`commit`),!0)},n=e=>{s(e.target)||t(e.clipboardData?.getData(`text/plain`)??``)&&(e.preventDefault(),e.stopPropagation())},r=e=>{(e.ctrlKey||e.metaKey)&&(e.key!==`v`&&e.key!==`V`||s(e.target)||navigator.clipboard?.readText&&(e.preventDefault(),navigator.clipboard.readText().then(e=>{t(e)})))};return document.addEventListener(`paste`,n,!0),window.addEventListener(`keydown`,r,!0),()=>{document.removeEventListener(`paste`,n,!0),window.removeEventListener(`keydown`,r,!0)}},[j,C,D,G]);let de=e=>{if(!C){if(!j&&(e.key===`Enter`||e.key===` `||e.key===`ArrowDown`)){e.preventDefault(),Q(e.currentTarget);return}j&&e.key===`Escape`&&(e.preventDefault(),$())}};return S&&c(A)?null:p(ne,{children:[p(t,{type:`color`,label:b,error:x,element_blok:{className:[ae||``]},element_input:{className:[e.w,i.q],props:{"data-disabled":C?`true`:void 0}},children:[T?f(`span`,{className:e.a,children:T}):null,p(`div`,{ref:P,className:i.t,role:`combobox`,tabIndex:C?-1:0,"aria-expanded":j,"aria-haspopup":`dialog`,"aria-disabled":C,"aria-label":b||`Цвет`,onClick:e=>Q(e.currentTarget),onKeyDown:de,children:[f(`span`,{className:i.c,"data-enter":se?`true`:void 0,style:{"--preview-color":W}}),f(`span`,{className:[i.g,U?``:i.m].filter(Boolean).join(` `),children:U||y})]}),O?f(`div`,{className:i.j,role:`listbox`,"aria-label":`Палитра`,onClick:e=>e.stopPropagation(),onMouseDown:e=>e.stopPropagation(),children:ue.map(e=>{let t=K!=null&&l(e)===l(K);return f(`button`,{type:`button`,role:`option`,"aria-label":e,disabled:C||!!K,className:i.k,"data-exit":t?`true`:void 0,style:{"--swatch":e},onClick:t=>{t.stopPropagation(),le(e)}},e)})}):null,w?f(`span`,{className:e.a,children:w}):null]}),j&&re(f(te,{parent:oe||void 0,hsva:F,setHsva:I,hexDraft:L,setHexDraft:R,alpha:D,palette:B,showPalette:!O,onLive:e=>G(e,`live`),onCommit:e=>G(e,`commit`),onClose:$}),document.body)]})}h.displayName=`ColorField`;export{h as default};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { Hsva } from './colorUtils';
|
|
3
|
+
/**
|
|
4
|
+
* Попап пикера {@link ColorField}: SV, hue, alpha, hex, опционально палитра.
|
|
5
|
+
* Не публичный API. Позиция — `parent` (rect триггера) + clamp во вьюпорт.
|
|
6
|
+
*/
|
|
7
|
+
export default function ColorPanel({ parent, hsva, setHsva, hexDraft, setHexDraft, alpha, palette, showPalette, onLive, onCommit, onClose, }: {
|
|
8
|
+
parent?: DOMRect;
|
|
9
|
+
hsva: Hsva;
|
|
10
|
+
setHsva: React.Dispatch<React.SetStateAction<Hsva>>;
|
|
11
|
+
hexDraft: string;
|
|
12
|
+
setHexDraft: React.Dispatch<React.SetStateAction<string>>;
|
|
13
|
+
alpha: boolean;
|
|
14
|
+
palette: string[];
|
|
15
|
+
/** false — пресеты уже в поле, в попапе не дублируем */
|
|
16
|
+
showPalette?: boolean;
|
|
17
|
+
onLive: (next: Hsva) => void;
|
|
18
|
+
onCommit: (next: Hsva) => void;
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{acquireOverlayZIndex as e,releaseOverlayZIndex as t}from"../overlayZ.js";import n from"../styles/Color.module.js";import{clamp as r,cssColor as i,formatHexColor as a,hasEyeDropper as o,hueCss as s,normalizeHexCompare as c,parseHexColor as l}from"./colorUtils.js";import u from"react";import{Fragment as d,jsx as f,jsxs as p}from"react/jsx-runtime";function m({parent:s,hsva:m,setHsva:v,hexDraft:b,setHexDraft:x,alpha:S,palette:C,showPalette:w=!0,onLive:T,onCommit:E,onClose:D}){let[O]=u.useState(()=>e());if(u.useEffect(()=>()=>t(O),[O]),!s)return null;let k=(S?340:300)-(w?0:90),A=window.innerHeight-s.bottom<k+8?s.top-k-8:s.bottom+8;A=Math.min(A,window.innerHeight-k-8),A=Math.max(8,A);let j=Math.min(Math.max(8,s.left),window.innerWidth-260-8),M=i({...m,a:1}),N=c(a(m,S)),P=(e,t)=>{v(e),t===`live`?T(e):E(e)},F=()=>{let e=l(b);if(!e){x(a(m,S));return}P(e,`commit`)},I=e=>{let t=e.replace(/[^\d]/g,``),n=r(Number(t||0),0,100);P({...m,a:n/100},`commit`)},L=async()=>{if(o())try{let e=window.EyeDropper,t=l((await new e().open()).sRGBHex);t&&P({...t,a:m.a},`commit`)}catch{}};return p(d,{children:[f(`div`,{className:n.o,style:{zIndex:O},onClick:D,"aria-hidden":!0}),p(`div`,{className:n.p,role:`dialog`,"aria-label":`Выбор цвета`,style:{top:A,left:j,width:260,zIndex:O+1},children:[f(h,{hsva:m,onChange:(e,t,n)=>P({...m,s:e,v:t},n)}),p(`div`,{className:n.r,children:[o()?f(`button`,{type:`button`,className:n.e,"aria-label":`Пипетка`,onClick:L,children:f(y,{})}):null,p(`div`,{className:n.s,children:[f(g,{h:m.h,onChange:(e,t)=>P({...m,h:e},t)}),S?f(_,{a:m.a,color:M,onChange:(e,t)=>P({...m,a:e},t)}):null]})]}),p(`div`,{className:n.i,children:[f(`span`,{className:n.f,children:`Hex`}),f(`input`,{className:n.n,value:b.replace(/^#/,``),spellCheck:!1,"aria-label":`Hex`,onChange:e=>{let t=e.target.value.replace(/[^0-9a-fA-F]/g,``);x(t?`#${t}`:``)},onBlur:F,onKeyDown:e=>{e.key===`Enter`&&(e.preventDefault(),e.target.blur())}}),S?f(`input`,{className:`${n.n} ${n.x}`,value:`${Math.round(m.a*100)} %`,"aria-label":`Прозрачность`,onChange:e=>I(e.target.value),onFocus:e=>e.target.select()}):null]}),w?f(`div`,{className:n.y,role:`listbox`,"aria-label":`Палитра`,children:C.map(e=>{let t=c(e)===N;return f(`button`,{type:`button`,role:`option`,"aria-checked":t,"aria-label":e,className:n.z,style:{"--swatch":e},onClick:()=>{let t=l(e);t&&P({...t,a:S?t.a:1},`commit`)}},e)})}):null]})]})}function h({hsva:e,onChange:t}){let a=u.useRef(null),o=u.useRef(!1),c=u.useRef(t);c.current=t;let l=(t,n)=>{let i=a.current;if(!i)return{s:e.s,v:e.v};let o=i.getBoundingClientRect();return{s:r((t-o.left)/o.width,0,1),v:r(1-(n-o.top)/o.height,0,1)}};return u.useEffect(()=>{let e=e=>{if(!o.current)return;let{s:t,v:n}=l(e.clientX,e.clientY);c.current(t,n,`live`)},t=e=>{if(!o.current)return;o.current=!1;let{s:t,v:n}=l(e.clientX,e.clientY);c.current(t,n,`commit`)};return window.addEventListener(`pointermove`,e),window.addEventListener(`pointerup`,t),()=>{window.removeEventListener(`pointermove`,e),window.removeEventListener(`pointerup`,t)}},[]),p(`div`,{ref:a,className:n.v,style:{background:s(e.h)},onPointerDown:e=>{o.current=!0;let{s:t,v:n}=l(e.clientX,e.clientY);c.current(t,n,`live`)},children:[f(`div`,{className:n.w}),f(`div`,{className:n.b}),f(`div`,{className:n.h,style:{left:`${e.s*100}%`,top:`${(1-e.v)*100}%`,background:i({...e,a:1})}})]})}function g({h:e,onChange:t}){return f(v,{className:`${n.l} ${n.u}`,ratio:e/360,onRatio:(e,n)=>t(e*360,n),handleColor:s(e)})}function _({a:e,color:t,onChange:r}){return f(v,{className:`${n.l} ${n.a}`,style:{"--alpha-color":t},ratio:e,onRatio:r,handleColor:t})}function v({className:e,style:t,ratio:i,onRatio:a,handleColor:o}){let s=u.useRef(null),c=u.useRef(!1),l=(e,t)=>{let n=s.current;if(!n)return;let i=n.getBoundingClientRect();a(r((e-i.left)/i.width,0,1),t)};return u.useEffect(()=>{let e=e=>{c.current&&l(e.clientX,`live`)},t=e=>{c.current&&(c.current=!1,l(e.clientX,`commit`))};return window.addEventListener(`pointermove`,e),window.addEventListener(`pointerup`,t),()=>{window.removeEventListener(`pointermove`,e),window.removeEventListener(`pointerup`,t)}},[a]),f(`div`,{ref:s,className:e,style:t,onPointerDown:e=>{c.current=!0,l(e.clientX,`live`)},children:f(`div`,{className:n.d,style:{left:`${r(i,0,1)*100}%`,background:o}})})}function y(){return p(`svg`,{width:`16`,height:`16`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":!0,children:[f(`path`,{d:`m2 22 1-1h3l9-9`}),f(`path`,{d:`M3 21v-3l9-9`}),f(`path`,{d:`m15 5 3 3`}),f(`path`,{d:`M17.5 2.5a2.12 2.12 0 0 1 3 3L12 14l-4 1 1-4Z`})]})}export{m as default};
|
package/dist/fields/Date.d.ts
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { TInput } from './type';
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Дата с выпадающим календарём (портал).
|
|
5
|
+
*
|
|
6
|
+
* **В форме:** `string` `YYYY-MM-DD` или `""`.
|
|
7
|
+
*
|
|
8
|
+
* **Элементы:** капсула → кнопка-триггер с текстом `дд.мм.гггг`.
|
|
9
|
+
* `icon` — **полная замена** триггера (не слот `before`). `min`/`max` — `Date`.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* <DateField name="due" form={form} label="Срок" min={new Date()} />
|
|
13
|
+
*/
|
|
14
|
+
export type TDate = TInput & {
|
|
4
15
|
max?: Date;
|
|
5
16
|
min?: Date;
|
|
17
|
+
/** Полная замена UI своим триггером (не аффикс) */
|
|
6
18
|
icon?: () => React.JSX.Element;
|
|
7
19
|
};
|
|
8
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Поле даты. Календарь следует за полем при скролле страницы.
|
|
22
|
+
*/
|
|
9
23
|
declare function DateField({ label, error, hidden, className, placeholder, disabled, onValue, max, min, form, icon, name, value, after, before, }: TDate): import("react/jsx-runtime").JSX.Element;
|
|
10
24
|
declare namespace DateField {
|
|
11
25
|
var displayName: string;
|
package/dist/fields/Date.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{isEmptyTextLike as r}from"./visibility.js";import i from"../styles/Form.module.js";import{acquireOverlayZIndex as a,releaseOverlayZIndex as o}from"../overlayZ.js";import s from"react";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";import{createPortal as d}from"react-dom";function f(e){return!!(e==null||typeof e==`string`&&r(e)||e instanceof Date&&Number.isNaN(e.getTime()))}function p(e,t,n,r){let i=new Date(e,t-1,1);return!(n&&new Date(e,t,0)<n||r&&i>r)}function m(e,t,n){return!(t&&e<t.getFullYear()||n&&e>n.getFullYear())}function h(e,t,n){let r=e;if(t){let e=t.getFullYear();r+11<e&&(r=e-11),r<e&&(r=e)}if(n){let e=n.getFullYear();r>e&&(r=e-11),r+11>e&&(r=e-11)}return r}function g(e){if(!(e instanceof HTMLElement))return!1;let t=e.tagName;return t===`INPUT`||t===`TEXTAREA`||t===`SELECT`?!0:!!e.isContentEditable}function _(e){let t=(e||``).trim();if(!t)return null;let n=t.split(/\s+/)[0].replace(/,/g,`.`),r=(e,t,n)=>{if(t<1||t>12||n<1||n>31)return null;let r=e<100?2e3+e:e,i=new Date(r,t-1,n);return Number.isNaN(i.getTime())||i.getFullYear()!==r||i.getMonth()!==t-1||i.getDate()!==n?null:i},i=/^(\d{1,2})[./](\d{1,2})[./](\d{2,4})$/.exec(n);if(i)return r(parseInt(i[3],10),parseInt(i[2],10),parseInt(i[1],10));if(i=/^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(n),i)return r(parseInt(i[1],10),parseInt(i[2],10),parseInt(i[3],10));if(i=/^(\d{1,2})\/(\d{1,2})\/(\d{2,4})$/.exec(n),i)return r(parseInt(i[3],10),parseInt(i[2],10),parseInt(i[1],10));let a=n.replace(/\D/g,``);return a.length===8?r(parseInt(a.slice(4,8),10),parseInt(a.slice(2,4),10),parseInt(a.slice(0,2),10))||r(parseInt(a.slice(0,4),10),parseInt(a.slice(4,6),10),parseInt(a.slice(6,8),10)):null}function v(e,t,n){let r=e.getTime();return t&&r<t.getTime()&&(r=t.getTime()),n&&r>n.getTime()&&(r=n.getTime()),new Date(r)}function y(e){return e.getFullYear()+`-`+w(2,e.getMonth()+1)+`-`+w(2,e.getDate())}function b({label:i=``,error:a,hidden:o=!1,className:p,placeholder:m,disabled:h=!1,onValue:b,max:S,min:C,form:w,icon:E,name:D=``,value:O,after:k=``,before:A=``}){let j=s.useCallback(e=>{if(e==null||typeof e==`string`&&r(e))return``;if(e instanceof Date)return Number.isNaN(e.getTime())?``:e;if(typeof e==`string`||typeof e==`number`){let t=new Date(e);return Number.isNaN(t.getTime())?``:e}return``},[]),{value:M}=n(w,D,O),[N,P]=s.useState(j(M)),F=s.useRef(null),[I,L]=s.useState(null),[R,z]=s.useState(!1),B=s.useCallback(()=>{let e=F.current;if(!e)return;let t=e.getBoundingClientRect();L({x:t.left,y:t.top,b:t.bottom})},[]);s.useEffect(()=>{if(!R)return;B();let e=()=>{B()};return window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,e),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,e)}},[R,B]),s.useEffect(()=>{P(j(M))},[M,j]);let V=s.useCallback(e=>{let t={value:e,name:D};P(e),w&&D&&w.setValue(t),b&&b!==w?.onValue&&b(t)},[w,D,b]);s.useEffect(()=>{if(!R||h)return;let e=``,t=t=>{let n=_(t);if(!n)return!1;let r=y(v(n,C,S));return r===e?!0:(e=r,V(r),z(!1),!0)},n=e=>{g(e.target)||t(e.clipboardData?.getData(`text/plain`)??``)&&(e.preventDefault(),e.stopPropagation())},r=e=>{(e.ctrlKey||e.metaKey)&&(e.key!==`v`&&e.key!==`V`||g(e.target)||navigator.clipboard?.readText&&(e.preventDefault(),navigator.clipboard.readText().then(e=>{t(e)})))};return document.addEventListener(`paste`,n,!0),window.addEventListener(`keydown`,r,!0),()=>{document.removeEventListener(`paste`,n,!0),window.removeEventListener(`keydown`,r,!0)}},[R,h,C,S,V]);let H=e=>{if(h)return;let t=e.currentTarget.getBoundingClientRect();L({x:t.left,y:t.top,b:t.bottom}),z(!0)},U={};m&&(U.placeholder=m);let W=M==null||M===``||typeof M==`string`&&r(M);return o&&W?l(c,{}):u(c,{children:[E?E():u(t,{type:`date`,label:i,error:a,element_blok:{className:[p||``]},element_input:{className:[e.w],props:{"data-disabled":h?`true`:void 0}},children:[A?l(`span`,{className:e.a,children:A}):null,l(`div`,{ref:F,onClick:H,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),H(e))},tabIndex:h?-1:0,role:`button`,"aria-haspopup":`dialog`,"aria-expanded":R,"aria-label":i||m||`Дата`,"data-edit":h?void 0:`true`,...U,style:{cursor:h?`not-allowed`:`pointer`},children:f(N)?``:T(N,`dd.mm.yyyy`)}),k?l(`span`,{className:e.a,children:k}):null]}),R&&I&&d(l(x,{position:I,onValue:V,date:N,max:S,min:C,onOpen:z,className:p},`elcrm-date-popup`),document.body)]})}b.displayName=`DateField`;var x=({date:e,onValue:t,position:n,max:r,min:d,className:f,onOpen:g})=>{let[_]=s.useState(()=>a());s.useEffect(()=>()=>o(_),[_]);let v=new Date,y=new Date(e);Number.isNaN(y.getTime())&&(y=new Date);let b={y:v.getFullYear(),d:v.getDate(),m:v.getMonth()+1},x={y:y.getFullYear(),d:y.getDate(),m:y.getMonth()+1},[S,T]=s.useState({y:x.y,m:x.m}),[E,D]=s.useState(`days`),[O,k]=s.useState(()=>h(x.y-5,d,r)),A=e=>{let t=e.getDay();return t===0&&(t=7),t-1},j=[`пн`,`вт`,`ср`,`чт`,`пт`,`сб`,`вс`],M=S.m-1,N=new Date(S.y,M),P=[];for(let e=0;e<A(N);e++)P.push(0);for(;N.getMonth()===M;)P.push(N.getDate()),N.setDate(N.getDate()+1);let F=()=>{g(!1)},I=()=>{W(S.m+1,S.y)},L=()=>{W(S.m-1,S.y)},R=()=>{E===`days`?L():E===`months`?W(S.m,S.y-1):k(e=>h(e-12,d,r))},z=()=>{E===`days`?I():E===`months`?W(S.m,S.y+1):k(e=>h(e+12,d,r))},B=e=>{e.stopPropagation(),k(h(S.y-5,d,r)),D(`years`)},V=e=>{p(S.y,e,d,r)&&(T({y:S.y,m:e}),D(`days`))},H=e=>{m(e,d,r)&&(T({y:e,m:S.m}),D(`days`))},U=e=>{t(S.y+`-`+w(2,S.m)+`-`+w(2,e)),F()},W=(e,t)=>{t=e>12?t+1:e<1?t-1:t,e=e>12?1:e<1?12:e,T({y:t,m:e})},G=e=>b.y===S.y&&b.m===S.m&&b.d===e?{"data-active":`true`}:{},K=e=>x.y===S.y&&x.m===S.m&&x.d===e?{"data-current":`true`}:{},q=e=>{let t=!0;return r&&r.getTime()<new Date(S.y,S.m-1,e).getTime()&&(t=!1),d&&d.getTime()>new Date(S.y,S.m-1,e).getTime()&&(t=!1),t},J=window.innerHeight-n.b<360?Math.max(8,n.y-360):n.b;J=Math.min(J,window.innerHeight-360-8),J=Math.max(8,J);let Y=window.innerWidth-n.x<270?n.x-246:n.x;return Y=Math.max(8,Math.min(Y,window.innerWidth-270-8)),u(c,{children:[l(`div`,{className:i.o,style:{zIndex:_},onClick:()=>F()}),u(`div`,{className:[i.c,f&&f+`-backdrop`].join(` `),style:{top:J+`px`,left:Y+`px`,zIndex:_+1},onClick:e=>e.stopPropagation(),children:[u(`s`,{children:[l(`i`,{onClick:e=>{e.stopPropagation(),R()},children:l(`svg`,{width:`16`,height:`16`,fill:`currentColor`,viewBox:`0 0 24 24`,xmlns:`http://www.w3.org/2000/svg`,children:l(`path`,{d:`m15.09 4.594-7.646 6.553a1.126 1.126 0 0 0 0 1.708l7.647 6.552c.73.625 1.857.107 1.857-.854V5.447c0-.961-1.127-1.48-1.857-.853Z`})})}),u(`h2`,{children:[l(`span`,{className:i.h,onClick:e=>{e.stopPropagation(),D(E===`years`?`months`:E===`months`?`days`:`months`)},title:E===`years`?`К списку месяцев`:E===`months`?`К календарю`:`Выбрать месяц`,children:C(S.m).name}),l(`span`,{className:i.y,onClick:e=>{e.stopPropagation(),E===`years`?D(`days`):B(e)},title:E===`years`?`К календарю`:`Выбрать год`,children:S.y})]}),l(`i`,{onClick:e=>{e.stopPropagation(),z()},children:l(`svg`,{width:`16`,height:`16`,fill:`currentColor`,viewBox:`0 0 24 24`,xmlns:`http://www.w3.org/2000/svg`,children:l(`path`,{d:`m8.91 19.406 7.646-6.552a1.125 1.125 0 0 0 0-1.709L8.91 4.593c-.73-.625-1.857-.107-1.857.854v13.107c0 .96 1.127 1.479 1.857.852Z`})})})]}),E===`days`&&u(`div`,{"grid-calendar":``,role:`grid`,"aria-label":`Календарь`,children:[j.map((e,t)=>l(`p`,{role:`columnheader`,children:e},`v`+t)),P.map((e,t)=>l(`span`,{role:`gridcell`,children:e!==0&&q(e)?l(`b`,{role:`button`,tabIndex:0,"aria-label":`${e}`,...G(e),...K(e),"aria-selected":K(e)[`data-current`]?`true`:void 0,onClick:()=>U(e),onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),U(e))},children:e}):l(`p`,{"aria-disabled":e===0?void 0:!0,...G(e),...K(e),children:e===0?``:e})},t))]}),E===`months`&&l(`div`,{"grid-month":``,role:`listbox`,"aria-label":`Месяц`,children:Array.from({length:12},(e,t)=>{let n=t+1,a=p(S.y,n,d,r),o=b.y===S.y&&b.m===n?{"data-active":`true`}:{},s=x.y===S.y&&x.m===n?{"data-current":`true`}:{};return l(`b`,{role:`option`,tabIndex:a?0:-1,"aria-selected":s[`data-current`]?`true`:`false`,"aria-disabled":a?void 0:!0,className:a?``:i.d,...o,...s,onClick:()=>a&&V(n),onKeyDown:e=>{a&&(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),V(n))},children:C(n).abc},n)})}),E===`years`&&l(`div`,{"grid-month":``,role:`listbox`,"aria-label":`Год`,children:Array.from({length:12},(e,t)=>{let n=O+t,a=m(n,d,r),o=b.y===n?{"data-active":`true`}:{},s=x.y===n?{"data-current":`true`}:{};return l(`b`,{role:`option`,tabIndex:a?0:-1,"aria-selected":s[`data-current`]?`true`:`false`,"aria-disabled":a?void 0:!0,className:a?``:i.d,...o,...s,onClick:()=>a&&H(n),onKeyDown:e=>{a&&(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),H(n))},children:n},n)})})]})]})},S={val:`01`,name:`—`,names:`—`,abc:`—`};function C(e){let t=typeof e==`number`?e:new Date(e).getMonth()+1;return(!Number.isFinite(t)||t<1||t>12)&&(t=new Date().getMonth()+1),{1:{val:`01`,name:`Январь`,names:`Января`,abc:`Янв`},2:{val:`02`,name:`Февраль`,names:`Февраля`,abc:`Февр`},3:{val:`03`,name:`Март`,names:`Марта`,abc:`Март`},4:{val:`04`,name:`Апрель`,names:`Апреля`,abc:`Апр`},5:{val:`05`,name:`Май`,names:`Мая`,abc:`Май`},6:{val:`06`,name:`Июнь`,names:`Июня`,abc:`Июнь`},7:{val:`07`,name:`Июль`,names:`Июля`,abc:`Июль`},8:{val:`08`,name:`Август`,names:`Августа`,abc:`Авг`},9:{val:`09`,name:`Сентябрь`,names:`Сентября`,abc:`Сент`},10:{val:`10`,name:`Октябрь`,names:`Октября`,abc:`Окт`},11:{val:`11`,name:`Ноябрь`,names:`Ноября`,abc:`Нояб`},12:{val:`12`,name:`Декабрь`,names:`Декабря`,abc:`Дек`}}[t]??S}function w(e,t){let n=t+``;for(;n.length<e;)n=`0`+n;return n}function T(e,t){let n,r;t===void 0?(r=String(e),n=new Date):(r=t,n=new Date(e)),Number.isNaN(n.getTime())&&(n=new Date);let i=r,a={yyyy:n.getFullYear(),yy:(``+n.getFullYear()).slice(-2),mm:(`0`+(n.getMonth()+1)).slice(-2),dd:(`0`+n.getDate()).slice(-2),HH:(`0`+n.getHours()).slice(-2),MM:(`0`+n.getMinutes()).slice(-2),SS:(`0`+n.getSeconds()).slice(-2),__:` \xA0\xA0\xA0\xA0 `,_:` \xA0\xA0 `};for(let e in a)i=i.replace(e,String(a[e]));return i}export{b as default};
|
|
1
|
+
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{createFieldEmit as r}from"./emit.js";import{isEmptyTextLike as i}from"./visibility.js";import{FormatDate as a,clampDateValue as o,isDateDisplayEmpty as s,isEditablePasteTarget as c,parseClipboardDate as l,toStoreDateString as u}from"./dateUtils.js";import d from"./DateCalendar.js";import f from"react";import{Fragment as p,jsx as m,jsxs as h}from"react/jsx-runtime";import{createPortal as g}from"react-dom";function _({label:_=``,error:v,hidden:y=!1,className:b,placeholder:x,disabled:S=!1,onValue:C,max:w,min:T,form:E,icon:D,name:O=``,value:k,after:A=``,before:j=``}){let M=f.useCallback(e=>{if(e==null||typeof e==`string`&&i(e))return``;if(e instanceof Date)return Number.isNaN(e.getTime())?``:e;if(typeof e==`string`||typeof e==`number`){let t=new Date(e);return Number.isNaN(t.getTime())?``:e}return``},[]),{value:N}=n(E,O,k),[P,F]=f.useState(M(N)),I=f.useRef(null),[L,R]=f.useState(null),[z,B]=f.useState(!1),V=f.useCallback(()=>{let e=I.current;if(!e)return;let t=e.getBoundingClientRect();R({x:t.left,y:t.top,b:t.bottom})},[]);f.useEffect(()=>{if(!z)return;V();let e=()=>{V()};return window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,e),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,e)}},[z,V]),f.useEffect(()=>{F(M(N))},[N,M]);let H=f.useCallback(e=>{F(e),r(E,O,C)(e,!0)},[E,O,C]);f.useEffect(()=>{if(!z||S)return;let e=``,t=t=>{let n=l(t);if(!n)return!1;let r=u(o(n,T,w));return r===e?!0:(e=r,H(r),B(!1),!0)},n=e=>{c(e.target)||t(e.clipboardData?.getData(`text/plain`)??``)&&(e.preventDefault(),e.stopPropagation())},r=e=>{(e.ctrlKey||e.metaKey)&&(e.key!==`v`&&e.key!==`V`||c(e.target)||navigator.clipboard?.readText&&(e.preventDefault(),navigator.clipboard.readText().then(e=>{t(e)})))};return document.addEventListener(`paste`,n,!0),window.addEventListener(`keydown`,r,!0),()=>{document.removeEventListener(`paste`,n,!0),window.removeEventListener(`keydown`,r,!0)}},[z,S,T,w,H]);let U=e=>{if(S)return;let t=e.currentTarget.getBoundingClientRect();R({x:t.left,y:t.top,b:t.bottom}),B(!0)},W={};x&&(W.placeholder=x);let G=N==null||N===``||typeof N==`string`&&i(N);return y&&G?m(p,{}):h(p,{children:[D?D():h(t,{type:`date`,label:_,error:v,element_blok:{className:[b||``]},element_input:{className:[e.w],props:{"data-disabled":S?`true`:void 0}},children:[j?m(`span`,{className:e.a,children:j}):null,m(`div`,{ref:I,onClick:U,onKeyDown:e=>{(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),U(e))},tabIndex:S?-1:0,role:`button`,"aria-haspopup":`dialog`,"aria-expanded":z,"aria-label":_||x||`Дата`,"data-edit":S?void 0:`true`,...W,style:{cursor:S?`not-allowed`:`pointer`},children:s(P)?``:a(P,`dd.mm.yyyy`)}),A?m(`span`,{className:e.a,children:A}):null]}),z&&L&&g(m(d,{position:L,onValue:H,date:P,max:w,min:T,onOpen:B,className:b},`elcrm-date-popup`),document.body)]})}_.displayName=`DateField`;export{_ as default};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { TDatePosition } from './dateUtils';
|
|
2
|
+
/**
|
|
3
|
+
* Попап календаря {@link DateField} (портал в `document.body`, якорь при скролле).
|
|
4
|
+
* Не публичный API пакета.
|
|
5
|
+
*/
|
|
6
|
+
export type TDateCalendarProps = {
|
|
7
|
+
/** Текущая дата поля (пустое → сегодня в сетке, не в форме). */
|
|
8
|
+
date: Date | string | number;
|
|
9
|
+
/** Коммит `YYYY-MM-DD`. */
|
|
10
|
+
onValue: (value: string) => void;
|
|
11
|
+
/** Прямоугольник триггера для позиционирования. */
|
|
12
|
+
position: TDatePosition;
|
|
13
|
+
max?: Date;
|
|
14
|
+
min?: Date;
|
|
15
|
+
className?: string;
|
|
16
|
+
/** Закрытие попапа. */
|
|
17
|
+
onOpen: (open: boolean) => void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Календарь: дни / месяцы / годы. z-index из общего стека оверлеев.
|
|
21
|
+
*/
|
|
22
|
+
declare function DateCalendar({ date, onValue, position, max, min, className, onOpen, }: TDateCalendarProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
declare namespace DateCalendar {
|
|
24
|
+
var displayName: string;
|
|
25
|
+
}
|
|
26
|
+
export default DateCalendar;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"../styles/Form.module.js";import{acquireOverlayZIndex as t,releaseOverlayZIndex as n}from"../overlayZ.js";import{FormatMonth as r,FormatNumber as i,clampYearPageStart as a,isMonthInRange as o,isYearInRange as s}from"./dateUtils.js";import c from"react";import{Fragment as l,jsx as u,jsxs as d}from"react/jsx-runtime";function f({date:f,onValue:p,position:m,max:h,min:g,className:_,onOpen:v}){let[y]=c.useState(()=>t());c.useEffect(()=>()=>n(y),[y]);let b=new Date,x=new Date(f);Number.isNaN(x.getTime())&&(x=new Date);let S={y:b.getFullYear(),d:b.getDate(),m:b.getMonth()+1},C={y:x.getFullYear(),d:x.getDate(),m:x.getMonth()+1},[w,T]=c.useState({y:C.y,m:C.m}),[E,D]=c.useState(`days`),[O,k]=c.useState(()=>a(C.y-5,g,h)),A=e=>{let t=e.getDay();return t===0&&(t=7),t-1},j=[`пн`,`вт`,`ср`,`чт`,`пт`,`сб`,`вс`],M=w.m-1,N=new Date(w.y,M),P=[];for(let e=0;e<A(N);e++)P.push(0);for(;N.getMonth()===M;)P.push(N.getDate()),N.setDate(N.getDate()+1);let F=()=>{v(!1)},I=()=>{W(w.m+1,w.y)},L=()=>{W(w.m-1,w.y)},R=()=>{E===`days`?L():E===`months`?W(w.m,w.y-1):k(e=>a(e-12,g,h))},z=()=>{E===`days`?I():E===`months`?W(w.m,w.y+1):k(e=>a(e+12,g,h))},B=e=>{e.stopPropagation(),k(a(w.y-5,g,h)),D(`years`)},V=e=>{o(w.y,e,g,h)&&(T({y:w.y,m:e}),D(`days`))},H=e=>{s(e,g,h)&&(T({y:e,m:w.m}),D(`days`))},U=e=>{p(w.y+`-`+i(2,w.m)+`-`+i(2,e)),F()},W=(e,t)=>{t=e>12?t+1:e<1?t-1:t,e=e>12?1:e<1?12:e,T({y:t,m:e})},G=e=>S.y===w.y&&S.m===w.m&&S.d===e?{"data-active":`true`}:{},K=e=>C.y===w.y&&C.m===w.m&&C.d===e?{"data-current":`true`}:{},q=e=>{let t=!0;return h&&h.getTime()<new Date(w.y,w.m-1,e).getTime()&&(t=!1),g&&g.getTime()>new Date(w.y,w.m-1,e).getTime()&&(t=!1),t},J=window.innerHeight-m.b<360?Math.max(8,m.y-360):m.b;J=Math.min(J,window.innerHeight-360-8),J=Math.max(8,J);let Y=window.innerWidth-m.x<270?m.x-246:m.x;return Y=Math.max(8,Math.min(Y,window.innerWidth-270-8)),d(l,{children:[u(`div`,{className:e.o,style:{zIndex:y},onClick:()=>F()}),d(`div`,{className:[e.c,_&&_+`-backdrop`].join(` `),style:{top:J+`px`,left:Y+`px`,zIndex:y+1},onClick:e=>e.stopPropagation(),children:[d(`s`,{children:[u(`i`,{onClick:e=>{e.stopPropagation(),R()},children:u(`svg`,{width:`16`,height:`16`,fill:`currentColor`,viewBox:`0 0 24 24`,xmlns:`http://www.w3.org/2000/svg`,children:u(`path`,{d:`m15.09 4.594-7.646 6.553a1.126 1.126 0 0 0 0 1.708l7.647 6.552c.73.625 1.857.107 1.857-.854V5.447c0-.961-1.127-1.48-1.857-.853Z`})})}),d(`h2`,{children:[u(`span`,{className:e.h,onClick:e=>{e.stopPropagation(),D(E===`years`?`months`:E===`months`?`days`:`months`)},title:E===`years`?`К списку месяцев`:E===`months`?`К календарю`:`Выбрать месяц`,children:r(w.m).name}),u(`span`,{className:e.y,onClick:e=>{e.stopPropagation(),E===`years`?D(`days`):B(e)},title:E===`years`?`К календарю`:`Выбрать год`,children:w.y})]}),u(`i`,{onClick:e=>{e.stopPropagation(),z()},children:u(`svg`,{width:`16`,height:`16`,fill:`currentColor`,viewBox:`0 0 24 24`,xmlns:`http://www.w3.org/2000/svg`,children:u(`path`,{d:`m8.91 19.406 7.646-6.552a1.125 1.125 0 0 0 0-1.709L8.91 4.593c-.73-.625-1.857-.107-1.857.854v13.107c0 .96 1.127 1.479 1.857.852Z`})})})]}),E===`days`&&d(`div`,{"grid-calendar":``,role:`grid`,"aria-label":`Календарь`,children:[j.map((e,t)=>u(`p`,{role:`columnheader`,children:e},`v`+t)),P.map((e,t)=>u(`span`,{role:`gridcell`,children:e!==0&&q(e)?u(`b`,{role:`button`,tabIndex:0,"aria-label":`${e}`,...G(e),...K(e),"aria-selected":K(e)[`data-current`]?`true`:void 0,onClick:()=>U(e),onKeyDown:t=>{(t.key===`Enter`||t.key===` `)&&(t.preventDefault(),U(e))},children:e}):u(`p`,{"aria-disabled":e===0?void 0:!0,...G(e),...K(e),children:e===0?``:e})},t))]}),E===`months`&&u(`div`,{"grid-month":``,role:`listbox`,"aria-label":`Месяц`,children:Array.from({length:12},(t,n)=>{let i=n+1,a=o(w.y,i,g,h),s=S.y===w.y&&S.m===i?{"data-active":`true`}:{},c=C.y===w.y&&C.m===i?{"data-current":`true`}:{};return u(`b`,{role:`option`,tabIndex:a?0:-1,"aria-selected":c[`data-current`]?`true`:`false`,"aria-disabled":a?void 0:!0,className:a?``:e.d,...s,...c,onClick:()=>a&&V(i),onKeyDown:e=>{a&&(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),V(i))},children:r(i).abc},i)})}),E===`years`&&u(`div`,{"grid-month":``,role:`listbox`,"aria-label":`Год`,children:Array.from({length:12},(t,n)=>{let r=O+n,i=s(r,g,h),a=S.y===r?{"data-active":`true`}:{},o=C.y===r?{"data-current":`true`}:{};return u(`b`,{role:`option`,tabIndex:i?0:-1,"aria-selected":o[`data-current`]?`true`:`false`,"aria-disabled":i?void 0:!0,className:i?``:e.d,...a,...o,onClick:()=>i&&H(r),onKeyDown:e=>{i&&(e.key===`Enter`||e.key===` `)&&(e.preventDefault(),H(r))},children:r},r)})})]})]})}f.displayName=`DateCalendar`;export{f as default};
|
package/dist/fields/Display.d.ts
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Только показ значения (капсула как у String / Textarea, всегда `data-disabled`).
|
|
4
|
+
*
|
|
5
|
+
* **В форме:** любое; на экран идёт `format(value)` или `emptyText`.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <DisplayField name="id" form={form} label="ID" format={(v) => `#${v}`} />
|
|
5
9
|
*/
|
|
6
10
|
export type TDisplay = TInput & {
|
|
7
11
|
/** Форматтер отображаемого текста */
|
|
8
12
|
format?: (value: unknown) => string;
|
|
9
13
|
emptyText?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Многострочный вид (как TextareaField).
|
|
16
|
+
* По умолчанию — если в тексте есть переносы `\n`.
|
|
17
|
+
*/
|
|
18
|
+
multiline?: boolean;
|
|
10
19
|
};
|
|
11
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Readonly-поле. Не пишет в форму при клике.
|
|
22
|
+
*/
|
|
23
|
+
declare function DisplayField({ value, form, name, placeholder, label, error, hidden, disabled, className, after, before, format, emptyText, size, multiline, }: TDisplay): import("react/jsx-runtime").JSX.Element | null;
|
|
12
24
|
declare namespace DisplayField {
|
|
13
25
|
var displayName: string;
|
|
14
26
|
}
|
|
15
|
-
/** @deprecated используйте `DisplayField` */
|
|
16
|
-
export { DisplayField as ReadonlyField };
|
|
17
27
|
export default DisplayField;
|
package/dist/fields/Display.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{isEmptyTextLike as r}from"./visibility.js";import"react";import{jsx as i,jsxs as a}from"react/jsx-runtime";function o(e){if(e==null)return``;if(typeof e==`string`)return e;if(typeof e==`number`||typeof e==`boolean`)return String(e);if(Array.isArray(e))return e.map(String).join(`, `);try{return JSON.stringify(e)}catch{return String(e)}}function s({value:s=``,form:c,name:l=``,placeholder:u=``,label:d=``,error:f=``,hidden:p=!1,className:
|
|
1
|
+
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{isEmptyTextLike as r}from"./visibility.js";import"react";import{jsx as i,jsxs as a}from"react/jsx-runtime";function o(e){if(e==null)return``;if(typeof e==`string`)return e;if(typeof e==`number`||typeof e==`boolean`)return String(e);if(Array.isArray(e))return e.map(String).join(`, `);try{return JSON.stringify(e)}catch{return String(e)}}function s({value:s=``,form:c,name:l=``,placeholder:u=``,label:d=``,error:f=``,hidden:p=!1,disabled:m=!1,className:h=``,after:g=``,before:_=``,format:v=o,emptyText:y,size:b=`m`,multiline:x}){let{value:S}=n(c,l,s),C=v(S),w=!C.trim(),T=w?y||u||`—`:C,E=x===!0||x!==!1&&T.includes(`
|
|
2
|
+
`);return p&&r(S)?null:a(t,{type:`display`,label:d,error:f,size:b,element_blok:{className:[h||``]},element_input:{className:[E?e.n:e.w,e.d],props:{"data-readonly":`true`,"data-disabled":m?`true`:void 0,"data-multiline":E?`true`:void 0}},children:[_?i(`span`,{className:e.a,children:_}):null,i(`div`,{className:w?e.z:void 0,"aria-label":d||`Значение`,"aria-readonly":`true`,"aria-disabled":m?!0:void 0,children:T}),g?i(`span`,{className:e.a,children:g}):null]})}s.displayName=`DisplayField`;export{s as default};
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
/**
|
|
2
|
+
/** Раскладка зоны: default · fill (100% родителя) · overlay (прозрачная поверх превью) */
|
|
3
|
+
export type TDragDropLayout = "default" | "fill" | "overlay";
|
|
4
|
+
/**
|
|
5
|
+
* Зона drag-and-drop (+ клик = тот же выбор файла).
|
|
6
|
+
*
|
|
7
|
+
* **В форме:** как {@link FileField} (`keepFile` / мета).
|
|
8
|
+
*
|
|
9
|
+
* **Элементы:** пунктирная зона `.z` внутри прозрачной капсулы; список файлов под ней.
|
|
10
|
+
* `layout`: `default` | `fill` | `overlay`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <DragDropField name="files" form={form} multiple maxFiles={5} hint="Перетащите сюда" />
|
|
14
|
+
*/
|
|
3
15
|
export type TDragDrop = TInput & {
|
|
4
16
|
accept?: string;
|
|
5
17
|
multiple?: boolean;
|
|
@@ -16,11 +28,22 @@ export type TDragDrop = TInput & {
|
|
|
16
28
|
hint?: string;
|
|
17
29
|
/** Дописывать к уже выбранным при multiple (по умолчанию true) */
|
|
18
30
|
append?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Раскладка без локальных !important-хаков.
|
|
33
|
+
* - `fill` — высота 100% контейнера
|
|
34
|
+
* - `overlay` — как fill + прозрачный фон (превью снизу)
|
|
35
|
+
*/
|
|
36
|
+
layout?: TDragDropLayout;
|
|
37
|
+
/** CSS aspect-ratio на зоне (число или строка, напр. `1` / `"16/9"`) */
|
|
38
|
+
aspectRatio?: string | number;
|
|
39
|
+
/** Скрыть список файлов и «Очистить всё» (часто с overlay) */
|
|
40
|
+
hideList?: boolean;
|
|
19
41
|
};
|
|
20
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Drop-зона. Капсула без рамки (`padding: 0`), рамка у внутренней зоны.
|
|
44
|
+
*/
|
|
45
|
+
declare function DragDropField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, accept, multiple, maxSize, maxFiles, keepFile, hint, append, layout, aspectRatio, hideList, }: TDragDrop): import("react/jsx-runtime").JSX.Element | null;
|
|
21
46
|
declare namespace DragDropField {
|
|
22
47
|
var displayName: string;
|
|
23
48
|
}
|
|
24
|
-
/** Алиас на случай опечатки «DragonDrop» */
|
|
25
|
-
export { DragDropField as DragonDropField };
|
|
26
49
|
export default DragDropField;
|
package/dist/fields/DragDrop.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import r from"../styles/File.module.js";import
|
|
1
|
+
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{createFieldEmit as r}from"./emit.js";import i from"../styles/File.module.js";import a from"../styles/Drag.module.js";import o from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";function l(e){return{name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}}function u(e){return typeof File<`u`&&e instanceof File?l(e):e}function d(e){return e?Array.isArray(e)?e:typeof File<`u`&&e instanceof File||typeof e==`object`&&e&&`name`in e?[e]:[]:[]}function f(e){return e<1024?`${e} Б`:e<1024*1024?`${(e/1024).toFixed(1)} КБ`:`${(e/(1024*1024)).toFixed(1)} МБ`}function p(e,t){if(!t||!t.trim())return!0;let n=t.split(`,`).map(e=>e.trim().toLowerCase()).filter(Boolean);if(n.length===0)return!0;let r=e.name.toLowerCase(),i=(e.type||``).toLowerCase();return n.some(e=>{if(e.startsWith(`.`))return r.endsWith(e);if(e.endsWith(`/*`)){let t=e.slice(0,-1);return i.startsWith(t)}return i===e})}function m(e){return`${e.name}:${e.size}`}function h({value:h,form:g,onValue:_,onBlur:v,name:y=``,placeholder:b=`Перетащите файлы сюда или нажмите`,label:x=``,error:S=``,hidden:C=!1,disabled:w=!1,className:T=``,after:E=``,before:D=``,accept:O,multiple:k=!0,maxSize:A,maxFiles:j,keepFile:M=!0,hint:N,append:P=!0,layout:F=`default`,aspectRatio:I,hideList:L=!1}){let{value:R}=n(g,y,h),z=d(R),B=!w,V=o.useRef(null),[H,U]=o.useState(``),[W,G]=o.useState(!1),K=o.useRef(0),q=e=>{r(g,y,_)(e,!0)},J=e=>{if(e.length===0)return;let t=e.filter(e=>!p(e,O));if(t.length>0){U(`Тип файла не подходит: ${t.map(e=>e.name).join(`, `)}`);return}if(A!==void 0){let t=e.find(e=>e.size>A);if(t){U(`Файл «${t.name}» больше ${f(A)}`);return}}let n;if(!k)n=[e[0]];else if(P&&z.length>0){let t=new Set(z.map(e=>m(e))),r=e.filter(e=>!t.has(m(e))),i=z.filter(e=>typeof File<`u`&&e instanceof File);n=M&&i.length===z.length?[...i,...r]:[...e]}else n=e;if(j!==void 0&&n.length>j){U(`Не больше ${j} файлов`);return}U(``);let r=M?k?n:n[0]:k?n.map(l):l(n[0]);q(r),v?.({value:r,name:y})},Y=()=>{B&&(q(k?[]:null),V.current&&(V.current.value=``),U(``))},X=e=>{if(!B)return;if(!k){Y();return}let t=z.filter((t,n)=>n!==e);q(M?t:t.map(u)),V.current&&(V.current.value=``)},ee=e=>{e.preventDefault(),e.stopPropagation(),B&&(K.current+=1,G(!0))},te=e=>{e.preventDefault(),e.stopPropagation(),--K.current,K.current<=0&&(K.current=0,G(!1))},ne=e=>{e.preventDefault(),e.stopPropagation(),e.dataTransfer&&(e.dataTransfer.dropEffect=B?`copy`:`none`)},re=e=>{e.preventDefault(),e.stopPropagation(),K.current=0,G(!1),B&&J(Array.from(e.dataTransfer.files||[]))},Z=z.length===0;if(C&&Z)return null;let ie=S||H,ae=N||b,Q=I==null?void 0:typeof I==`number`?String(I):I,$=!L&&!Z;return c(t,{type:`dragdrop`,label:x,error:ie,element_blok:{className:[T||``]},element_input:{className:[a.r],props:{"data-disabled":w||!B?`true`:void 0,"data-dragging":W?`true`:void 0,"data-layout":F===`default`?void 0:F,style:Q?{"--field-drag-aspect":Q}:void 0}},children:[D?s(`span`,{className:e.a,children:D}):null,c(`div`,{className:a.i,"data-layout":F===`default`?void 0:F,style:Q?{aspectRatio:Q}:void 0,children:[s(`input`,{ref:V,type:`file`,className:i.p,accept:O,multiple:k,disabled:w||!B,"aria-label":x||b||`Файлы`,onChange:e=>{J(Array.from(e.target.files||[])),e.target.value=``}}),c(`button`,{type:`button`,className:a.z,disabled:!B,"data-dragging":W?`true`:void 0,"data-layout":F===`default`?void 0:F,onClick:()=>V.current?.click(),onDragEnter:ee,onDragLeave:te,onDragOver:ne,onDrop:re,children:[s(`span`,{className:a.h,children:ae}),O&&F===`default`?s(`span`,{className:a.a,children:O}):null]}),$?s(`ul`,{className:a.l,children:z.map((e,t)=>c(`li`,{className:a.t,children:[c(`div`,{className:a.m,children:[s(`span`,{className:a.n,title:e.name,children:e.name}),s(`span`,{className:a.s,children:f(e.size)})]}),B?s(`button`,{type:`button`,className:a.d,"aria-label":`Удалить ${e.name}`,onClick:()=>X(t),children:`×`}):null]},`${e.name}-${t}`))}):null,$&&B?s(`button`,{type:`button`,className:a.c,onClick:Y,children:`Очистить всё`}):null]}),E?s(`span`,{className:e.a,children:E}):null]})}h.displayName=`DragDropField`;export{h as default};
|
package/dist/fields/Email.d.ts
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { TInput } from './type';
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Email. Нативный `<input type="email">`.
|
|
5
|
+
*
|
|
6
|
+
* **В форме:** `string`.
|
|
7
|
+
*
|
|
8
|
+
* @property validate — проверка формата на blur (по умолчанию `true`); ошибка «Некорректный email».
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <EmailField name="email" form={form} label="Почта" autoComplete="email" />
|
|
12
|
+
*/
|
|
13
|
+
export type TEmail = TInput & {
|
|
14
|
+
/** Проверка формата при blur (по умолчанию `true`) */
|
|
15
|
+
validate?: boolean;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Поле email. Справа при ошибке валидации — бейдж, если не задан `after`.
|
|
19
|
+
*/
|
|
20
|
+
declare function EmailField({ validate, error, onBlur, onValue, after, ...rest }: TEmail): import("react/jsx-runtime").JSX.Element;
|
|
6
21
|
declare namespace EmailField {
|
|
7
22
|
var displayName: string;
|
|
8
23
|
}
|
package/dist/fields/Email.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"./NativeTextField.js";import t from"react";import{jsx as
|
|
1
|
+
import e from"./NativeTextField.js";import{validateEmail as t}from"./emailUtils.js";import n from"react";import{jsx as r}from"react/jsx-runtime";function i(){return r(`span`,{"aria-hidden":!0,style:{display:`inline-flex`,alignItems:`center`,justifyContent:`center`,width:20,height:20,borderRadius:`50%`,background:`var(--field-error-color)`,color:`#fff`,fontSize:12,fontWeight:700,lineHeight:1,flexShrink:0},children:`i`})}function a({validate:a=!0,error:o=``,onBlur:s,onValue:c,after:l,...u}){let[d,f]=n.useState(``),p=o||d,m=l!=null&&l!==``,h=e=>{a&&d&&f(``),c?.(e)},g=e=>{a&&f(t(e.value)),s?.(e)};return r(e,{...u,error:p,fieldType:`email`,inputType:`email`,inputmode:u.inputmode??`email`,autoComplete:u.autoComplete??`email`,defaultAriaLabel:`Email`,onValue:h,onBlur:g,after:m?l:p?r(i,{}):void 0})}a.displayName=`EmailField`;var o=n.memo(a);export{o as default};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { default as React, HTMLAttributes } from 'react';
|
|
2
|
+
import { UseFormApi } from '../hooks/use';
|
|
3
|
+
import { TInput, TValue } from './type';
|
|
4
|
+
/**
|
|
5
|
+
* Тип поля в группе — соответствует компоненту `*Field`.
|
|
6
|
+
* `string` → StringField, `number` → NumberField, …
|
|
7
|
+
*/
|
|
8
|
+
export type FieldGroupFieldKind = "string" | "password" | "email" | "url" | "phone" | "number" | "money" | "percent" | "mask" | "date" | "time" | "textarea" | "select" | "radio" | "tabs" | "check" | "color" | "tags" | "rating" | "code" | "card" | "file" | "drag" | "rich" | "display" | "hidden" | "range" | "options" | "modal";
|
|
9
|
+
/** Пропы, которые группа задаёт сама (item их не дублирует) */
|
|
10
|
+
type GroupOwned = "form" | "onValue" | "onBlur";
|
|
11
|
+
/**
|
|
12
|
+
* Частые пропы конкретных полей (без `[key: string]` — сохраняем типы `field`/`name`).
|
|
13
|
+
* Редкие пропы можно передать через assertion или расширить тип.
|
|
14
|
+
*/
|
|
15
|
+
type FieldGroupItemExtras = {
|
|
16
|
+
/** Select / Radio / Tabs / Options */
|
|
17
|
+
options?: unknown;
|
|
18
|
+
/** Select: порядок id */
|
|
19
|
+
order?: number[];
|
|
20
|
+
/** Mask: шаблон `_` */
|
|
21
|
+
format?: string;
|
|
22
|
+
/** Code: число ячеек */
|
|
23
|
+
length?: number;
|
|
24
|
+
/** Code: визуальные группы */
|
|
25
|
+
group?: number;
|
|
26
|
+
cellSize?: number | string;
|
|
27
|
+
numeric?: boolean;
|
|
28
|
+
/** Card / Email: проверка на blur */
|
|
29
|
+
validate?: boolean;
|
|
30
|
+
/** Money: множитель записи в форму */
|
|
31
|
+
course?: number;
|
|
32
|
+
isClear?: boolean;
|
|
33
|
+
decimals?: number;
|
|
34
|
+
min?: number | string | Date;
|
|
35
|
+
max?: number | string | Date;
|
|
36
|
+
step?: number;
|
|
37
|
+
variant?: string;
|
|
38
|
+
inline?: boolean;
|
|
39
|
+
equal?: boolean;
|
|
40
|
+
maxTags?: number;
|
|
41
|
+
separators?: string[];
|
|
42
|
+
allowClear?: boolean;
|
|
43
|
+
accept?: string;
|
|
44
|
+
multiple?: boolean;
|
|
45
|
+
maxSize?: number;
|
|
46
|
+
maxFiles?: number;
|
|
47
|
+
keepFile?: boolean;
|
|
48
|
+
hint?: string;
|
|
49
|
+
append?: boolean;
|
|
50
|
+
layout?: string;
|
|
51
|
+
aspectRatio?: string | number;
|
|
52
|
+
hideList?: boolean;
|
|
53
|
+
toolbar?: boolean;
|
|
54
|
+
colors?: string[];
|
|
55
|
+
alpha?: boolean;
|
|
56
|
+
emptyText?: string;
|
|
57
|
+
/** Options / Modal: `"module.name"` */
|
|
58
|
+
modal?: string;
|
|
59
|
+
inlinePalette?: boolean;
|
|
60
|
+
outFormat?: "array" | "string" | "object";
|
|
61
|
+
separator?: string;
|
|
62
|
+
picker?: boolean;
|
|
63
|
+
presets?: string[];
|
|
64
|
+
align?: "start" | "center" | "top" | "bottom";
|
|
65
|
+
showFocusRing?: boolean;
|
|
66
|
+
native?: boolean;
|
|
67
|
+
eyes?: [React.ReactNode?, React.ReactNode?];
|
|
68
|
+
autoComplete?: string;
|
|
69
|
+
inputType?: TInput["inputType"];
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Элемент группы: обязательны `field` + `name`,
|
|
73
|
+
* остальное — пропы конкретного поля (`label`, `placeholder`, `options`, …).
|
|
74
|
+
*/
|
|
75
|
+
export type FieldGroupItem = {
|
|
76
|
+
/** Какое поле рендерить */
|
|
77
|
+
field: FieldGroupFieldKind;
|
|
78
|
+
/** Имя в form / onValue */
|
|
79
|
+
name: string;
|
|
80
|
+
} & Partial<Omit<TInput, GroupOwned | "name">> & FieldGroupItemExtras;
|
|
81
|
+
/**
|
|
82
|
+
* Несколько полей одним списком `items`.
|
|
83
|
+
*
|
|
84
|
+
* **Элементы:** `div[role=group]` → внутри обычные `*Field`.
|
|
85
|
+
* Общие `form` / `disabled` / `size` / `errors` на группе; у item — `field` + `name` + свои пропы.
|
|
86
|
+
*
|
|
87
|
+
* `field`: `string` → StringField, `select` → SelectField, `card` → CardField, …
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* <FieldGroup form={form} items={[
|
|
91
|
+
* { field: "string", name: "login", label: "Логин", native: true },
|
|
92
|
+
* { field: "email", name: "email", label: "Почта" },
|
|
93
|
+
* ]} />
|
|
94
|
+
*/
|
|
95
|
+
export type FieldGroupProps = Omit<HTMLAttributes<HTMLDivElement>, "onChange"> & {
|
|
96
|
+
items: FieldGroupItem[];
|
|
97
|
+
/** Общая форма для всех items */
|
|
98
|
+
form?: UseFormApi;
|
|
99
|
+
/** Общий onValue (если у item нет своего) */
|
|
100
|
+
onValue?: (data: TValue) => void | Promise<void>;
|
|
101
|
+
/** Общий onBlur */
|
|
102
|
+
onBlur?: (data: TValue) => void | Promise<void>;
|
|
103
|
+
/** disabled на всю группу (item.disabled перекрывает) */
|
|
104
|
+
disabled?: boolean;
|
|
105
|
+
/** размер по умолчанию */
|
|
106
|
+
size?: TInput["size"];
|
|
107
|
+
/**
|
|
108
|
+
* Ошибки по имени поля — удобно снаружи:
|
|
109
|
+
* `errors={{ email: "…", card: "…" }}`
|
|
110
|
+
*/
|
|
111
|
+
errors?: Record<string, string>;
|
|
112
|
+
/**
|
|
113
|
+
* Значения без form (controlled map).
|
|
114
|
+
* Если есть `form` — обычно не нужно.
|
|
115
|
+
*/
|
|
116
|
+
values?: Record<string, unknown>;
|
|
117
|
+
/** Переопределить `--field-group-gap` (число → px) */
|
|
118
|
+
gap?: number | string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Группа полей. Не капсула — только раскладка и прокидывание `form`.
|
|
122
|
+
*/
|
|
123
|
+
declare function FieldGroup({ items, form, onValue, onBlur, disabled, size, errors, values, gap, className, style, ...rest }: FieldGroupProps): import("react/jsx-runtime").JSX.Element;
|
|
124
|
+
declare namespace FieldGroup {
|
|
125
|
+
var displayName: string;
|
|
126
|
+
}
|
|
127
|
+
export default FieldGroup;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"./Range.js";import t from"./Money.js";import n from"./Mask.js";import r from"./Date.js";import i from"./Time.js";import a from"./Textarea.js";import o from"./Phone.js";import s from"./Input.js";import c from"./Numeric.js";import l from"./Options.js";import u from"./Select.js";import d from"./Modal.js";import f from"./Password.js";import p from"./Check.js";import m from"./Color.js";import h from"./Email.js";import g from"./Card.js";import _ from"./Url.js";import v from"./Percent.js";import y from"./Radio.js";import b from"./Tags.js";import x from"./Rating.js";import S from"./Code.js";import C from"./Tabs.js";import w from"./File.js";import T from"./DragDrop.js";import E from"./RichText.js";import D from"./Hidden.js";import O from"./Display.js";import k from"../styles/FieldGroup.module.js";import"react";import{jsx as A}from"react/jsx-runtime";var j=e=>e,M={string:j(s),password:j(f),email:j(h),url:j(_),phone:j(o),number:j(c),money:j(t),percent:j(v),mask:j(n),date:j(r),time:j(i),textarea:j(a),select:j(u),radio:j(y),tabs:j(C),check:j(p),color:j(m),tags:j(b),rating:j(x),code:j(S),card:j(g),file:j(w),drag:j(T),rich:j(E),display:j(O),hidden:j(D),range:j(e),options:j(l),modal:j(d)};function N({items:e,form:t,onValue:n,onBlur:r,disabled:i=!1,size:a,errors:o,values:s,gap:c,className:l,style:u,...d}){let f=c===void 0?u:{...u,"--field-group-gap":typeof c==`number`?`${c}px`:c};return A(`div`,{className:[k.r,l].filter(Boolean).join(` `),role:`group`,style:f,...d,children:e.map(e=>{let{field:c,name:l,disabled:u,size:d,error:f,value:p,...m}=e,h=M[c];if(!h)return null;let g=p===void 0?s?s[l]:void 0:p;return A(h,{name:l,form:t,onValue:n,onBlur:r,disabled:u??i,size:d??a,error:f??o?.[l]??``,...g===void 0?{}:{value:g},...m},l)})})}N.displayName=`FieldGroup`;export{N as default};
|
package/dist/fields/File.d.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
/** Метаданные
|
|
2
|
+
/** Метаданные файла в форме, если не храните сам `File` (`keepFile={false}`). */
|
|
3
3
|
export type TFileMeta = {
|
|
4
4
|
name: string;
|
|
5
5
|
size: number;
|
|
6
6
|
type: string;
|
|
7
7
|
lastModified: number;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* Выбор файла(ов) кнопкой в капсуле.
|
|
11
|
+
*
|
|
12
|
+
* **В форме:** `File` / `File[]` при `keepFile`, иначе {@link TFileMeta} / массив меты.
|
|
13
|
+
*
|
|
14
|
+
* **Элементы:** кнопка «Выбрать» + имя/размер + крестик очистки.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* <FileField name="doc" form={form} label="Файл" accept=".pdf" maxSize={5_000_000} />
|
|
18
|
+
*/
|
|
9
19
|
export type TFile = TInput & {
|
|
10
20
|
accept?: string;
|
|
11
21
|
multiple?: boolean;
|
|
@@ -17,6 +27,9 @@ export type TFile = TInput & {
|
|
|
17
27
|
*/
|
|
18
28
|
keepFile?: boolean;
|
|
19
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Поле файла. Для drag-and-drop зоны смотрите {@link DragDropField}.
|
|
32
|
+
*/
|
|
20
33
|
declare function FileField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, accept, multiple, maxSize, keepFile, }: TFile): import("react/jsx-runtime").JSX.Element | null;
|
|
21
34
|
declare namespace FileField {
|
|
22
35
|
var displayName: string;
|
package/dist/fields/File.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import r from"../styles/File.module.js";import
|
|
1
|
+
import e from"../styles/Field.module.js";import t from"../core/Field.js";import{useFormFieldState as n}from"../hooks/use.js";import{createFieldEmit as r}from"./emit.js";import i from"../styles/File.module.js";import a from"react";import{jsx as o,jsxs as s}from"react/jsx-runtime";function c(e){return{name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}}function l(e){return e?Array.isArray(e)?e:typeof File<`u`&&e instanceof File||typeof e==`object`&&e&&`name`in e?[e]:[]:[]}function u(e){return e<1024?`${e} Б`:e<1024*1024?`${(e/1024).toFixed(1)} КБ`:`${(e/(1024*1024)).toFixed(1)} МБ`}function d({value:d,form:f,onValue:p,onBlur:m,name:h=``,placeholder:g=`Выберите файл`,label:_=``,error:v=``,hidden:y=!1,disabled:b=!1,className:x=``,after:S=``,before:C=``,accept:w,multiple:T=!1,maxSize:E,keepFile:D=!0}){let{value:O}=n(f,h,d),k=l(O),A=!b,j=a.useRef(null),[M,N]=a.useState(``),P=e=>{r(f,h,p)(e,!0)},F=e=>{if(!e||e.length===0)return;let t=Array.from(e);if(E!==void 0){let e=t.find(e=>e.size>E);if(e){N(`Файл «${e.name}» больше ${u(E)}`);return}}N(``);let n=D?T?t:t[0]:T?t.map(c):c(t[0]);P(n),m?.({value:n,name:h})},I=()=>{A&&(P(T?[]:null),j.current&&(j.current.value=``),N(``))},L=k.length===0;return y&&L?null:s(t,{type:`file`,label:_,error:v||M,element_blok:{className:[x||``]},element_input:{className:[i.r],props:{"data-disabled":b||!A?`true`:void 0}},children:[C?o(`span`,{className:e.a,children:C}):null,s(`div`,{className:i.n,children:[o(`input`,{ref:j,type:`file`,className:i.p,accept:w,multiple:T,disabled:b||!A,"aria-label":_||g||`Файл`,onChange:e=>F(e.target.files)}),o(`button`,{type:`button`,className:i.b,disabled:!A,onClick:()=>j.current?.click(),children:g}),o(`div`,{className:i.l,children:L?o(`span`,{className:i.e,children:`Нет файла`}):k.map((e,t)=>s(`span`,{className:i.t,children:[o(`span`,{className:i.m,children:e.name}),o(`span`,{className:i.z,children:u(e.size)})]},`${e.name}-${t}`))}),!L&&A?o(`button`,{type:`button`,className:i.c,"aria-label":`Очистить`,onClick:I,children:`×`}):null]}),S?o(`span`,{className:e.a,children:S}):null]})}d.displayName=`FileField`;export{d as default};
|
package/dist/fields/Hidden.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* Значение в форме без видимого UI.
|
|
4
|
+
*
|
|
5
|
+
* **Элементы:** только `<input type="hidden">` (для нативного submit).
|
|
6
|
+
* Пропы: `value`, `form`, `onValue`, `name`, `id`, `hidden`.
|
|
5
7
|
*/
|
|
6
8
|
export type THidden = Pick<TInput, "value" | "form" | "onValue" | "name" | "id" | "hidden">;
|
|
9
|
+
/**
|
|
10
|
+
* Скрытое поле. `hidden` на этом компоненте полностью убирает input.
|
|
11
|
+
*/
|
|
7
12
|
declare function HiddenField({ value, form, onValue, name, id, hidden, }: THidden): import("react/jsx-runtime").JSX.Element | null;
|
|
8
13
|
declare namespace HiddenField {
|
|
9
14
|
var displayName: string;
|
package/dist/fields/Hidden.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useFormFieldState as e}from"../hooks/use.js";import t from"react";import{jsx as
|
|
1
|
+
import{useFormFieldState as e}from"../hooks/use.js";import{createFieldEmit as t}from"./emit.js";import n from"react";import{jsx as r}from"react/jsx-runtime";function i({value:i=``,form:a,onValue:o,name:s=``,id:c,hidden:l=!1}){let{value:u}=e(a,s,i);if(n.useEffect(()=>{!a||!s||a.getValue(s)===void 0&&t(a,s,o)(u)},[a,s,u,o]),l)return null;let d=u==null?``:typeof u==`string`||typeof u==`number`||typeof u==`boolean`?String(u):JSON.stringify(u);return r(`input`,{type:`hidden`,id:c,name:s||void 0,value:d,readOnly:!0,"data-field":`hidden`,"aria-hidden":!0})}i.displayName=`HiddenField`;export{i as default};
|
package/dist/fields/Input.d.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { default as React } from 'react';
|
|
2
2
|
import { TInput } from './type';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Однострочный текст.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* У `contentEditable` автозаполнение **не работает** как у поля логина.
|
|
8
|
-
* Включите **`native`** и задайте **`autoComplete="username"`** (и оберните поля в `<form>`, при необходимости).
|
|
6
|
+
* **В форме:** `string`.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
8
|
+
* **Элементы:** подпись `label` → капсула → слоты `before` / `after`.
|
|
9
|
+
* Ввод по умолчанию — `contentEditable`; `native` — настоящий `<input>`
|
|
10
|
+
* (нужен для автозаполнения: `autoComplete="username"` + обёртка `<form>`).
|
|
11
|
+
*
|
|
12
|
+
* Общие пропы — {@link TInput}.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <StringField name="login" form={form} label="Логин" native autoComplete="username" />
|
|
11
16
|
*/
|
|
12
17
|
declare function StringField({ value, form, onValue, name, placeholder, label, hidden, disabled, onBlur: onBlurField, className, after, before, error, inputmode, maxLength, spellCheck, native, autoComplete, inputType, id, size, }: TInput): import("react/jsx-runtime").JSX.Element | "";
|
|
13
18
|
declare const _default: React.MemoExoticComponent<typeof StringField>;
|