@bitrise/bitkit-v2 0.3.497-beta.3187 → 0.3.498-beta.3202
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/AGENTS.md +13 -1
- package/dist/components/BitkitSearchInput/BitkitSearchInput.d.ts +2 -0
- package/dist/components/BitkitSearchInput/BitkitSearchInput.js +2 -1
- package/dist/components/BitkitSearchInput/BitkitSearchInput.js.map +1 -1
- package/dist/components/BitkitTextInput/BitkitTextInput.d.ts +8 -3
- package/dist/components/BitkitTextInput/BitkitTextInput.js +6 -3
- package/dist/components/BitkitTextInput/BitkitTextInput.js.map +1 -1
- package/package.json +4 -1
package/AGENTS.md
CHANGED
|
@@ -4,6 +4,18 @@ Conventions for **using** `@bitrise/bitkit-v2` in your own application code. Bui
|
|
|
4
4
|
|
|
5
5
|
If you're contributing to the library itself, see `CLAUDE.md` instead — this file only covers consumer-side usage.
|
|
6
6
|
|
|
7
|
+
## Top rules
|
|
8
|
+
|
|
9
|
+
The mistakes that break things silently or come up in almost every review. Each is detailed further down; read the whole file before you write code.
|
|
10
|
+
|
|
11
|
+
1. **Only use props and tokens that exist.** Check them in the Storybook MCP. An unknown token, or a dot-form semantic token like `text.secondary`, is dropped without an error. → _Before you start_, _Coding rules_
|
|
12
|
+
2. **Don't rebuild what Bitkit has**, and never use Chakra's own `Button`, `Input`, `Dialog` and the like. → _Building UI_
|
|
13
|
+
3. **Don't override a Bitkit component's styling**, and don't pass an empty or placeholder value to satisfy a required prop. Layout props (`width`, `flex`, `margin*`, …) are fine. → _Building UI_
|
|
14
|
+
4. **Never wrap a Bitkit component in `<Box>` for styling.** Put the style props on the component. → _Coding rules_
|
|
15
|
+
5. **Only semantic colors.** No palette colors, `sys/*` colors, hex codes or color names. → _Coding rules_
|
|
16
|
+
6. **Sizes are tokens.** For a content width use a `layout/maxWidth/*` token; anything else above 128 uses `rem()`. A bare-number string like `"420"` is dropped. → _Coding rules_
|
|
17
|
+
7. **Omit default prop values.** → _Coding rules_
|
|
18
|
+
|
|
7
19
|
## Before you start (instructions for the AI assistant)
|
|
8
20
|
|
|
9
21
|
**AI assistants: using the `bitkit-storybook-mcp` MCP server is mandatory before generating Bitkit code.** Memory, naming intuition, and TypeScript types are all secondary sources — hallucinated props are the #1 failure mode of AI-generated Bitkit code. Code generated with the MCP is much more accurate (live prop tables, real variants, real tokens, real examples) and faster (one structured query vs. crawling node_modules) than code generated without it.
|
|
@@ -222,7 +234,7 @@ Bitkit components use consistent prop names across the library:
|
|
|
222
234
|
- **A surface that stays dark in both color modes** (logs, terminal output) — `background/fixed-contrast` with `text/on-color` (and `color/white/*` for dimmer text). `background/contrast` turns light in dark mode, so the white text on it disappears. Don't reach for a palette color instead.
|
|
223
235
|
- **Always use `textStyle`** — never set `fontFamily` or `fontSize` on `<Text>`. Use `textStyle="body/md/regular"`, `textStyle="code/md"`, etc.
|
|
224
236
|
- **Sizes as strings** — `width="32"`, not `width={32}`. Tokens are strings.
|
|
225
|
-
- **Use existing size tokens** — closest existing token (1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128) representing pixel values (1=1px, 32=32px). Avoid arbitrary px (`width="100px"`). Above 128
|
|
237
|
+
- **Use existing size tokens** — closest existing token (1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128) representing pixel values (1=1px, 32=32px). Avoid arbitrary px (`width="100px"`). Above 128: for a content width use a `layout/maxWidth/*` token (`640`, `960`, `1280`, `1616`; e.g. `maxWidth="layout/maxWidth/960"`, sizes only, not spacing); for anything else use Bitkit's `rem()` helper (`import { rem } from '@bitrise/bitkit-v2'`, `maxWidth={rem(800)}`), never a bare-number string — `maxWidth="800"` is not a token, reaches the CSS without a unit, and the browser silently drops it.
|
|
226
238
|
- **Omit default prop values** — `<IconCross />`, not `<IconCross size="24" />` when 24 is the default.
|
|
227
239
|
- **Never pass `color` to a colored icon** — multi/fixed-color icons (`Icon*Color`, `IconWarningYellow`, `Icon*White`, etc.) have their colors baked in. They omit `color` from their props, so passing it is a type error. Only monochrome icons follow `color` (via `currentColor`).
|
|
228
240
|
- **No shorthand props** — use full names: `marginInline`, `paddingBlockEnd`, `width`, `background`, `padding`. Never `mx`, `pb`, `w`, `bg`, `p`, `mt`.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { BitkitTextInputProps } from '../BitkitTextInput/BitkitTextInput';
|
|
2
2
|
export interface BitkitSearchInputProps extends Omit<BitkitTextInputProps, 'badge' | 'counter' | 'counterText' | 'endAddon' | 'endAddonProps' | 'endElement' | 'errorText' | 'inputProps' | 'maxLength' | 'onChange' | 'startAddon' | 'startAddonProps' | 'startElement' | 'state' | 'tooltip' | 'warningText'> {
|
|
3
|
+
/** Focuses the `<input>` on mount. */
|
|
4
|
+
autoFocus?: boolean;
|
|
3
5
|
onChange?: (value: string) => void;
|
|
4
6
|
state?: 'disabled';
|
|
5
7
|
value?: string;
|
|
@@ -5,7 +5,7 @@ import { forwardRef } from "react";
|
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
//#region lib/components/BitkitSearchInput/BitkitSearchInput.tsx
|
|
7
7
|
var BitkitSearchInput = forwardRef((props, ref) => {
|
|
8
|
-
const { label = "Search", labelHidden = true, onChange, optional = true, placeholder, required = false, size = "lg", state, value, ...rest } = props;
|
|
8
|
+
const { autoFocus, label = "Search", labelHidden = true, onChange, optional = true, placeholder, required = false, size = "lg", state, value, ...rest } = props;
|
|
9
9
|
const showClearButton = Boolean(value) && state !== "disabled";
|
|
10
10
|
const iconSize = size === "lg" ? "24" : "16";
|
|
11
11
|
const closeButtonSize = size === "lg" ? "md" : "sm";
|
|
@@ -13,6 +13,7 @@ var BitkitSearchInput = forwardRef((props, ref) => {
|
|
|
13
13
|
ref,
|
|
14
14
|
...rest,
|
|
15
15
|
inputProps: {
|
|
16
|
+
autoFocus,
|
|
16
17
|
onChange: (e) => onChange?.(e.target.value),
|
|
17
18
|
type: "search",
|
|
18
19
|
value
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitSearchInput.js","names":[],"sources":["../../../lib/components/BitkitSearchInput/BitkitSearchInput.tsx"],"sourcesContent":["import { forwardRef } from 'react';\n\nimport { IconMagnifier } from '../../icons';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitTextInput, { type BitkitTextInputProps } from '../BitkitTextInput/BitkitTextInput';\n\nexport interface BitkitSearchInputProps extends Omit<\n BitkitTextInputProps,\n | 'badge'\n | 'counter'\n | 'counterText'\n | 'endAddon'\n | 'endAddonProps'\n | 'endElement'\n | 'errorText'\n | 'inputProps'\n | 'maxLength'\n | 'onChange'\n | 'startAddon'\n | 'startAddonProps'\n | 'startElement'\n | 'state'\n | 'tooltip'\n | 'warningText'\n> {\n onChange?: (value: string) => void;\n state?: 'disabled';\n value?: string;\n}\n\nconst BitkitSearchInput = forwardRef<HTMLDivElement, BitkitSearchInputProps>((props, ref) => {\n // A search/filter field should never be a required form control, so default it out of\n // BitkitField's `required: !optional` behaviour — an empty box was silently blocking form submit.\n const {\n label = 'Search',\n labelHidden = true,\n onChange,\n optional = true,\n placeholder,\n required = false,\n size = 'lg',\n state,\n value,\n ...rest\n } = props;\n\n const showClearButton = Boolean(value) && state !== 'disabled';\n const iconSize = size === 'lg' ? '24' : '16';\n const closeButtonSize = size === 'lg' ? 'md' : 'sm';\n\n return (\n <BitkitTextInput\n ref={ref}\n {...rest}\n inputProps={{\n onChange: (e) => onChange?.(e.target.value),\n type: 'search',\n value,\n }}\n label={label}\n labelHidden={labelHidden}\n optional={optional}\n placeholder={placeholder}\n required={required}\n size={size}\n startElement={<IconMagnifier size={iconSize} />}\n state={state}\n endElement={\n showClearButton ? (\n <BitkitCloseButton\n aria-label=\"Clear\"\n colorVariant=\"neutral\"\n onClick={() => onChange?.('')}\n size={closeButtonSize}\n />\n ) : undefined\n }\n />\n );\n});\n\nBitkitSearchInput.displayName = 'BitkitSearchInput';\n\nexport default BitkitSearchInput;\n"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"BitkitSearchInput.js","names":[],"sources":["../../../lib/components/BitkitSearchInput/BitkitSearchInput.tsx"],"sourcesContent":["import { forwardRef } from 'react';\n\nimport { IconMagnifier } from '../../icons';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport BitkitTextInput, { type BitkitTextInputProps } from '../BitkitTextInput/BitkitTextInput';\n\nexport interface BitkitSearchInputProps extends Omit<\n BitkitTextInputProps,\n | 'badge'\n | 'counter'\n | 'counterText'\n | 'endAddon'\n | 'endAddonProps'\n | 'endElement'\n | 'errorText'\n | 'inputProps'\n | 'maxLength'\n | 'onChange'\n | 'startAddon'\n | 'startAddonProps'\n | 'startElement'\n | 'state'\n | 'tooltip'\n | 'warningText'\n> {\n /** Focuses the `<input>` on mount. */\n autoFocus?: boolean;\n onChange?: (value: string) => void;\n state?: 'disabled';\n value?: string;\n}\n\nconst BitkitSearchInput = forwardRef<HTMLDivElement, BitkitSearchInputProps>((props, ref) => {\n // A search/filter field should never be a required form control, so default it out of\n // BitkitField's `required: !optional` behaviour — an empty box was silently blocking form submit.\n const {\n autoFocus,\n label = 'Search',\n labelHidden = true,\n onChange,\n optional = true,\n placeholder,\n required = false,\n size = 'lg',\n state,\n value,\n ...rest\n } = props;\n\n const showClearButton = Boolean(value) && state !== 'disabled';\n const iconSize = size === 'lg' ? '24' : '16';\n const closeButtonSize = size === 'lg' ? 'md' : 'sm';\n\n return (\n <BitkitTextInput\n ref={ref}\n {...rest}\n inputProps={{\n autoFocus,\n onChange: (e) => onChange?.(e.target.value),\n type: 'search',\n value,\n }}\n label={label}\n labelHidden={labelHidden}\n optional={optional}\n placeholder={placeholder}\n required={required}\n size={size}\n startElement={<IconMagnifier size={iconSize} />}\n state={state}\n endElement={\n showClearButton ? (\n <BitkitCloseButton\n aria-label=\"Clear\"\n colorVariant=\"neutral\"\n onClick={() => onChange?.('')}\n size={closeButtonSize}\n />\n ) : undefined\n }\n />\n );\n});\n\nBitkitSearchInput.displayName = 'BitkitSearchInput';\n\nexport default BitkitSearchInput;\n"],"mappings":";;;;;;AAgCA,IAAM,oBAAoB,YAAoD,OAAO,QAAQ;CAG3F,MAAM,EACJ,WACA,QAAQ,UACR,cAAc,MACd,UACA,WAAW,MACX,aACA,WAAW,OACX,OAAO,MACP,OACA,OACA,GAAG,SACD;CAEJ,MAAM,kBAAkB,QAAQ,KAAK,KAAK,UAAU;CACpD,MAAM,WAAW,SAAS,OAAO,OAAO;CACxC,MAAM,kBAAkB,SAAS,OAAO,OAAO;CAE/C,OACE,oBAAC,iBAAD;EACO;EACL,GAAI;EACJ,YAAY;GACV;GACA,WAAW,MAAM,WAAW,EAAE,OAAO,KAAK;GAC1C,MAAM;GACN;EACF;EACO;EACM;EACH;EACG;EACH;EACJ;EACN,cAAc,oBAAC,eAAD,EAAe,MAAM,SAAW,CAAA;EACvC;EACP,YACE,kBACE,oBAAC,mBAAD;GACE,cAAW;GACX,cAAa;GACb,eAAe,WAAW,EAAE;GAC5B,MAAM;EACP,CAAA,IACC,KAAA;CAEP,CAAA;AAEL,CAAC;AAED,kBAAkB,cAAc"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { InputProps } from '@chakra-ui/react/input';
|
|
2
2
|
import { InputAddonProps } from '@chakra-ui/react/input-addon';
|
|
3
|
-
import { ChangeEventHandler, ReactNode } from 'react';
|
|
3
|
+
import { ChangeEventHandler, ReactNode, Ref } from 'react';
|
|
4
4
|
import { BitkitFieldProps } from '../BitkitField/BitkitField';
|
|
5
5
|
export interface BitkitTextInputProps extends Omit<BitkitFieldProps, 'children' | 'onChange' | 'state' | 'value'> {
|
|
6
6
|
/**
|
|
@@ -20,9 +20,14 @@ export interface BitkitTextInputProps extends Omit<BitkitFieldProps, 'children'
|
|
|
20
20
|
endElement?: ReactNode;
|
|
21
21
|
/**
|
|
22
22
|
* Escape hatch for the underlying Chakra `Input` — extra native attributes or a spread
|
|
23
|
-
* `{...register()}`. On conflict, values here win over the top-level `value`/`onChange`.
|
|
23
|
+
* `{...register()}`. On conflict, values here win over the top-level `value`/`onChange`. A `ref`
|
|
24
|
+
* here and `inputRef` both receive the `<input>`.
|
|
24
25
|
*/
|
|
25
|
-
inputProps?: InputProps
|
|
26
|
+
inputProps?: InputProps & {
|
|
27
|
+
ref?: Ref<HTMLInputElement>;
|
|
28
|
+
};
|
|
29
|
+
/** Ref to the `<input>` itself, for focus management and form libraries. `ref` stays on the field root. */
|
|
30
|
+
inputRef?: Ref<HTMLInputElement>;
|
|
26
31
|
/** Maximum number of characters accepted; also feeds the optional `counter`. */
|
|
27
32
|
maxLength?: HTMLInputElement['maxLength'];
|
|
28
33
|
/**
|
|
@@ -2,9 +2,10 @@ import IconErrorCircleFilled from "../../icons/IconErrorCircleFilled.js";
|
|
|
2
2
|
import IconWarningYellow from "../../icons/IconWarningYellow.js";
|
|
3
3
|
import BitkitField from "../BitkitField/BitkitField.js";
|
|
4
4
|
import { useSlotRecipe } from "@chakra-ui/react/styled-system";
|
|
5
|
-
import { forwardRef } from "react";
|
|
5
|
+
import { forwardRef, useMemo } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { Box } from "@chakra-ui/react/box";
|
|
8
|
+
import { mergeRefs } from "@chakra-ui/react";
|
|
8
9
|
import { Group } from "@chakra-ui/react/group";
|
|
9
10
|
import { Input } from "@chakra-ui/react/input";
|
|
10
11
|
import { InputAddon } from "@chakra-ui/react/input-addon";
|
|
@@ -17,7 +18,7 @@ import { InputAddon } from "@chakra-ui/react/input-addon";
|
|
|
17
18
|
* inline inside it, and `counter` pairs with `maxLength`.
|
|
18
19
|
*/
|
|
19
20
|
var BitkitTextInput = forwardRef((props, ref) => {
|
|
20
|
-
const { counter, endAddon, endAddonProps, endElement, inputProps, maxLength, onChange, onValueChange, placeholder, size = "lg", startAddon, startAddonProps, startElement, state, value, ...rest } = props;
|
|
21
|
+
const { counter, endAddon, endAddonProps, endElement, inputProps, inputRef, maxLength, onChange, onValueChange, placeholder, size = "lg", startAddon, startAddonProps, startElement, state, value, ...rest } = props;
|
|
21
22
|
const styles = useSlotRecipe({ key: "textInput" })({ size });
|
|
22
23
|
const iconSize = size === "md" ? "16" : "24";
|
|
23
24
|
const addonSize = size === "md" ? "md" : "lg";
|
|
@@ -29,7 +30,8 @@ var BitkitTextInput = forwardRef((props, ref) => {
|
|
|
29
30
|
size: iconSize
|
|
30
31
|
});
|
|
31
32
|
else if (hasWarning) statusIcon = /* @__PURE__ */ jsx(IconWarningYellow, { size: iconSize });
|
|
32
|
-
const { css: inputCss, onChange: inputOnChange, value: inputValue, ...restInputProps } = inputProps ?? {};
|
|
33
|
+
const { css: inputCss, onChange: inputOnChange, ref: inputPropsRef, value: inputValue, ...restInputProps } = inputProps ?? {};
|
|
34
|
+
const setInputRef = useMemo(() => mergeRefs(inputRef, inputPropsRef), [inputRef, inputPropsRef]);
|
|
33
35
|
const resolvedValue = inputValue ?? value;
|
|
34
36
|
const nativeOnChange = inputOnChange ?? onChange;
|
|
35
37
|
const handleChange = onValueChange || nativeOnChange ? (event) => {
|
|
@@ -77,6 +79,7 @@ var BitkitTextInput = forwardRef((props, ref) => {
|
|
|
77
79
|
maxLength,
|
|
78
80
|
onChange: handleChange,
|
|
79
81
|
placeholder,
|
|
82
|
+
ref: setInputRef,
|
|
80
83
|
value: resolvedValue,
|
|
81
84
|
...restInputProps,
|
|
82
85
|
unstyled: true
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BitkitTextInput.js","names":[],"sources":["../../../lib/components/BitkitTextInput/BitkitTextInput.tsx"],"sourcesContent":["import { Box } from '@chakra-ui/react/box';\nimport { Group } from '@chakra-ui/react/group';\nimport { Input, type InputProps } from '@chakra-ui/react/input';\nimport { InputAddon, type InputAddonProps } from '@chakra-ui/react/input-addon';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { type ChangeEventHandler, forwardRef, type ReactNode } from 'react';\n\nimport { IconErrorCircleFilled, IconWarningYellow } from '../../icons';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\n\nexport interface BitkitTextInputProps extends Omit<BitkitFieldProps, 'children' | 'onChange' | 'state' | 'value'> {\n /**\n * Show a live character counter (`length/maxLength`) next to the label. Requires `maxLength` and a\n * known `value`.\n */\n counter?: boolean;\n /**\n * Content attached to the trailing edge, outside the input border. A bare string or number\n * renders in the standard bordered addon box (a unit label like `USD`); any other node renders\n * as itself — see `startAddon`.\n */\n endAddon?: ReactNode;\n /** Props forwarded to the trailing `InputAddon` box. Inert unless the addon is a string. */\n endAddonProps?: InputAddonProps;\n /** Inline content rendered inside the input, at the trailing edge (e.g. an icon or reveal button). */\n endElement?: ReactNode;\n /**\n * Escape hatch for the underlying Chakra `Input` — extra native attributes or a spread\n * `{...register()}`. On conflict, values here win over the top-level `value`/`onChange`.\n */\n inputProps?: InputProps;\n /** Maximum number of characters accepted; also feeds the optional `counter`. */\n maxLength?: HTMLInputElement['maxLength'];\n /**\n * Native change event — the same handler you could put on `inputProps`, promoted to the top level.\n * Reach for `onValueChange` when you only need the value. If both this and `inputProps.onChange` are\n * set, `inputProps.onChange` wins (it's the same event, so they shouldn't both be set).\n */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Convenience change handler that receives the value directly, without `event.target.value`. */\n onValueChange?: (value: string) => void;\n /** Placeholder text shown while the field is empty. */\n placeholder?: string;\n /** Field size — controls height and typography. */\n size?: 'md' | 'lg';\n /**\n * Content attached to the leading edge, outside the input border. Text has to arrive as a bare\n * string (or number) to get the standard bordered addon box (a unit label like `$`) — wrapped in\n * a `<Text>` or a fragment it counts as a component, which loses the box and leaves\n * `startAddonProps` inert. Any other node keeps its own styling and is welded to the field\n * instead: it shares the field's border and the corners it touches are squared automatically, so\n * a `BitkitButton` / `BitkitIconButton` needs only its own `variant` and a `size` matching the\n * field's — the field's `size` does not reach it.\n */\n startAddon?: ReactNode;\n /** Props forwarded to the leading `InputAddon` box. Inert unless the addon is a string. */\n startAddonProps?: InputAddonProps;\n /** Inline content rendered inside the input, at the leading edge (e.g. an icon). */\n startElement?: ReactNode;\n /**\n * Field state. `error`/`warning` are also implied by `errorText`/`warningText`; `disabled` and\n * `readOnly` gate interaction.\n */\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n /**\n * Controlled value. Pair with `onValueChange` or `onChange`. Can also be supplied via `inputProps`,\n * which wins on conflict.\n */\n value?: string;\n}\n\n/**\n * A single-line text field wrapping the native `<input>`, with a label and helper/error/warning\n * text. Reflect the value with `value` + `onValueChange` (or the native `onChange`); `inputProps` is\n * the escape hatch for any other native attribute. `startAddon`/`endAddon` sit outside the border\n * (as a bordered box for text, welded to the field for a component), `startElement`/`endElement`\n * inline inside it, and `counter` pairs with `maxLength`.\n */\nconst BitkitTextInput = forwardRef<HTMLDivElement, BitkitTextInputProps>((props, ref) => {\n const {\n counter,\n endAddon,\n endAddonProps,\n endElement,\n inputProps,\n maxLength,\n onChange,\n onValueChange,\n placeholder,\n size = 'lg',\n startAddon,\n startAddonProps,\n startElement,\n state,\n value,\n ...rest\n } = props;\n\n const recipe = useSlotRecipe({ key: 'textInput' });\n const styles = recipe({ size });\n\n const iconSize = size === 'md' ? '16' : '24';\n const addonSize = size === 'md' ? 'md' : 'lg';\n\n const hasWarning = state === 'warning' || !!rest.warningText;\n const isInvalid = state === 'error' || !!rest.errorText;\n\n let statusIcon: ReactNode = null;\n if (isInvalid) {\n statusIcon = <IconErrorCircleFilled color=\"icon/negative\" size={iconSize} />;\n } else if (hasWarning) {\n statusIcon = <IconWarningYellow size={iconSize} />;\n }\n\n // `value` / `onChange` may come in at the top level or via `inputProps` — the object wins on\n // conflict (natural for a spread `{...register()}`). `onValueChange` always fires with the value.\n const { css: inputCss, onChange: inputOnChange, value: inputValue, ...restInputProps } = inputProps ?? {};\n const resolvedValue = inputValue ?? value;\n const nativeOnChange = inputOnChange ?? onChange;\n const handleChange: ChangeEventHandler<HTMLInputElement> | undefined =\n onValueChange || nativeOnChange\n ? (event) => {\n onValueChange?.(event.target.value);\n nativeOnChange?.(event);\n }\n : undefined;\n\n const counterText =\n counter && maxLength && resolvedValue !== undefined ? `${resolvedValue.toString().length}/${maxLength}` : undefined;\n\n const stateAttrs = {\n 'data-disabled': state === 'disabled' ? '' : undefined,\n 'data-invalid': isInvalid ? '' : undefined,\n 'data-readonly': state === 'readOnly' ? '' : undefined,\n };\n\n const renderAddon = (addon: ReactNode, position: 'end' | 'start', addonProps?: InputAddonProps) =>\n typeof addon === 'string' || typeof addon === 'number' ? (\n <InputAddon position={position} size={addonSize} {...stateAttrs} {...addonProps}>\n {addon}\n </InputAddon>\n ) : (\n addon\n );\n\n return (\n <BitkitField counterText={counterText} ref={ref} state={state} {...rest}>\n <Group attached width=\"100%\">\n {startAddon != null && renderAddon(startAddon, 'start', startAddonProps)}\n <Box css={styles.field} data-has-end-element={endElement != null ? '' : undefined} flex=\"1\" {...stateAttrs}>\n {/* A label around the leading element and the input, so a click on e.g. an icon focuses the input.\n The end element stays outside, as it may be a control. `contents` keeps the field's layout. */}\n <Box as=\"label\" display=\"contents\">\n {startElement != null && (\n <Box css={styles.element} {...stateAttrs}>\n {startElement}\n </Box>\n )}\n <Input\n css={[styles.input, inputCss]}\n maxLength={maxLength}\n onChange={handleChange}\n placeholder={placeholder}\n value={resolvedValue}\n {...restInputProps}\n unstyled\n />\n </Box>\n {statusIcon != null && (\n <Box css={styles.element} {...stateAttrs}>\n {statusIcon}\n </Box>\n )}\n {endElement != null && (\n <Box css={styles.element} {...stateAttrs}>\n {endElement}\n </Box>\n )}\n </Box>\n {endAddon != null && renderAddon(endAddon, 'end', endAddonProps)}\n </Group>\n </BitkitField>\n );\n});\n\nBitkitTextInput.displayName = 'BitkitTextInput';\n\nexport default BitkitTextInput;\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8EA,IAAM,kBAAkB,YAAkD,OAAO,QAAQ;CACvF,MAAM,EACJ,SACA,UACA,eACA,YACA,YACA,WACA,UACA,eACA,aACA,OAAO,MACP,YACA,iBACA,cACA,OACA,OACA,GAAG,SACD;CAGJ,MAAM,SADS,cAAc,EAAE,KAAK,YAAY,CACjC,CAAA,CAAO,EAAE,KAAK,CAAC;CAE9B,MAAM,WAAW,SAAS,OAAO,OAAO;CACxC,MAAM,YAAY,SAAS,OAAO,OAAO;CAEzC,MAAM,aAAa,UAAU,aAAa,CAAC,CAAC,KAAK;CACjD,MAAM,YAAY,UAAU,WAAW,CAAC,CAAC,KAAK;CAE9C,IAAI,aAAwB;CAC5B,IAAI,WACF,aAAa,oBAAC,uBAAD;EAAuB,OAAM;EAAgB,MAAM;CAAW,CAAA;MACtE,IAAI,YACT,aAAa,oBAAC,mBAAD,EAAmB,MAAM,SAAW,CAAA;CAKnD,MAAM,EAAE,KAAK,UAAU,UAAU,eAAe,OAAO,YAAY,GAAG,mBAAmB,cAAc,CAAC;CACxG,MAAM,gBAAgB,cAAc;CACpC,MAAM,iBAAiB,iBAAiB;CACxC,MAAM,eACJ,iBAAiB,kBACZ,UAAU;EACT,gBAAgB,MAAM,OAAO,KAAK;EAClC,iBAAiB,KAAK;CACxB,IACA,KAAA;CAEN,MAAM,cACJ,WAAW,aAAa,kBAAkB,KAAA,IAAY,GAAG,cAAc,SAAS,CAAC,CAAC,OAAO,GAAG,cAAc,KAAA;CAE5G,MAAM,aAAa;EACjB,iBAAiB,UAAU,aAAa,KAAK,KAAA;EAC7C,gBAAgB,YAAY,KAAK,KAAA;EACjC,iBAAiB,UAAU,aAAa,KAAK,KAAA;CAC/C;CAEA,MAAM,eAAe,OAAkB,UAA2B,eAChE,OAAO,UAAU,YAAY,OAAO,UAAU,WAC5C,oBAAC,YAAD;EAAsB;EAAU,MAAM;EAAW,GAAI;EAAY,GAAI;EAClE,UAAA;CACS,CAAA,IAEZ;CAGJ,OACE,oBAAC,aAAD;EAA0B;EAAkB;EAAY;EAAO,GAAI;EACjE,UAAA,qBAAC,OAAD;GAAO,UAAA;GAAS,OAAM;GAAtB,UAAA;IACG,cAAc,QAAQ,YAAY,YAAY,SAAS,eAAe;IACvE,qBAAC,KAAD;KAAK,KAAK,OAAO;KAAO,wBAAsB,cAAc,OAAO,KAAK,KAAA;KAAW,MAAK;KAAI,GAAI;KAAhG,UAAA;MAGE,qBAAC,KAAD;OAAK,IAAG;OAAQ,SAAQ;OAAxB,UAAA,CACG,gBAAgB,QACf,oBAAC,KAAD;QAAK,KAAK,OAAO;QAAS,GAAI;QAC3B,UAAA;OACE,CAAA,GAEP,oBAAC,OAAD;QACE,KAAK,CAAC,OAAO,OAAO,QAAQ;QACjB;QACX,UAAU;QACG;QACb,OAAO;QACP,GAAI;QACJ,UAAA;OACD,CAAA,CACE;;MACJ,cAAc,QACb,oBAAC,KAAD;OAAK,KAAK,OAAO;OAAS,GAAI;OAC3B,UAAA;MACE,CAAA;MAEN,cAAc,QACb,oBAAC,KAAD;OAAK,KAAK,OAAO;OAAS,GAAI;OAC3B,UAAA;MACE,CAAA;KAEJ;;IACJ,YAAY,QAAQ,YAAY,UAAU,OAAO,aAAa;GAC1D;;CACI,CAAA;AAEjB,CAAC;AAED,gBAAgB,cAAc"}
|
|
1
|
+
{"version":3,"file":"BitkitTextInput.js","names":[],"sources":["../../../lib/components/BitkitTextInput/BitkitTextInput.tsx"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport { mergeRefs } from '@chakra-ui/react';\nimport { Box } from '@chakra-ui/react/box';\nimport { Group } from '@chakra-ui/react/group';\nimport { Input, type InputProps } from '@chakra-ui/react/input';\nimport { InputAddon, type InputAddonProps } from '@chakra-ui/react/input-addon';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { type ChangeEventHandler, forwardRef, type ReactNode, type Ref, useMemo } from 'react';\n\nimport { IconErrorCircleFilled, IconWarningYellow } from '../../icons';\nimport BitkitField, { type BitkitFieldProps } from '../BitkitField/BitkitField';\n\nexport interface BitkitTextInputProps extends Omit<BitkitFieldProps, 'children' | 'onChange' | 'state' | 'value'> {\n /**\n * Show a live character counter (`length/maxLength`) next to the label. Requires `maxLength` and a\n * known `value`.\n */\n counter?: boolean;\n /**\n * Content attached to the trailing edge, outside the input border. A bare string or number\n * renders in the standard bordered addon box (a unit label like `USD`); any other node renders\n * as itself — see `startAddon`.\n */\n endAddon?: ReactNode;\n /** Props forwarded to the trailing `InputAddon` box. Inert unless the addon is a string. */\n endAddonProps?: InputAddonProps;\n /** Inline content rendered inside the input, at the trailing edge (e.g. an icon or reveal button). */\n endElement?: ReactNode;\n /**\n * Escape hatch for the underlying Chakra `Input` — extra native attributes or a spread\n * `{...register()}`. On conflict, values here win over the top-level `value`/`onChange`. A `ref`\n * here and `inputRef` both receive the `<input>`.\n */\n inputProps?: InputProps & { ref?: Ref<HTMLInputElement> };\n /** Ref to the `<input>` itself, for focus management and form libraries. `ref` stays on the field root. */\n inputRef?: Ref<HTMLInputElement>;\n /** Maximum number of characters accepted; also feeds the optional `counter`. */\n maxLength?: HTMLInputElement['maxLength'];\n /**\n * Native change event — the same handler you could put on `inputProps`, promoted to the top level.\n * Reach for `onValueChange` when you only need the value. If both this and `inputProps.onChange` are\n * set, `inputProps.onChange` wins (it's the same event, so they shouldn't both be set).\n */\n onChange?: ChangeEventHandler<HTMLInputElement>;\n /** Convenience change handler that receives the value directly, without `event.target.value`. */\n onValueChange?: (value: string) => void;\n /** Placeholder text shown while the field is empty. */\n placeholder?: string;\n /** Field size — controls height and typography. */\n size?: 'md' | 'lg';\n /**\n * Content attached to the leading edge, outside the input border. Text has to arrive as a bare\n * string (or number) to get the standard bordered addon box (a unit label like `$`) — wrapped in\n * a `<Text>` or a fragment it counts as a component, which loses the box and leaves\n * `startAddonProps` inert. Any other node keeps its own styling and is welded to the field\n * instead: it shares the field's border and the corners it touches are squared automatically, so\n * a `BitkitButton` / `BitkitIconButton` needs only its own `variant` and a `size` matching the\n * field's — the field's `size` does not reach it.\n */\n startAddon?: ReactNode;\n /** Props forwarded to the leading `InputAddon` box. Inert unless the addon is a string. */\n startAddonProps?: InputAddonProps;\n /** Inline content rendered inside the input, at the leading edge (e.g. an icon). */\n startElement?: ReactNode;\n /**\n * Field state. `error`/`warning` are also implied by `errorText`/`warningText`; `disabled` and\n * `readOnly` gate interaction.\n */\n state?: 'disabled' | 'error' | 'readOnly' | 'warning';\n /**\n * Controlled value. Pair with `onValueChange` or `onChange`. Can also be supplied via `inputProps`,\n * which wins on conflict.\n */\n value?: string;\n}\n\n/**\n * A single-line text field wrapping the native `<input>`, with a label and helper/error/warning\n * text. Reflect the value with `value` + `onValueChange` (or the native `onChange`); `inputProps` is\n * the escape hatch for any other native attribute. `startAddon`/`endAddon` sit outside the border\n * (as a bordered box for text, welded to the field for a component), `startElement`/`endElement`\n * inline inside it, and `counter` pairs with `maxLength`.\n */\nconst BitkitTextInput = forwardRef<HTMLDivElement, BitkitTextInputProps>((props, ref) => {\n const {\n counter,\n endAddon,\n endAddonProps,\n endElement,\n inputProps,\n inputRef,\n maxLength,\n onChange,\n onValueChange,\n placeholder,\n size = 'lg',\n startAddon,\n startAddonProps,\n startElement,\n state,\n value,\n ...rest\n } = props;\n\n const recipe = useSlotRecipe({ key: 'textInput' });\n const styles = recipe({ size });\n\n const iconSize = size === 'md' ? '16' : '24';\n const addonSize = size === 'md' ? 'md' : 'lg';\n\n const hasWarning = state === 'warning' || !!rest.warningText;\n const isInvalid = state === 'error' || !!rest.errorText;\n\n let statusIcon: ReactNode = null;\n if (isInvalid) {\n statusIcon = <IconErrorCircleFilled color=\"icon/negative\" size={iconSize} />;\n } else if (hasWarning) {\n statusIcon = <IconWarningYellow size={iconSize} />;\n }\n\n // `value` / `onChange` may come in at the top level or via `inputProps` — the object wins on\n // conflict (natural for a spread `{...register()}`). `onValueChange` always fires with the value.\n const {\n css: inputCss,\n onChange: inputOnChange,\n ref: inputPropsRef,\n value: inputValue,\n ...restInputProps\n } = inputProps ?? {};\n const setInputRef = useMemo(() => mergeRefs(inputRef, inputPropsRef), [inputRef, inputPropsRef]);\n const resolvedValue = inputValue ?? value;\n const nativeOnChange = inputOnChange ?? onChange;\n const handleChange: ChangeEventHandler<HTMLInputElement> | undefined =\n onValueChange || nativeOnChange\n ? (event) => {\n onValueChange?.(event.target.value);\n nativeOnChange?.(event);\n }\n : undefined;\n\n const counterText =\n counter && maxLength && resolvedValue !== undefined ? `${resolvedValue.toString().length}/${maxLength}` : undefined;\n\n const stateAttrs = {\n 'data-disabled': state === 'disabled' ? '' : undefined,\n 'data-invalid': isInvalid ? '' : undefined,\n 'data-readonly': state === 'readOnly' ? '' : undefined,\n };\n\n const renderAddon = (addon: ReactNode, position: 'end' | 'start', addonProps?: InputAddonProps) =>\n typeof addon === 'string' || typeof addon === 'number' ? (\n <InputAddon position={position} size={addonSize} {...stateAttrs} {...addonProps}>\n {addon}\n </InputAddon>\n ) : (\n addon\n );\n\n return (\n <BitkitField counterText={counterText} ref={ref} state={state} {...rest}>\n <Group attached width=\"100%\">\n {startAddon != null && renderAddon(startAddon, 'start', startAddonProps)}\n <Box css={styles.field} data-has-end-element={endElement != null ? '' : undefined} flex=\"1\" {...stateAttrs}>\n {/* A label around the leading element and the input, so a click on e.g. an icon focuses the input.\n The end element stays outside, as it may be a control. `contents` keeps the field's layout. */}\n <Box as=\"label\" display=\"contents\">\n {startElement != null && (\n <Box css={styles.element} {...stateAttrs}>\n {startElement}\n </Box>\n )}\n <Input\n css={[styles.input, inputCss]}\n maxLength={maxLength}\n onChange={handleChange}\n placeholder={placeholder}\n ref={setInputRef}\n value={resolvedValue}\n {...restInputProps}\n unstyled\n />\n </Box>\n {statusIcon != null && (\n <Box css={styles.element} {...stateAttrs}>\n {statusIcon}\n </Box>\n )}\n {endElement != null && (\n <Box css={styles.element} {...stateAttrs}>\n {endElement}\n </Box>\n )}\n </Box>\n {endAddon != null && renderAddon(endAddon, 'end', endAddonProps)}\n </Group>\n </BitkitField>\n );\n});\n\nBitkitTextInput.displayName = 'BitkitTextInput';\n\nexport default BitkitTextInput;\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmFA,IAAM,kBAAkB,YAAkD,OAAO,QAAQ;CACvF,MAAM,EACJ,SACA,UACA,eACA,YACA,YACA,UACA,WACA,UACA,eACA,aACA,OAAO,MACP,YACA,iBACA,cACA,OACA,OACA,GAAG,SACD;CAGJ,MAAM,SADS,cAAc,EAAE,KAAK,YAAY,CACjC,CAAA,CAAO,EAAE,KAAK,CAAC;CAE9B,MAAM,WAAW,SAAS,OAAO,OAAO;CACxC,MAAM,YAAY,SAAS,OAAO,OAAO;CAEzC,MAAM,aAAa,UAAU,aAAa,CAAC,CAAC,KAAK;CACjD,MAAM,YAAY,UAAU,WAAW,CAAC,CAAC,KAAK;CAE9C,IAAI,aAAwB;CAC5B,IAAI,WACF,aAAa,oBAAC,uBAAD;EAAuB,OAAM;EAAgB,MAAM;CAAW,CAAA;MACtE,IAAI,YACT,aAAa,oBAAC,mBAAD,EAAmB,MAAM,SAAW,CAAA;CAKnD,MAAM,EACJ,KAAK,UACL,UAAU,eACV,KAAK,eACL,OAAO,YACP,GAAG,mBACD,cAAc,CAAC;CACnB,MAAM,cAAc,cAAc,UAAU,UAAU,aAAa,GAAG,CAAC,UAAU,aAAa,CAAC;CAC/F,MAAM,gBAAgB,cAAc;CACpC,MAAM,iBAAiB,iBAAiB;CACxC,MAAM,eACJ,iBAAiB,kBACZ,UAAU;EACT,gBAAgB,MAAM,OAAO,KAAK;EAClC,iBAAiB,KAAK;CACxB,IACA,KAAA;CAEN,MAAM,cACJ,WAAW,aAAa,kBAAkB,KAAA,IAAY,GAAG,cAAc,SAAS,CAAC,CAAC,OAAO,GAAG,cAAc,KAAA;CAE5G,MAAM,aAAa;EACjB,iBAAiB,UAAU,aAAa,KAAK,KAAA;EAC7C,gBAAgB,YAAY,KAAK,KAAA;EACjC,iBAAiB,UAAU,aAAa,KAAK,KAAA;CAC/C;CAEA,MAAM,eAAe,OAAkB,UAA2B,eAChE,OAAO,UAAU,YAAY,OAAO,UAAU,WAC5C,oBAAC,YAAD;EAAsB;EAAU,MAAM;EAAW,GAAI;EAAY,GAAI;EAClE,UAAA;CACS,CAAA,IAEZ;CAGJ,OACE,oBAAC,aAAD;EAA0B;EAAkB;EAAY;EAAO,GAAI;EACjE,UAAA,qBAAC,OAAD;GAAO,UAAA;GAAS,OAAM;GAAtB,UAAA;IACG,cAAc,QAAQ,YAAY,YAAY,SAAS,eAAe;IACvE,qBAAC,KAAD;KAAK,KAAK,OAAO;KAAO,wBAAsB,cAAc,OAAO,KAAK,KAAA;KAAW,MAAK;KAAI,GAAI;KAAhG,UAAA;MAGE,qBAAC,KAAD;OAAK,IAAG;OAAQ,SAAQ;OAAxB,UAAA,CACG,gBAAgB,QACf,oBAAC,KAAD;QAAK,KAAK,OAAO;QAAS,GAAI;QAC3B,UAAA;OACE,CAAA,GAEP,oBAAC,OAAD;QACE,KAAK,CAAC,OAAO,OAAO,QAAQ;QACjB;QACX,UAAU;QACG;QACb,KAAK;QACL,OAAO;QACP,GAAI;QACJ,UAAA;OACD,CAAA,CACE;;MACJ,cAAc,QACb,oBAAC,KAAD;OAAK,KAAK,OAAO;OAAS,GAAI;OAC3B,UAAA;MACE,CAAA;MAEN,cAAc,QACb,oBAAC,KAAD;OAAK,KAAK,OAAO;OAAS,GAAI;OAC3B,UAAA;MACE,CAAA;KAEJ;;IACJ,YAAY,QAAQ,YAAY,UAAU,OAAO,aAAa;GAC1D;;CACI,CAAA;AAEjB,CAAC;AAED,gBAAgB,cAAc"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitrise/bitkit-v2",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.498-beta.3202",
|
|
5
5
|
"description": "Bitrise Design System Components built with Chakra UI V3",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"react",
|
|
@@ -39,7 +39,9 @@
|
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "vite build",
|
|
42
|
+
"prestorybook": "npm run docs:generate",
|
|
42
43
|
"storybook": "storybook dev -p 6006",
|
|
44
|
+
"prebuild-storybook": "npm run docs:generate",
|
|
43
45
|
"build-storybook": "storybook build",
|
|
44
46
|
"theme:generate-types": "chakra typegen lib/theme/index.ts && node ./scripts/prune-color-tokens.cjs",
|
|
45
47
|
"theme:diff-tokens": "node ./scripts/diff-design-tokens.cjs",
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
"lint": "eslint .",
|
|
48
50
|
"lint:fix": "eslint . --fix",
|
|
49
51
|
"check:agents-drift": "node ./scripts/check-agents-drift.cjs",
|
|
52
|
+
"docs:generate": "tsx ./scripts/generate-docs-text.ts",
|
|
50
53
|
"start": "npm run storybook",
|
|
51
54
|
"type-check": "tsc --noEmit",
|
|
52
55
|
"release": "release-it patch --ci",
|