@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/Input.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import
|
|
1
|
+
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import{createFieldEmit as i}from"./emit.js";import a,{asEditableClipboard as o}from"../events/onEvent.js";import{isEmptyTextLike as s}from"./visibility.js";import c from"react";import{jsx as l,jsxs as u}from"react/jsx-runtime";function d({value:n,form:d,onValue:m,name:h=``,placeholder:g=``,label:_=``,hidden:v=!1,disabled:y=!1,onBlur:b,className:x=``,after:S=``,before:C=``,error:w=``,inputmode:T=`text`,maxLength:E,spellCheck:D=!0,native:O=!1,autoComplete:k,inputType:A=`text`,id:j,size:M=`m`}){let{value:N,version:P}=r(d,h,n),F=!y,I=typeof N==`string`||typeof N==`number`?String(N):``,L=c.useRef(null),R=c.useRef(null),z=c.useRef(!1);c.useLayoutEffect(()=>{if(O)return;let e=R.current;e&&e.textContent!==I&&(e.textContent=I)},[O,I,P]),c.useEffect(()=>{if(!O)return;let e=L.current;e&&e.value!==I&&(e.value=I)},[O,I]);let B=i(d,h,m),V=e=>{let t=e.target;B((t.textContent||``).trim())},H=e=>{B(e.target.value)},U=e=>{let t=e.target;E&&(t.textContent||``).trim().length>=E&&e.key!==`Backspace`&&e.preventDefault(),e.key===`Enter`&&e.preventDefault()},W=e=>{e.key===`Enter`&&e.preventDefault()},G=e=>a.Paste(E||0,o(e));if(v&&s(N))return``;let K=e=>{a.Blur(e),b?.({value:(e.target.textContent||``).trim(),name:h}),z.current=!1},q=e=>{b?.({value:(e.target.value||``).trim(),name:h})},J=e=>{z.current=!0,a.Focus({target:e.currentTarget})},Y={};return g&&(Y.placeholder=g),F&&(Y.edit=``),O?u(t,{type:`input`,label:_,error:w,id:j,size:M,element_blok:{className:[x||``]},element_input:{className:[e.w],props:{"data-disabled":y||!F?`true`:void 0}},children:[C?l(`span`,{className:e.a,children:C}):null,l(f,{inputRef:L,name:h,displayString:I,placeholder:g,maxLength:E,spellCheck:D,disabled:y,editable:F,autoComplete:k,inputType:A,inputmode:T,label:_,onChangeNative:H,handleBlurNative:q,onKeyDownNative:W}),S?l(`span`,{className:e.a,children:S}):null]}):u(t,{type:`input`,label:_,error:w,id:j,size:M,element_blok:{className:[x]},element_input:{className:[e.w],props:{"data-disabled":y||!F?`true`:void 0}},children:[C?l(`span`,{className:e.a,children:C}):null,l(p,{ceRef:R,editable:F,label:_,placeholder:g,spellCheck:D,inputmode:T,dd:Y,onBlur:K,onFocus:J,onPaste:G,onInput:V,onKeyDown:U}),S?l(`span`,{className:e.a,children:S}):null]})}function f({inputRef:t,name:r,displayString:i,placeholder:a,maxLength:o,spellCheck:s,disabled:c,editable:u,autoComplete:d,inputType:f,inputmode:p,label:m,onChangeNative:h,handleBlurNative:g,onKeyDownNative:_}){let v=n();return l(`div`,{className:e.x,children:l(`input`,{id:v?.controlId,ref:t,className:e.y,type:f,name:r||void 0,defaultValue:i,placeholder:a,maxLength:o,spellCheck:s,disabled:c,readOnly:c,autoComplete:d,inputMode:p||`text`,"aria-label":m?void 0:a||`Текст`,"aria-describedby":v?.describedBy,"aria-invalid":v?.errorId?!0:void 0,"aria-disabled":c||!u?!0:void 0,tabIndex:c?-1:0,onChange:h,onBlur:g,onKeyDown:_})})}function p({ceRef:e,editable:t,label:r,placeholder:i,spellCheck:a,inputmode:o,dd:s,onBlur:c,onFocus:u,onPaste:d,onInput:f,onKeyDown:p}){let m=n();return l(`div`,{ref:e,id:m?.controlId,role:`textbox`,"aria-multiline":`false`,"aria-label":r?void 0:i||`Текст`,"aria-describedby":m?.describedBy,"aria-invalid":m?.errorId?!0:void 0,"aria-disabled":!t,tabIndex:t?0:-1,onBlur:c,onFocus:u,onPaste:d,onInput:f,onKeyDown:p,spellCheck:a,inputMode:o||`text`,contentEditable:t?`plaintext-only`:!1,suppressContentEditableWarning:!0,...s})}var m=c.memo(d);export{m as default};
|
package/dist/fields/Mask.d.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Маска ввода. **В форме — только цифры**; на экране шаблон `format`, `_` = слот цифры.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* <MaskField name="inn" form={form} label="ИНН" format="____________" />
|
|
7
|
+
*/
|
|
8
|
+
export type TMask = TInput & {
|
|
9
|
+
/**
|
|
10
|
+
* Шаблон, например `+_(___) ___-__-__` или `99.99.9999`.
|
|
11
|
+
* Цифры в шаблоне — фиксированные, `_` — ввод.
|
|
12
|
+
*/
|
|
3
13
|
format?: string;
|
|
4
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Поле с маской. Не путать с PhoneField (маски стран).
|
|
17
|
+
*/
|
|
5
18
|
declare function MaskField({ value, form, onValue, onBlur: onBlurField, name, format, placeholder, label, error, hidden, disabled, className, after, before, inputmode, view, }: TMask): import("react/jsx-runtime").JSX.Element;
|
|
6
19
|
declare namespace MaskField {
|
|
7
20
|
var displayName: string;
|
package/dist/fields/Mask.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,{asEditableClipboard as
|
|
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,{asEditableClipboard as a}from"../events/onEvent.js";import{isEmptyTextLike as o}from"./visibility.js";import s from"react";import{Fragment as c,jsx as l,jsxs as u}from"react/jsx-runtime";function d(e){let t=window.getSelection();if(!t||t.rangeCount===0)return 0;let n=t.getRangeAt(0);if(!e.contains(n.startContainer))return 0;let r=document.createRange();return r.selectNodeContents(e),r.setEnd(n.startContainer,n.startOffset),r.toString().length}function f(e,t){if(t<=0)return 0;let n=0;for(let r=0;r<e.length;r++)if(/\d/.test(e[r])&&(n++,n===t))return r+1;return e.length}function p({value:p=``,form:m,onValue:h,onBlur:g,name:_=``,format:v=`+_(___) ___-__-__`,placeholder:y=``,label:b=``,error:x=``,hidden:S=!1,disabled:C=!1,className:w=``,after:T=``,before:E=``,inputmode:D=`numeric`,view:O}){let k=!C,{value:A,version:j}=n(m,_,p),M=s.useRef(null),N=s.useRef(!1),P=v,F=!0,I=0,L=(e,t)=>{let n=t.firstChild;if(!n||n.nodeType!==Node.TEXT_NODE)return;let r=n.textContent?.length??0,i=Math.max(0,Math.min(e,r)),a=document.createRange(),o=window.getSelection();o&&(a.setStart(n,i),a.collapse(!0),o.removeAllRanges(),o.addRange(a),t.focus())},R=e=>i.PasteOne(a(e)),z=e=>{P=v,F=!0,I=0;let t=0,n=``,r=e.replace(/\D/g,``);r===``&&(F=!0);let i=r.split(``),a=``;i.forEach(e=>{F&&(a+=e,F&&(I=(``+a).length,P=v.replace(RegExp(`[0-9]`,`g`),`_`),n=``),I>(``+a).length&&(F=!0,P=v.replace(RegExp(`[0-9]`,`g`),`_`),n=``))});let o=P.replace(/\D/g,``);return o.length>=r.length&&(r=o),{q:P.replace(/[_\d]/g,function(){return r.charAt(t++)||`_`}),i:t,v:r,f:n}};s.useLayoutEffect(()=>{if(N.current)return;let e=M.current;if(!e)return;let t=z(String(A||``));e.textContent!==t.q&&(e.textContent=t.q)},[A,j,v]),z(String(A||``));let B=r(m,_,h),V=e=>{let t=e.target,n=t.textContent||``,r=d(t),i=n.slice(0,r).replace(/\D/g,``).length,a=z(n);t.textContent=a.q;let o=f(a.q,i);a.q.length>0&&queueMicrotask(()=>{L(Math.min(o,a.q.length),t)}),B(a.q.replace(/\D/g,``))},H=e=>{e.key===`Enter`&&e.preventDefault()},U=e=>{i.Blur(e),g&&g({value:(e.target.textContent??``).replace(/\D/g,``),name:_}),N.current=!1},W=e=>{N.current=!0,i.Focus({target:e.currentTarget})},G={};y&&(G.placeholder=y),G.inputMode=D??`numeric`;let K=String(A??``).replace(/\D/g,``);return S&&o(K)?l(c,{}):u(t,{type:`input`,label:b,error:x,element_blok:{className:[w]},element_input:{className:[e.w],props:{"data-disabled":C||!k?`true`:void 0}},children:[E?l(`span`,{className:e.a,children:E}):null,l(`div`,{ref:M,onPaste:R,onInput:V,onKeyDown:H,onBlur:U,onFocus:W,role:`textbox`,spellCheck:!1,"aria-label":b||y||_||`Маска`,"aria-disabled":!k,tabIndex:C?-1:0,"data-disabled":k?void 0:`true`,contentEditable:k?`plaintext-only`:!1,suppressContentEditableWarning:!0,...G}),T?l(`span`,{className:e.a,children:T}):null]})}p.displayName=`MaskField`;export{p as default};
|
package/dist/fields/Modal.d.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { UseFormApi } from '../hooks/use';
|
|
3
3
|
import { TValue } from './type';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Одно значение из модалки приложения (`Form.Init.onModal`).
|
|
6
|
+
*
|
|
7
|
+
* **В форме:** `string` или кортеж `[id, подпись]`.
|
|
8
|
+
*
|
|
9
|
+
* **Элементы:** капсула с текстом выбранного; клик → `modal` (`"module.name"`, обязателен).
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* <ModalField name="city" form={form} label="Город" modal="geo.city" />
|
|
13
|
+
*/
|
|
14
|
+
export type TModal = {
|
|
15
|
+
/** Строка или `[id, подпись]` — в капсуле показывается подпись. */
|
|
5
16
|
value?: string | [string | number, string];
|
|
6
17
|
form?: UseFormApi;
|
|
7
18
|
onValue?: (data: TValue) => void | Promise<void>;
|
|
8
19
|
name: string;
|
|
9
20
|
placeholder?: string;
|
|
21
|
+
/** Подпись над капсулой */
|
|
10
22
|
label?: string;
|
|
11
23
|
error?: string;
|
|
12
24
|
hidden?: boolean;
|
|
@@ -15,10 +27,15 @@ interface Input {
|
|
|
15
27
|
before?: ReactNode;
|
|
16
28
|
view?: string;
|
|
17
29
|
className?: string;
|
|
30
|
+
/** Обязательно: `"module.modalName"` → {@link Form.Init} `onModal`. */
|
|
18
31
|
modal: string;
|
|
32
|
+
/** Доп. данные в payload модалки */
|
|
19
33
|
options?: Record<string | number, unknown>;
|
|
20
|
-
}
|
|
21
|
-
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Поле-триггер модалки. Без `Form.Init` колбэк — no-op.
|
|
37
|
+
*/
|
|
38
|
+
declare function ModalField({ value, form, onValue, name, placeholder, label, error, hidden, disabled, after, before, view, modal, options, className, }: TModal): import("react/jsx-runtime").JSX.Element;
|
|
22
39
|
declare namespace ModalField {
|
|
23
40
|
var displayName: string;
|
|
24
41
|
}
|
package/dist/fields/Modal.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{isEmptyTextLike as i}from"./visibility.js";import{useFormInit as a}from"../FormInitContext.js";import"react";import{Fragment as o,jsx as s,jsxs as c}from"react/jsx-runtime";function l({value:l,form:u,onValue:d,name:f,placeholder:p=``,label:m,error:h,hidden:g=!1,disabled:_=!1,after:v,before:y,view:b,modal:x,options:S,className:C}){let{onModal:w}=a(),{value:T}=n(u,f,l),E=T,D=e=>e?Array.isArray(e)?e[1]||``:e:``,O=e=>{if(Array.isArray(e)&&e.length>=2)return[e[0],String(e[1]??``)];if(typeof e==`object`&&e){let t=e;if(t.id!==void 0&&t.name!==void 0)return[t.id,t.name]}return String(e??``)},k=e=>{r(u,f,d)(O(e),!0)},A=()=>{if(_||!x)return;let e=x.split(`.`);if(e.length<2)return;let t=Array.isArray(E)?E[0]:E;w({module:e[0],modalName:e[1],callback:k,onValue:k,options:S,fieldName:f,value:t})},j={};p&&(j.placeholder=p);let M=i(D(E));return g&&M?s(o,{}):c(t,{type:`input`,label:m,element_blok:{className:[C||``]},error:h,element_input:{className:[e.w],props:{onClick:A,"data-disabled":_?`true`:void 0}},children:[y?s(`span`,{className:e.a,children:y}):null,s(`div`,{...j,children:D(E)}),v?s(`span`,{className:e.a,children:v}):null]})}l.displayName=`ModalField`;export{l as default};
|
package/dist/fields/Money.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Деньги. В UI — сумма с разделителями; **в форме — целое × `course`** (копейки при `course={100}`).
|
|
4
|
+
*
|
|
5
|
+
* **Элементы:** contentEditable + опционально кнопка очистки (`isClear`) в капсуле.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* <MoneyField name="sum" form={form} label="Сумма" course={100} />
|
|
9
|
+
*/
|
|
10
|
+
export type TMoney = TInput & {
|
|
3
11
|
/** Курс для отображения (множитель при записи в форму) */
|
|
4
12
|
course?: number;
|
|
5
13
|
/** Кнопка очистки */
|
|
@@ -7,6 +15,9 @@ type TMoney = TInput & {
|
|
|
7
15
|
/** Режим фильтра символов при вводе */
|
|
8
16
|
type?: string;
|
|
9
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Поле суммы. Клавиатура `decimal`.
|
|
20
|
+
*/
|
|
10
21
|
declare function MoneyField({ value, form, onValue, name, placeholder, label, error, hidden, disabled, onBlur: onBlurField, className, after, before, inputmode, course, view, type, isClear, }: TMoney): import("react/jsx-runtime").JSX.Element | "";
|
|
11
22
|
declare namespace MoneyField {
|
|
12
23
|
var displayName: string;
|
package/dist/fields/Money.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,{pastePlainTextIntoSelection as
|
|
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,{pastePlainTextIntoSelection as a}from"../events/onEvent.js";import{isEmptyTextLike as o}from"./visibility.js";import s from"react";import{jsx as c,jsxs as l}from"react/jsx-runtime";function u(e){let t=window.getSelection();if(!t||t.rangeCount===0)return 0;let n=t.getRangeAt(0);if(!e.contains(n.startContainer))return 0;let r=document.createRange();return r.selectNodeContents(e),r.setEnd(n.startContainer,n.startOffset),r.toString().length}function d(e,t,n){let r=Math.max(0,Math.min(t,e.length));return n(e.slice(0,r)).length}function f({value:f,form:m,onValue:h,name:g=``,placeholder:_=``,label:v=``,error:y=``,hidden:b=!1,disabled:x=!1,onBlur:S,className:C=``,after:w=``,before:T=``,inputmode:E=`decimal`,course:D=100,view:O,type:k,isClear:A=!1}){let{value:j,version:M}=n(m,g||``,f),N=m&&g?j:f,P=s.useRef(null),F=s.useRef(!1);s.useLayoutEffect(()=>{if(F.current)return;let e=P.current;if(!e)return;let t=N!=null&&(typeof N==`number`?!0:!o(String(N))),n=!_||t?p(N??``,D):``;e.textContent!==n&&(e.textContent=n)},[N,M,_,D]);let I=e=>k===`multi`?e.replace(/[^+\d.+-]/g,``):e.replace(/[^+\d.]/g,``),L=(e,t)=>{let n=t.firstChild;if(!n||n.nodeType!==Node.TEXT_NODE)return;let r=n.textContent?.length??0,i=Math.max(0,Math.min(e,r)),a=document.createRange(),o=window.getSelection();o&&(a.setStart(n,i),a.collapse(!0),o.removeAllRanges(),o.addRange(a),t.focus())},R=r(m,g,h),z=e=>{let t=e.target,n=t.textContent||``,r=u(t),i=I(n),a=d(n,r,I);R(Number(i||0)*D),t.textContent=i,i.length>0&&queueMicrotask(()=>{L(Math.min(a,i.length),t)})},B=e=>{let t=e.target;i.Blur(e),(t.textContent||``)===``&&(t.textContent=`0`);let n=Number(I(t.textContent||``))*D;R(n,!0),S?.({value:n,name:g}),t.textContent=p(I(t.textContent||``).trim()||`0`),F.current=!1},V=e=>{F.current=!0;let t=e.target;t.textContent=I(t.textContent||``),i.Focus({target:e.currentTarget})},H=e=>{let t=e.currentTarget.previousElementSibling;t&&(t.textContent=``),R(0,!0)},U=e=>{e.key===`Enter`&&e.preventDefault()},W=e=>{e.preventDefault(),a(I(e.clipboardData?.getData(`text/plain`)??``),e.currentTarget)},G=!x,K=N==null||(typeof N==`number`?!1:o(String(N??``)));if(b&&K)return``;let q={};return q.inputMode=E??`decimal`,_&&(q.placeholder=_),G&&(q.edit=``),l(t,{type:`input`,label:v,error:y,element_blok:{className:[C]},element_input:{className:[e.w],props:{"data-disabled":x||!G?`true`:void 0}},children:[T?c(`span`,{className:e.a,children:T}):null,c(`div`,{ref:P,...q,onInput:z,onPaste:W,onBlur:B,onKeyDown:U,onFocus:V,role:`textbox`,spellCheck:!1,"aria-label":v||_||`Сумма`,"aria-disabled":!G,tabIndex:x?-1:0,contentEditable:G?`plaintext-only`:!1,suppressContentEditableWarning:!0}),A&&c(`button`,{type:`button`,onClick:H,"aria-label":`Очистить поле`,children:`x`}),w?c(`span`,{className:e.a,children:w}):null]})}f.displayName=`MoneyField`;function p(e,t){let n;if(typeof e==`string`){let t=e.replace(/\s/g,``).replace(`,`,`.`);n=parseFloat(t===``?`0`:t)}else n=Number(e??0);return Number.isNaN(n)&&(n=0),t!=null&&t>0&&(n/=t),n.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g,`$1 `)}export{f as default};
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Внутренний каркас для {@link EmailField} / {@link UrlField}.
|
|
4
|
+
* Не экспортируется из пакета — снаружи используйте готовые поля.
|
|
5
|
+
*/
|
|
2
6
|
export type TNativeTextField = TInput & {
|
|
3
|
-
/** data-field
|
|
7
|
+
/** `data-field` на корне (email / url). */
|
|
4
8
|
fieldType: string;
|
|
5
|
-
/** type у input */
|
|
9
|
+
/** HTML `type` у `<input>`. */
|
|
6
10
|
inputType: "email" | "url" | "text" | "search" | "tel";
|
|
7
|
-
/**
|
|
11
|
+
/** aria-label, если нет `label` и `placeholder`. */
|
|
8
12
|
defaultAriaLabel: string;
|
|
9
13
|
};
|
|
10
14
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
15
|
+
* Нативный `<input>` внутри той же капсулы, что у остальных полей.
|
|
16
|
+
* **Элементы:** `label` → капсула (`before` | input | `after`) → `error`.
|
|
13
17
|
*/
|
|
14
18
|
declare function NativeTextField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, maxLength, autoComplete, inputmode, id, size, fieldType, inputType, defaultAriaLabel, }: TNativeTextField): import("react/jsx-runtime").JSX.Element | null;
|
|
15
19
|
export default NativeTextField;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import{
|
|
1
|
+
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import{createFieldEmit as i}from"./emit.js";import{isEmptyTextLike as a}from"./visibility.js";import o from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";function l({value:n=``,form:l,onValue:d,onBlur:f,name:p=``,placeholder:m=``,label:h=``,error:g=``,hidden:_=!1,disabled:v=!1,className:y=``,after:b=``,before:x=``,maxLength:S,autoComplete:C,inputmode:w,id:T,size:E=`m`,fieldType:D,inputType:O,defaultAriaLabel:k}){let{value:A,version:j}=r(l,p,n),M=!v,N=typeof A==`string`||typeof A==`number`?String(A):``,P=o.useRef(null),F=i(l,p,d);return o.useEffect(()=>{let e=P.current;!e||e.value===N||(e.value=N)},[N,j]),_&&a(A)?null:c(t,{type:D,label:h,error:g,id:T,size:E,element_blok:{className:[y||``]},element_input:{className:[e.w],props:{"data-disabled":v||!M?`true`:void 0}},children:[x?s(`span`,{className:e.a,children:x}):null,s(u,{inputRef:P,name:p,display:N,placeholder:m,maxLength:S,disabled:v,editable:M,autoComplete:C,inputMode:w,inputType:O,label:h,defaultAriaLabel:k,emit:F,onBlurField:f}),b?s(`span`,{className:e.a,children:b}):null]})}function u({inputRef:t,name:r,display:i,placeholder:a,maxLength:o,disabled:c,editable:l,autoComplete:u,inputMode:d,inputType:f,label:p,defaultAriaLabel:m,emit:h,onBlurField:g}){let _=n();return s(`div`,{className:e.x,children:s(`input`,{id:_?.controlId,ref:t,className:e.y,type:f,name:r||void 0,defaultValue:i,placeholder:a,maxLength:o,disabled:c,readOnly:!l,autoComplete:u,inputMode:d,spellCheck:!1,"aria-label":p?void 0:a||m,"aria-describedby":_?.describedBy,"aria-invalid":_?.errorId?!0:void 0,onChange:e=>h(e.target.value),onBlur:e=>{let t=(e.target.value||``).trim();h(t,!0),g?.({value:t,name:r})},onKeyDown:e=>{e.key===`Enter`&&e.preventDefault()}})})}export{l as default};
|
package/dist/fields/Numeric.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Целые цифры как **строка** в форме (`"123"`), не `number`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - проп **`max`** (и `maxLength`) — лимит **символов**, не верхняя граница числа;
|
|
8
|
-
* - дробных нет (только `\d`).
|
|
5
|
+
* `max` / `maxLength` — лимит **символов**, не максимум числа.
|
|
6
|
+
* Для 0–100 / `number` используйте {@link PercentField}.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
8
|
+
* @example
|
|
9
|
+
* <NumberField name="qty" form={form} label="Кол-во" max={6} />
|
|
11
10
|
*/
|
|
12
|
-
type TNumeric = TInput & {
|
|
13
|
-
/** Макс. число символов (если нет `maxLength`). Не
|
|
11
|
+
export type TNumeric = TInput & {
|
|
12
|
+
/** Макс. число символов (если нет `maxLength`). Не верхняя граница значения. */
|
|
14
13
|
max?: number;
|
|
15
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Поле целых цифр (строка в form).
|
|
17
|
+
*/
|
|
16
18
|
declare function NumberField({ value, form, onValue, name, placeholder, label, hidden, disabled, onBlur: onBlurField, className, after, before, error, size, inputmode, maxLength, max, view, }: TNumeric): import("react/jsx-runtime").JSX.Element | null;
|
|
17
19
|
declare namespace NumberField {
|
|
18
20
|
var displayName: string;
|
package/dist/fields/Numeric.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import i,{asEditableClipboard as
|
|
1
|
+
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import{createFieldEmit as i}from"./emit.js";import a,{asEditableClipboard as o}from"../events/onEvent.js";import{isEmptyTextLike as s}from"./visibility.js";import c from"react";import{jsx as l,jsxs as u}from"react/jsx-runtime";function d({value:n,form:d,onValue:p,name:m=``,placeholder:h=``,label:g=``,hidden:_=!1,disabled:v=!1,onBlur:y,className:b=``,after:x=``,before:S=``,error:C=``,size:w=`m`,inputmode:T=`numeric`,maxLength:E,max:D,view:O}){let k=!v,{value:A,version:j}=r(d,m,n);h=h===void 0?``:h,D=D===void 0?99:D;let M=A!=null&&A!==``?String(A):``,N=c.useRef(null),P=c.useRef(!1);c.useLayoutEffect(()=>{if(P.current)return;let e=N.current;e&&e.textContent!==M&&(e.textContent=M)},[M,j]);let F=E??D,I=(e,t)=>{e.textContent!==t&&(e.textContent=t,queueMicrotask(()=>{let n=e.firstChild;if(n&&n.nodeType===Node.TEXT_NODE){let e=t.length,r=window.getSelection(),i=document.createRange();i.setStart(n,e),i.collapse(!0),r?.removeAllRanges(),r?.addRange(i)}else t||(e.innerHTML=`<br>`)}))},L=i(d,m,p),R=e=>{let t=e.currentTarget,n=(t.textContent||``).replace(/\D/g,``);F>0&&n.length>F&&(n=n.slice(0,F)),I(t,n),L(n)},z=e=>{P.current=!0;let t=e.currentTarget;t.textContent=(t.textContent||``).replace(/\D/g,``),a.Focus({target:e.currentTarget})},B=e=>{if((e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`v`||e.key===`Backspace`)return;let t=(e.currentTarget.textContent??``).replace(/\D/g,``).length;F>0&&t>=F&&e.key.length===1&&/\d/.test(e.key)&&e.preventDefault(),e.key===`Enter`&&e.preventDefault(),e.key.length===1&&!/\d/.test(e.key)&&!e.metaKey&&!e.ctrlKey&&e.preventDefault()},V=e=>{a.PasteDigits(F,o(e)),queueMicrotask(()=>{let e=N.current;if(!e)return;let t=(e.textContent||``).replace(/\D/g,``);F>0&&t.length>F&&(t=t.slice(0,F)),I(e,t),L(t)})},H=e=>{e.preventDefault()},U=e=>{a.Blur(e);let t=(e.currentTarget.textContent||``).replace(/\D/g,``);e.currentTarget.textContent!==t&&(e.currentTarget.textContent=t),y?.({value:t,name:m}),P.current=!1},W={};return k&&(W.edit=``),W.inputMode=T??`numeric`,h&&(W.placeholder=h),_&&s(A)?null:u(t,{type:`numeric`,label:g,error:C,size:w,element_blok:{className:[b||``]},element_input:{className:[e.w],props:{"data-disabled":v||!k?`true`:void 0}},children:[S?l(`span`,{className:e.a,children:S}):null,l(f,{divRef:N,editable:k,label:g,placeholder:h,dd:W,onPaste:V,onDrop:H,onInput:R,onFocus:z,onBlur:U,onKeyDown:B}),x?l(`span`,{className:e.a,children:x}):null]})}function f({divRef:e,editable:t,label:r,placeholder:i,dd:a,onPaste:o,onDrop:s,onInput:c,onFocus:u,onBlur:d,onKeyDown:f}){let p=n();return l(`div`,{ref:e,id:p?.controlId,onPaste:o,onDrop:s,onDragOver:e=>e.preventDefault(),onInput:c,onFocus:u,onBlur:d,onKeyDown:f,spellCheck:!1,role:`textbox`,"aria-multiline":`false`,tabIndex:t?0:-1,"aria-label":r?void 0:i||`Число`,"aria-disabled":t?void 0:!0,"aria-describedby":p?.describedBy,"aria-invalid":p?.errorId?!0:void 0,contentEditable:t?`plaintext-only`:!1,suppressContentEditableWarning:!0,...a})}d.displayName=`NumberField`;export{d as default};
|
package/dist/fields/Options.d.ts
CHANGED
|
@@ -1,31 +1,50 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { UseFormApi } from '../hooks/use';
|
|
3
3
|
import { TValue } from './type';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Выбор через модалку приложения (`Form.Init.onModal`).
|
|
6
|
+
*
|
|
7
|
+
* **В форме:** зависит от `outFormat`: строка ключей, массив или объект-флаги.
|
|
8
|
+
*
|
|
9
|
+
* **Элементы:** капсула-колонка выбранных подписей (`options[id].label`). Клик открывает модалку
|
|
10
|
+
* `modal` вида `"module.name"` (или `module` + `modal`).
|
|
11
|
+
*
|
|
12
|
+
* Нужен {@link Form.Init}.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* <OptionsField name="users" form={form} label="Сотрудники" modal="crm.pickUser" outFormat="string" />
|
|
16
|
+
*/
|
|
17
|
+
export type TOptions = {
|
|
5
18
|
value?: string;
|
|
6
19
|
form?: UseFormApi;
|
|
7
20
|
onValue?: (data: TValue) => void | Promise<void>;
|
|
8
21
|
name: string;
|
|
9
22
|
placeholder?: string;
|
|
10
|
-
/** Подпись поля */
|
|
11
23
|
label?: string;
|
|
12
24
|
error?: string;
|
|
13
25
|
hidden?: boolean;
|
|
14
|
-
/** true — блок выбора не открывается */
|
|
15
26
|
disabled?: boolean;
|
|
16
27
|
after?: ReactNode;
|
|
17
28
|
before?: ReactNode;
|
|
18
29
|
view?: string;
|
|
19
30
|
className?: string;
|
|
31
|
+
/** Имя модуля модалки, если `modal` без точки */
|
|
20
32
|
module?: string;
|
|
33
|
+
/** `"module.modalName"` или имя модалки вместе с `module` */
|
|
21
34
|
modal?: string;
|
|
35
|
+
/** Подписи выбранных ключей: `{ [id]: { label } }` */
|
|
22
36
|
options?: Record<string, {
|
|
23
37
|
label?: string;
|
|
24
38
|
}>;
|
|
39
|
+
/** Как писать выбор в форму. @default зависит от текущего value */
|
|
25
40
|
outFormat?: "array" | "string" | "object";
|
|
41
|
+
/** Разделитель ключей при `outFormat="string"`. @default "," */
|
|
26
42
|
separator?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Список выбранных значений; клик по капсуле открывает модалку приложения.
|
|
46
|
+
*/
|
|
47
|
+
declare function OptionsField({ value, form, onValue, name, placeholder, separator, label, error, hidden, disabled, outFormat, after, before, modal, options, className, }: TOptions): import("react/jsx-runtime").JSX.Element;
|
|
29
48
|
declare namespace OptionsField {
|
|
30
49
|
var displayName: string;
|
|
31
50
|
}
|
package/dist/fields/Options.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{useFormInit as i}from"../FormInitContext.js";import{Fragment as a,jsx as o,jsxs as s}from"react/jsx-runtime";var c=(e,t=`,`)=>(e!==void 0&&e!==``?e.split(t):[]).filter(e=>e!==``).reduce((e,t)=>(e[t]=!0,e),{});function l({value:l,form:u,onValue:d,name:f,placeholder:p=``,separator:m=`,`,label:h,error:g,hidden:_=!1,disabled:v=!1,outFormat:y=`object`,after:b,before:x,modal:S,options:C,className:w}){let{onModal:T}=i(),{value:E}=n(u,f,l),D=E,O=typeof D==`string`?c(D,m):D&&typeof D==`object`?D:{},k=()=>{if(v||!S)return;let e=S.split(`.`);T({module:e[0],modalName:e[1],callback:e=>{let t=e,n=y===`array`?Object.keys(t):y===`string`?Object.keys(t).filter(e=>t[e]).join(m):t;r(u,f,d)(n,!0)},onValue:d,options:C,fieldName:f,value:O})},A={};p&&(A.placeholder=p);let j=Object.keys(O).length===0;return _&&j?o(a,{}):s(t,{type:`options`,label:h,error:g,element_blok:{className:[w||``]},element_input:{className:[e.g],props:{onClick:k,"data-disabled":v?`true`:void 0}},children:[x?o(`span`,{className:e.a,children:x}):null,Object.keys(O).map(e=>o(`div`,{children:C?.[e]?.label},e)),b?o(`span`,{className:e.a,children:b}):null]})}l.displayName=`OptionsField`;export{l as default};
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { TPasswordField } from './type';
|
|
2
|
+
/**
|
|
3
|
+
* Пароль с переключателем видимости (кнопка-глаз в слоте `after`).
|
|
4
|
+
*
|
|
5
|
+
* **В форме:** `string`.
|
|
6
|
+
*
|
|
7
|
+
* **Элементы:** капсула + `<input type="password"|text>` + кнопка глаза.
|
|
8
|
+
* Проп `native` **скрывает весь компонент** (не делает input) — см. {@link TPasswordField}.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <PasswordField name="password" form={form} label="Пароль" autoComplete="current-password" />
|
|
12
|
+
*/
|
|
2
13
|
declare function PasswordField({ value, form, onValue, name, placeholder, label, error, hidden, disabled, onBlur: onBlurField, className, after, before, native, maxLength, eyes, id, size, }: TPasswordField): import("react/jsx-runtime").JSX.Element | "";
|
|
3
14
|
declare namespace PasswordField {
|
|
4
15
|
var displayName: string;
|
package/dist/fields/Password.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import i from"../events/onEvent.js";import{isEmptyTextLike as
|
|
1
|
+
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import{createFieldEmit as i}from"./emit.js";import a from"../events/onEvent.js";import{isEmptyTextLike as o}from"./visibility.js";import s from"react";import{jsx as c,jsxs as l}from"react/jsx-runtime";function u(){return l(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":!0,children:[c(`path`,{d:`M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z`}),c(`circle`,{cx:`12`,cy:`12`,r:`3`})]})}function d(){return l(`svg`,{xmlns:`http://www.w3.org/2000/svg`,width:`20`,height:`20`,viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":!0,children:[c(`path`,{d:`M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24`}),c(`line`,{x1:`1`,y1:`1`,x2:`23`,y2:`23`})]})}function f({value:n,form:f,onValue:m,name:h=``,placeholder:g=``,label:_,error:v,hidden:y=!1,disabled:b=!1,onBlur:x,className:S,after:C=``,before:w=``,native:T=!1,maxLength:E,eyes:D,id:O,size:k=`m`}){let{value:A}=r(f,h,n),[j,M]=s.useState(!1),N=s.useRef(null),P=typeof A==`string`?A:A==null?``:String(A);s.useEffect(()=>{let e=N.current;e&&e.value!==P&&(e.value=P)},[P]);let F=i(f,h,m),I=e=>{F(e.target.value)},L=e=>{e.key===`Enter`&&e.preventDefault()},R=e=>{a.Blur(e),x?.({value:(e.target.value||``).trim(),name:h||``})};if(T)return``;let z=!b;if(y&&o(P))return``;let B=D?.[0]??c(u,{}),V=D?.[1]??c(d,{});return l(t,{type:`input`,label:_,error:v,id:O,size:k,element_blok:{className:[S||``]},element_input:{className:[e.w],props:{"data-disabled":b||!z?`true`:void 0}},children:[w?c(`span`,{className:e.a,children:w}):null,c(p,{inputRef:N,passwordVisible:j,currentValue:P,disabled:b,editable:z,maxLength:E,placeholder:g,label:_,onChangeInput:I,keyDown:L,handleBlur:R}),c(`button`,{type:`button`,className:e.a,tabIndex:b?-1:0,"aria-label":j?`Скрыть пароль`:`Показать пароль`,"aria-pressed":j,disabled:b,onClick:()=>!b&&M(!j),children:j?V:B}),C?c(`span`,{className:e.a,children:C}):null]})}function p({inputRef:t,passwordVisible:r,currentValue:i,disabled:a,editable:o,maxLength:s,placeholder:l,label:u,onChangeInput:d,keyDown:f,handleBlur:p}){let m=n();return c(`div`,{className:e.x,children:c(`input`,{id:m?.controlId,className:e.y,type:r?`text`:`password`,defaultValue:i,ref:t,onChange:d,onKeyDown:f,onBlur:p,disabled:a,readOnly:a,maxLength:s,spellCheck:!1,autoComplete:`current-password`,"aria-label":u?void 0:l||`Пароль`,"aria-describedby":m?.describedBy,"aria-invalid":m?.errorId?!0:void 0,"aria-disabled":a||!o,tabIndex:a?-1:0,placeholder:l})})}f.displayName=`PasswordField`;export{f as default};
|
package/dist/fields/Percent.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Процент как **`number`** в форме (не строка).
|
|
3
4
|
*
|
|
4
|
-
* Отличия от NumberField:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Отличия от {@link NumberField}: значение `number`; `min`/`max` — границы числа (по умолчанию 0–100);
|
|
6
|
+
* `decimals` — дробная часть; справа по умолчанию аффикс `%`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* <PercentField name="vat" form={form} label="НДС" min={0} max={100} decimals={2} />
|
|
9
10
|
*/
|
|
10
11
|
export type TPercent = TInput & {
|
|
11
12
|
min?: number;
|
|
@@ -13,6 +14,9 @@ export type TPercent = TInput & {
|
|
|
13
14
|
/** Допускать дробную часть */
|
|
14
15
|
decimals?: number;
|
|
15
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Поле процента. Не путать с {@link NumberField} (там строка цифр).
|
|
19
|
+
*/
|
|
16
20
|
declare function PercentField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, min, max, decimals, inputmode, }: TPercent): import("react/jsx-runtime").JSX.Element | null;
|
|
17
21
|
declare namespace PercentField {
|
|
18
22
|
var displayName: string;
|
package/dist/fields/Percent.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,{asEditableClipboard as
|
|
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,{asEditableClipboard as a}from"../events/onEvent.js";import o from"react";import{jsx as s,jsxs as c}from"react/jsx-runtime";function l(e,t,n){return Number.isFinite(e)?Math.min(n,Math.max(t,e)):t}function u(e,t){let n=e.replace(`,`,`.`).replace(/[^\d.]/g,``);if(!n)return null;let r=Number(n);if(!Number.isFinite(r))return null;if(t<=0)return Math.round(r);let i=10**t;return Math.round(r*i)/i}function d(e,t){return Number.isFinite(e)?String(t<=0?Math.round(e):Number(e.toFixed(t))):``}function f({value:f=0,form:p,onValue:m,onBlur:h,name:g=``,placeholder:_=`0`,label:v=``,error:y=``,hidden:b=!1,disabled:x=!1,className:S=``,after:C,before:w=``,min:T=0,max:E=100,decimals:D=0,inputmode:O=`decimal`}){let{value:k,version:A}=n(p,g,f),j=!x,M=l(Number(k),T,E),N=k==null||k===``?``:d(M,D),P=o.useRef(null),F=o.useRef(!1);o.useLayoutEffect(()=>{if(F.current)return;let e=P.current;e&&e.textContent!==N&&(e.textContent=N)},[N,A]);let I=r(p,g,m),L=e=>{let t=(e.currentTarget.textContent||``).replace(/[^\d.,]/g,``);e.currentTarget.textContent!==t&&(e.currentTarget.textContent=t);let n=u(t,D);if(n===null){I(``);return}I(l(n,T,E))},R=e=>{i.Blur(e),F.current=!1;let t=u(e.currentTarget.textContent||``,D),n=t===null?T:l(t,T,E),r=d(n,D);e.currentTarget.textContent!==r&&(e.currentTarget.textContent=r),I(n,!0),h?.({value:n,name:g})};if(b&&(k==null||k===``))return null;let z={};return _&&(z.placeholder=_),j&&(z.edit=``),z.inputMode=O||`decimal`,c(t,{type:`percent`,label:v,error:y,element_blok:{className:[S||``]},element_input:{className:[e.w],props:{"data-disabled":x||!j?`true`:void 0}},children:[w?s(`span`,{className:e.a,children:w}):null,s(`div`,{ref:P,onInput:L,onFocus:e=>{F.current=!0,i.Focus({target:e.currentTarget})},onBlur:R,onPaste:e=>i.Paste(12,a(e)),onKeyDown:e=>{e.key===`Enter`&&e.preventDefault(),e.key.length===1&&!/[\d.,]/.test(e.key)&&!e.metaKey&&!e.ctrlKey&&e.preventDefault()},spellCheck:!1,role:`textbox`,tabIndex:j?0:-1,"aria-label":v||_||`Процент`,contentEditable:j?`plaintext-only`:!1,suppressContentEditableWarning:!0,...z}),C===void 0?s(`span`,{className:e.a,children:`%`}):s(`span`,{className:e.a,children:C})]})}f.displayName=`PercentField`;export{f as default};
|
package/dist/fields/Phone.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Телефон с маской по стране. В форму пишутся **только цифры** (без `+` и скобок).
|
|
4
|
+
*
|
|
5
|
+
* **В форме:** `string` цифр.
|
|
6
|
+
*
|
|
7
|
+
* **Элементы:** капсула, contentEditable по шаблону; полный каталог масок — lazy при фокусе.
|
|
8
|
+
* Копирование может вызвать `Form.Init.onNotice`.
|
|
9
|
+
*
|
|
10
|
+
* Пропы — {@link TInput}.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <PhoneField name="phone" form={form} label="Телефон" />
|
|
14
|
+
*/
|
|
3
15
|
declare function PhoneField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, inputmode, id, }: TInput): import("react/jsx-runtime").JSX.Element | null;
|
|
4
16
|
declare namespace PhoneField {
|
|
5
17
|
var displayName: string;
|
package/dist/fields/Phone.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import i,{asEditableClipboard as
|
|
1
|
+
import e from"../styles/Field.module.js";import t,{useFieldA11y as n}from"../core/Field.js";import{useFormFieldState as r}from"../hooks/use.js";import{createFieldEmit as i}from"./emit.js";import a,{asEditableClipboard as o}from"../events/onEvent.js";import{isEmptyTextLike as s}from"./visibility.js";import{PHONE_MASK_DEFAULTS as c}from"../mask/phoneDefaults.js";import{useFormInit as l}from"../FormInitContext.js";import u from"react";import{jsx as d,jsxs as f}from"react/jsx-runtime";var p={...c},m=!1,h=null;function g(){return m?Promise.resolve(!1):h||(h=import(`../mask/MaskPhone.js`).then(e=>(p={...c,...e.default},m=!0,!0)),h)}function _(e){let t=window.getSelection();if(!t||t.rangeCount===0)return 0;let n=t.getRangeAt(0);if(!e.contains(n.startContainer))return 0;let r=document.createRange();return r.selectNodeContents(e),r.setEnd(n.startContainer,n.startOffset),r.toString().length}function v(e,t){if(t<=0)return 0;let n=0;for(let r=0;r<e.length;r++)if(/\d/.test(e[r])&&(n++,n===t))return r+1;return e.length}function y(e){let t=`+_(___) ___-__-__`,n=!0,r=0,i=0,a=``,o=e.replace(/\D/g,``);o===``&&(n=!0);let s=o.split(``),c=``;s.forEach(e=>{n&&(c+=e,p[c]!==void 0&&n?(n=!1,t=p[c].mask.replace(RegExp(`[0-9]`,`g`),`_`),a=p[c].code):(n&&(r=(``+c).length,t=`+${c}_(___) ___-__-__`.replace(RegExp(`[0-9]`,`g`),`_`),a=``),r>(``+c).length&&(n=!0,t=`+${c}_(___) ___-__-__`.replace(RegExp(`[0-9]`,`g`),`_`),a=``)))});let l=t.replace(/\D/g,``);return l.length>=o.length&&(o=l),{q:t.replace(/[_\d]/g,function(){return o.charAt(i++)||`_`}),i,v:o,f:a}}function b({value:n,form:c,onValue:p,onBlur:m,name:h=``,placeholder:b=``,label:S=``,error:C=``,hidden:w=!1,disabled:T=!1,className:E=``,after:D=``,before:O=``,inputmode:k=`tel`,id:A}){let{onNotice:j}=l(),M=!T,{value:N,version:P}=r(c,h,n),[,F]=u.useState(0),I=u.useRef(null),L=u.useRef(!1),R=(e,t)=>{let n=t.firstChild;if(!n||n.nodeType!==Node.TEXT_NODE)return;let r=n.textContent?.length??0,i=Math.max(0,Math.min(e,r)),a=document.createRange(),o=window.getSelection();o&&(a.setStart(n,i),a.collapse(!0),o.removeAllRanges(),o.addRange(a),t.focus())},z=i(c,h,p),B=e=>{let t=e.target,n=t.textContent||``,r=_(t),i=n.slice(0,r).replace(/\D/g,``).length,a=y(n);t.textContent=a.q;let o=v(a.q,i);a.q.length>0&&queueMicrotask(()=>{R(Math.min(o,a.q.length),t)}),z(a.q.replace(/\D/g,``))},V=e=>a.PasteOne(o(e));u.useLayoutEffect(()=>{if(L.current)return;let e=I.current;if(!e)return;let t=y(String(N||``));e.textContent!==t.q&&(e.textContent=t.q)},[N,P]);let H=e=>{e.preventDefault();let t=e.target;navigator.clipboard.writeText((t.textContent||``).replace(/\D/g,``)).then(function(){j(`Данные в буфер обмене`)},function(e){})},U=e=>{e.key===`Enter`&&e.preventDefault()},W=e=>{a.Blur(e),m&&m({value:(e.target.textContent||``).replace(/\D/g,``),name:h||``}),L.current=!1},G=e=>{L.current=!0,a.Focus({target:e.currentTarget}),g().then(e=>{if(!e)return;F(e=>e+1);let t=I.current;if(!t||document.activeElement!==t)return;let n=t.textContent||``,r=_(t),i=n.slice(0,r).replace(/\D/g,``).length,a=y(n);t.textContent!==a.q&&(t.textContent=a.q,queueMicrotask(()=>{R(Math.min(v(a.q,i),a.q.length),t)}))})},K={};b&&(K.placeholder=b),K.inputMode=k??`tel`;let q=String(N??``).replace(/\D/g,``);return w&&s(q)?null:f(t,{type:`input`,label:S,error:C,id:A,element_blok:{className:[E]},element_input:{className:[e.w],props:{"data-disabled":T||!M?`true`:void 0}},children:[O?d(`span`,{className:e.a,children:O}):null,d(x,{divRef:I,editable:M,disabled:T,label:S,placeholder:b,dd:K,onPaste:V,onInput:B,onKeyDown:U,onContextMenu:H,onBlur:W,onFocus:G}),D?d(`span`,{className:e.a,children:D}):null]})}function x({divRef:e,editable:t,disabled:r,label:i,placeholder:a,dd:o,onPaste:s,onInput:c,onKeyDown:l,onContextMenu:u,onBlur:f,onFocus:p}){let m=n();return d(`div`,{ref:e,id:m?.controlId,onPaste:s,onInput:c,onKeyDown:l,onContextMenu:u,onBlur:f,onFocus:p,spellCheck:!1,role:`textbox`,"aria-label":i?void 0:a||`Телефон`,"aria-describedby":m?.describedBy,"aria-invalid":m?.errorId?!0:void 0,"aria-disabled":!t,tabIndex:r?-1:0,"data-disabled":t?void 0:`true`,contentEditable:t?`plaintext-only`:!1,suppressContentEditableWarning:!0,...o})}b.displayName=`PhoneField`;export{b as default};
|
package/dist/fields/Radio.d.ts
CHANGED
|
@@ -10,12 +10,28 @@ type RadioOptionArrayItem = {
|
|
|
10
10
|
s?: number;
|
|
11
11
|
};
|
|
12
12
|
type RadioOptions = RadioOptionObject | RadioOptionArrayItem[];
|
|
13
|
+
/**
|
|
14
|
+
* Группа радиокнопок.
|
|
15
|
+
*
|
|
16
|
+
* **В форме:** `string | number` (id выбранного пункта).
|
|
17
|
+
*
|
|
18
|
+
* **Элементы:** капсула-`radiogroup` → кнопки `role="radio"`.
|
|
19
|
+
* Опции как у Select: `{ n, s }` / `{ i|id, n }`. `inline` — в ряд. `align` — кружок vs текст.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* <RadioField name="sex" form={form} label="Пол" options={[{ i: "m", n: "М" }, { i: "f", n: "Ж" }]} />
|
|
23
|
+
*/
|
|
13
24
|
export type TRadio = TInput & {
|
|
14
25
|
options?: RadioOptions;
|
|
15
26
|
/** Горизонтальный ряд вместо колонки */
|
|
16
27
|
inline?: boolean;
|
|
28
|
+
/** Кружок: top | center | bottom относительно текста. По умолчанию center */
|
|
29
|
+
align?: "top" | "center" | "bottom";
|
|
17
30
|
};
|
|
18
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Радиогруппа. Один выбранный id в форме.
|
|
33
|
+
*/
|
|
34
|
+
declare function RadioField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, options, inline, align, }: TRadio): import("react/jsx-runtime").JSX.Element | null;
|
|
19
35
|
declare namespace RadioField {
|
|
20
36
|
var displayName: string;
|
|
21
37
|
}
|
package/dist/fields/Radio.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/Radio.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/Radio.module.js";import a from"react";import{jsx as o,jsxs as s}from"react/jsx-runtime";function c(e){return e?Array.isArray(e)?e.map(e=>({id:e.id??e.i??``,label:e.n||``,hidden:e.s===0})).filter(e=>e.label.length>0&&e.id!==``):Object.keys(e).map(t=>{let n=e[t],r=Number(t);return{id:Number.isFinite(r)&&String(r)===t?r:t,label:n?.n||``,hidden:n?.s===0}}).filter(e=>e.label.length>0):[]}function l(e,t){return e===t?!0:String(e)===String(t)}function u({value:u=``,form:d,onValue:f,onBlur:p,name:m=``,placeholder:h=``,label:g=``,error:_=``,hidden:v=!1,disabled:y=!1,className:b=``,after:x=``,before:S=``,options:C,inline:w=!1,align:T=`center`}){let{value:E}=n(d,m,u),D=!y,O=c(C).filter(e=>!e.hidden),k=a.useId(),A=e=>{r(d,m,f)(e,!0)};return v&&(E==null||E===``)?null:s(t,{type:`radio`,label:g,error:_,element_blok:{className:[b||``]},element_input:{className:[i.d,w?i.i:``],props:{"data-disabled":y||!D?`true`:void 0,role:`radiogroup`,"aria-label":g||h||`Выбор`}},children:[S?o(`span`,{className:e.a,children:S}):null,o(`div`,{children:O.map(e=>{let t=l(E,e.id);return s(`button`,{type:`button`,className:i.t,role:`radio`,"aria-checked":t,"aria-disabled":y||!D?!0:void 0,disabled:y||!D,"data-align":T,name:k,onClick:()=>{D&&A(e.id)},onBlur:()=>{!p||!m||p({value:d?d.getValue(m,E):E,name:m})},children:[o(`span`,{className:i.o,"aria-hidden":!0,children:t?o(`span`,{className:i.f}):null}),o(`span`,{className:i.x,children:e.label})]},String(e.id))})}),x?o(`span`,{className:e.a,children:x}):null]})}u.displayName=`RadioField`;export{u as default};
|
package/dist/fields/Range.d.ts
CHANGED
|
@@ -1,16 +1,29 @@
|
|
|
1
1
|
import { TInput } from './type';
|
|
2
|
-
/**
|
|
2
|
+
/** `bar` — толстая «таблетка» с заливкой; `line` — тонкая линия и круглый thumb */
|
|
3
|
+
export type TRangeVariant = "bar" | "line";
|
|
4
|
+
/**
|
|
5
|
+
* Слайдер. **В форме:** `number`.
|
|
6
|
+
*
|
|
7
|
+
* **Элементы:** капсула как у Input → трек (`bar` таблетка / `line` + thumb). Не нативный `<input type="range">`.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* <RangeField name="vol" form={form} label="Громкость" min={0} max={100} variant="bar" />
|
|
11
|
+
*/
|
|
3
12
|
export type TRange = TInput & {
|
|
4
13
|
min?: number;
|
|
5
14
|
max?: number;
|
|
6
15
|
step?: number;
|
|
16
|
+
/**
|
|
17
|
+
* `bar` — iOS-прогресс (заливка слева),
|
|
18
|
+
* `line` — тонкий трек и кружок (как системный range).
|
|
19
|
+
*/
|
|
20
|
+
variant?: TRangeVariant;
|
|
7
21
|
};
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Диапазон. Клавиши стрелок при фокусе на треке.
|
|
24
|
+
*/
|
|
25
|
+
declare function RangeField({ value, form, onValue, onBlur: onBlurField, name, placeholder, label, error, hidden, disabled, className, after, before, min, max, step, variant, }: TRange): import("react/jsx-runtime").JSX.Element | null;
|
|
11
26
|
declare namespace RangeField {
|
|
12
27
|
var displayName: string;
|
|
13
28
|
}
|
|
14
29
|
export default RangeField;
|
|
15
|
-
/** Псевдоним для обратной совместимости */
|
|
16
|
-
export { RangeField as ProgressField };
|
package/dist/fields/Range.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"react";import{jsx as
|
|
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"react";import{jsx as a,jsxs as o}from"react/jsx-runtime";function s(e,t,n){let r=e;return Number.isFinite(r)||(r=t),Math.min(n,Math.max(t,r))}function c(e,t,n,r){return s(!Number.isFinite(r)||r<=0?e:t+Math.round((e-t)/r)*r,t,n)}function l({value:l=0,form:u,onValue:d,onBlur:f,name:p=``,placeholder:m=``,label:h=``,error:g=``,hidden:_=!1,disabled:v=!1,className:y=``,after:b=``,before:x=``,min:S=0,max:C=100,step:w=1,variant:T=`bar`}){let{value:E}=n(u,p,l),D=s(Number(E),S,C),O=C===S?0:(D-S)/(C-S)*100,k=O>=99.999,A=!v,j=i.useRef(null),M=i.useCallback(e=>{let t=c(e,S,C,w);r(u,p,d)(t,!0)},[u,p,d,S,C,w]),N=i.useCallback(e=>{let t=j.current;if(!t)return S;let n=t.getBoundingClientRect(),r=n.width||1;return c(S+(e-n.left)/r*(C-S),S,C,w)},[S,C,w]),P=e=>{A&&(e.preventDefault(),e.currentTarget.setPointerCapture(e.pointerId),M(N(e.clientX)))},F=e=>{A&&e.currentTarget.hasPointerCapture(e.pointerId)&&M(N(e.clientX))},I=e=>{try{e.currentTarget.releasePointerCapture(e.pointerId)}catch{}},L=e=>{if(!A)return;let t=D;if(e.key===`ArrowRight`||e.key===`ArrowUp`)t=D+w;else if(e.key===`ArrowLeft`||e.key===`ArrowDown`)t=D-w;else if(e.key===`Home`)t=S;else if(e.key===`End`)t=C;else if(e.key===`PageUp`)t=D+w*10;else if(e.key===`PageDown`)t=D-w*10;else return;e.preventDefault(),M(c(t,S,C,w))},R=()=>{if(!f||!p)return;let e=u&&p?u.getValue(p,l):D;f({value:s(Number(e),S,C),name:p})};return _&&(E==null||E===``)?null:o(t,{type:`range`,label:h,error:g,element_blok:{className:[y||``]},element_input:{className:[e.w,e.r],props:{"data-disabled":v||!A?`true`:void 0,"data-variant":T}},children:[x?a(`span`,{className:e.a,children:x}):null,a(`div`,{children:o(`div`,{ref:j,className:e.k,role:`slider`,tabIndex:A?0:-1,"aria-label":h||m||`Диапазон`,"aria-valuemin":S,"aria-valuemax":C,"aria-valuenow":D,"data-disabled":v||!A?`true`:void 0,onPointerDown:P,onPointerMove:F,onPointerUp:I,onPointerCancel:I,onKeyDown:L,onBlur:R,children:[a(`div`,{className:k?`${e.u} ${e.v}`:e.u,style:{width:`${O}%`},"aria-hidden":!0}),T===`line`?a(`div`,{className:e.h,style:{left:`${O}%`},"aria-hidden":!0}):null]})}),b?a(`span`,{className:e.a,children:b}):null]})}l.displayName=`RangeField`;export{l as default};
|