@dvcol/neo-svelte
Version:
Neomorphic ui library for svelte 5
420 lines (419 loc) • 14.9 kB
TypeScript
import type { Snippet } from 'svelte';
import type { HTMLInputAttributes, HTMLSelectAttributes, HTMLTextareaAttributes } from 'svelte/elements';
import type { NeoAffixProps } from './neo-affix.model.js';
import type { NeoInputValidationProps } from './neo-input-validation.model.js';
import type { NeoLabelProps } from './neo-label.model.js';
import type { NeoValidationFieldContext, NeoValidationProps, NeoValidationState } from './neo-validation.model.js';
import type { HTMLTransitionProps, HTMLUseProps } from '../../utils/action.utils.js';
import type { BorderRadiusInput } from '../../utils/border.utils.js';
import type { Color } from '../../utils/colors.utils.js';
import type { HTMLNeoBaseElement, HTMLRefProps, HTMLTagProps, SvelteEvent } from '../../utils/html-element.utils.js';
import type { BlurElevation, BlurElevationString, ShadowElevation, ShadowElevationString, ShadowHoverElevation, ShadowHoverElevationsString } from '../../utils/shadow.utils.js';
import type { SizeInput } from '../../utils/style.utils.js';
export type NeoInputValue<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = T extends HTMLTextAreaElement ? HTMLTextareaAttributes['value'] : T extends HTMLSelectElement ? HTMLSelectAttributes['value'] : HTMLInputAttributes['value'] | HTMLInputAttributes['checked'] | HTMLInputAttributes['bind:files'];
type NeoInputHTMLAttributes<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = T extends HTMLTextAreaElement ? HTMLTextareaAttributes : T extends HTMLSelectElement ? HTMLSelectAttributes & {
type?: 'select';
} : HTMLInputAttributes & {
files?: HTMLInputAttributes['bind:files'];
group?: HTMLInputAttributes['bind:group'];
};
export type NeoInputState<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = NeoValidationState<NeoInputValue<T>>;
export interface NeoInputStyles {
/**
* Input elevation.
* @default 3
*/
elevation?: NeoInputElevation;
/**
* Whether to increase/decrease the elevation when hovered/focused.
* @default 0
*/
hover?: NeoInputHoverElevation;
/**
* The blur level to apply when in glass mode.
*
* @default elevation, min: 1, max: 5
* @see glass
*/
blur?: NeoInputBlur;
/**
* If true, negative elevation (< 0) will be displayed as pressed instead of inset.
*/
pressed?: boolean;
/**
* By default, inputs with no elevation will display a border.
* If this is `true`, the input will never display a border.
*/
borderless?: boolean;
/**
* If true, the input will have a rounded border.
*/
rounded?: BorderRadiusInput;
/**
* If true, the input will be displayed with a glass effect.
*/
glass?: boolean;
/**
* Tints the input with the current color.
*/
tinted?: boolean;
/**
* Text color to use for the input.
*/
color?: Color | CSSStyleDeclaration['color'];
/**
* If true, the input input start as flat on first render.
* @see [@starting-style](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) for browser support
*/
start?: boolean;
/**
* If true, the input will be disabled and a loading skeleton will be displayed instead of the text.
*/
skeleton?: boolean;
/**
* If true, the input will be disabled.
*/
disabled?: boolean;
/**
* If true, the input will be readonly.
*/
readonly?: boolean;
}
export interface NeoInputMethods<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> {
/**
* Change the input state. If no value is provided, the state attributes will be unchanged.
* @param state
*/
mark: (state: NeoInputState<T>) => unknown;
/**
* Clear the input. If a state is provided, the input state will be updated accordingly.
* If a partial state is provided, the input state will be reinitialized and the provided state will be merged.
*
* Note: Clearing the input will trigger `onclear` and `oninput` events, but not `onchange`.
* @param state
* @param event
*/
clear: (state?: NeoInputState<T>, event?: InputEvent | SvelteEvent<InputEvent>) => Promise<unknown>;
/**
* Change the input value.
* @param value
* @param event
*/
change: (value: NeoInputValue<T>, event?: InputEvent | SvelteEvent<InputEvent>) => Promise<NeoInputState<T>>;
/**
* Check the input validity.
* @param update whether to check the input dirty and/or valid state.
* @param update.dirty whether to mark the input dirty
* @param update.valid whether to force a valid state
*/
validate: (update?: {
dirty?: boolean;
valid?: boolean;
}) => NeoInputState<T>;
}
export type NeoInputBlur = BlurElevation | BlurElevationString;
export type NeoInputElevation = ShadowElevation | ShadowElevationString;
export type NeoInputHoverElevation = ShadowHoverElevation | ShadowHoverElevationsString;
export type NeoInputContext<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = NeoValidationFieldContext<T, NeoInputValue<T>> & Partial<NeoInputStyles & NeoInputMethods<T>>;
export declare const NeoInputLabelPlacement: {
readonly Inside: "inside";
readonly Top: "top";
readonly Left: "left";
readonly Right: "right";
};
export type NeoInputLabelPlacements = (typeof NeoInputLabelPlacement)[keyof typeof NeoInputLabelPlacement];
export type NeoBaseInputProps<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement = NeoInputHTMLElement> = {
/**
* A snippet to display as the input children.
*/
children?: Snippet;
/**
* A snippet to display as the input display value.
*
* @note: This will hide the input (hidden & display: none) and might impact accessibility or form submission.
*/
display?: Snippet<[NeoInputState<T>]> | string;
/**
* Will apply a display: none style to the input.
*/
hide?: boolean;
/**
* If `true`, the input will have no left padding/border radius.
*/
after?: boolean;
/**
* If `true`, the input will have no right padding/border radius.
*/
before?: boolean;
/**
* If the input is currently focused.
*/
focused?: boolean;
/**
* If false, and a default value is provided, the input will be set to the default value when cleared.
*
* Note: This does not apply to checkboxes, radios, and file inputs.
*
* @default true
*/
nullable?: boolean;
/**
* Fall back value when the input value is cleared.
*/
defaultValue?: HTMLInputAttributes['value'];
/**
* Fall back checked state when the input value is cleared.
*/
defaultChecked?: HTMLInputAttributes['checked'];
/**
* Optional flex strategy for the container
*/
flex?: CSSStyleDeclaration['flex'];
/**
* Optional width constraints.
*/
width?: SizeInput<'width'>;
/**
* Optional height constraints.
*/
height?: SizeInput<'height'>;
/**
* If true, the input will adjust its size to fit the content.
*
* @see [field-sizing](https://developer.mozilla.org/en-US/docs/Web/CSS/field-sizing) for browser support
*/
fitContent?: boolean;
/**
* If `true`, the input dirty state will update on input events.
* If `false`, the input dirty state will only update on change events.
* @default false
*/
dirtyOnInput?: boolean;
/**
* If `true`, the input dirty state will update on blur events.
* @default false
*/
dirtyOnBlur?: boolean;
/**
* If `true`, the input will check for validity on input events.
* If `false`, the input will only check for validity on change events.
* @default false
*/
validateOnInput?: boolean;
/**
* If `true`, the input will check for validity on blur events.
* @default false
*/
validateOnBlur?: boolean;
/**
* Reflect the input validation message.
*/
validationMessage?: T['validationMessage'];
/**
* Callback when the input state is manually changed.
* @param state
*/
onmark?: (state: NeoInputState<T>) => unknown;
/**
* Callback when the input is cleared.
* @param state
* @param event
*/
onclear?: (state: NeoInputState<T>, event?: InputEvent) => unknown;
/**
* The props to pass to the input display wrapper.
*/
displayProps?: HTMLNeoBaseElement & HTMLTagProps;
} & HTMLUseProps & HTMLRefProps<T> & NeoInputState<T> & NeoInputHTMLAttributes<T>;
export type NeoInputGroupProps<T extends HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement> = {
/**
* A snippet or a string to display as the input label.
*/
label?: Snippet<[NeoInputContext<T>]> | string;
/**
* A snippet or a string to display as the input info message.
*/
message?: Snippet<[NeoInputContext<T>]> | string;
/**
* A snippet or a string to display as the input error message.
*/
error?: Snippet<[NeoInputContext<T>]> | string;
/**
* A snippet to display as the input suffix.
*/
after?: Snippet<[NeoInputContext<T>]>;
/**
* Element(s) to render inside the input-group.
*/
children?: Snippet<[NeoInputContext<T>]>;
/**
* Display a loading spinner inside the input.
* If defined, some space will be reserved for the spinner.
* Set to `undefined` when not loading to regain the space.
*/
loading?: boolean;
/**
* If the input is currently hovered.
*/
hovered?: boolean;
/**
* Whether the input group has focus (input, affixes or label).
*/
focusin?: boolean;
/**
* Display a clear button to reset the input value.
* If used in combination with `loading`, the clear button will be hidden while loading.
* @default false
*/
clearable?: boolean;
/**
* If `true`, the input will display validation states.
* @default false
*/
validation?: NeoInputValidationProps<T>['validation'];
/**
* If `true`, and `validation` is enabled, the validation state will be displayed with an icon.
*/
validationIcon?: boolean;
/**
* If false, the input will not be registered with the form context.
*/
register?: NeoInputValidationProps<T>['register'];
/**
* If true, the cursor will be set to pointer when hovering the input.
*/
clickable?: boolean;
/**
* Display the label as a placeholder inside the input when empty
* @default true
*/
floating?: boolean;
/**
* Label placement.
* When set to outside (`top`, `left`, `right`), the label will be displayed within the input container's margin.
* Make sure to set the container's margin appropriately to avoid collision or content shift.
*
* @default 'inside'
*/
placement?: NeoInputLabelPlacements;
/**
* The ref to bind to the input wrapper (validation).
*/
validationRef?: HTMLRefProps['ref'];
/**
* The props to pass to the validation component.
*/
validationProps?: NeoInputValidationProps<T>;
/**
* The ref to bind to the input container.
*/
containerRef?: HTMLRefProps['ref'];
/**
* The props to pass to the input container.
*/
containerProps?: HTMLNeoBaseElement & HTMLTagProps;
/**
* The props to pass to the message and error components.
*/
messageProps?: NeoValidationProps<T>['messageProps'];
/**
* The props to pass to the suffix.
*/
afterProps?: HTMLNeoBaseElement & HTMLTagProps;
/**
* The ref to bind to the suffix.
*/
afterRef?: HTMLElement;
/**
* The props to pass to the affix.
*/
affixProps?: NeoAffixProps;
/**
* The ref to bind to the affix.
*/
affixRef?: HTMLElement;
/**
* The props to pass to the label.
*/
labelProps?: NeoLabelProps;
/**
* The ref to bind to the label.
*/
labelRef?: HTMLLabelElement;
} & Omit<NeoBaseInputProps<T>, 'after' | 'before'> & NeoInputStyles & HTMLTransitionProps;
export type NeoInputProps<T extends HTMLInputElement | HTMLSelectElement = NeoInputHTMLElement> = {
/**
* A snippet to display as the input prefix.
*/
before?: Snippet<[NeoInputContext<T>]>;
/**
* A snippet to display inside the input group but outside the input.
* This is meant for internal use only. Elements will not be styled as affixes.
*
* Use `before` or `after` for custom affixes.
*
* @internal
*/
inner?: Snippet<[NeoInputContext<T>]>;
/**
* Unique identifier of the input.
* @default neo-input-{uuid}
*/
id?: HTMLElement['id'];
/**
* The props to pass to the prefix.
*/
beforeProps?: HTMLNeoBaseElement & HTMLTagProps;
/**
* The ref to bind to the prefix.
*/
beforeRef?: HTMLElement;
} & NeoInputGroupProps<T> & HTMLInputAttributes & {
files?: HTMLInputAttributes['bind:files'];
group?: HTMLInputAttributes['bind:group'];
};
export interface NeoTextAreaResize {
/**
* The minimum number of rows the textarea can have.
* @default 1
*/
min?: number;
/**
* The maximum number of rows the textarea can have.
*/
max?: number;
}
export type NeoTextareaProps<T extends HTMLTextAreaElement = NeoTextareaHTMLElement> = {
/**
* Fall back value when the textarea value is cleared.
*/
defaultValue?: HTMLTextareaAttributes['value'];
/**
* Overrides the default scrollbars.
*/
scrollbar?: boolean;
/**
* Unique identifier of the textarea.
* @default neo-textarea-{uuid}
*/
id?: HTMLElement['id'];
/**
* The resize strategy of the textarea.
*/
resize?: CSSStyleDeclaration['resize'];
/**
* Automatically increments/decrements the textarea rows to fit the content.
*
* If `true`, the textarea will increment indefinitely and will not decrement.
* If an object is provided, the textarea will increment up to `max` rows and decrement to `min` rows.
*
* @default true
*/
autoResize?: boolean | NeoTextAreaResize;
} & Omit<NeoInputGroupProps<T>, 'hide' | 'display' | 'displayProps'> & HTMLTextareaAttributes;
export type NeoInputHTMLElement<T extends HTMLInputElement = HTMLInputElement> = T & Partial<NeoInputMethods<T>>;
export type NeoSelectHTMLElement<T extends HTMLSelectElement = HTMLSelectElement> = T & Partial<NeoInputMethods<T>>;
export type NeoTextareaHTMLElement<T extends HTMLTextAreaElement = HTMLTextAreaElement> = T & Partial<NeoInputMethods<T>> & {
resize?: () => void;
};
export {};