@bspk/ui 1.1.14 → 1.1.15
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/dist/Link.d.ts +2 -1
- package/dist/Link.js.map +1 -1
- package/dist/NumberInput.d.ts +2 -6
- package/dist/NumberInput.js +11 -3
- package/dist/NumberInput.js.map +1 -1
- package/dist/SegmentedControl.d.ts +11 -7
- package/dist/SegmentedControl.js +7 -6
- package/dist/SegmentedControl.js.map +1 -1
- package/dist/StylesProviderAnywhere.js +1 -1
- package/dist/StylesProviderBetterHomesGardens.js +1 -1
- package/dist/StylesProviderCartus.js +1 -1
- package/dist/StylesProviderCentury21.js +1 -1
- package/dist/StylesProviderColdwellBanker.js +1 -1
- package/dist/StylesProviderCorcoran.js +1 -1
- package/dist/StylesProviderDenaliBoss.js +1 -1
- package/dist/StylesProviderEra.js +1 -1
- package/dist/StylesProviderSothebys.js +1 -1
- package/dist/TabGroup.d.ts +15 -6
- package/dist/TabGroup.js +13 -5
- package/dist/TabGroup.js.map +1 -1
- package/dist/Textarea.d.ts +18 -8
- package/dist/Textarea.js +14 -7
- package/dist/Textarea.js.map +1 -1
- package/dist/base.css +1 -1
- package/dist/hooks/{useNavOptions.d.ts → useOptionIconsInvalid.d.ts} +4 -5
- package/dist/hooks/useOptionIconsInvalid.js +21 -0
- package/dist/hooks/useOptionIconsInvalid.js.map +1 -0
- package/dist/textarea.css +1 -1
- package/package.json +1 -1
- package/src/Link.tsx +28 -25
- package/src/NumberInput.tsx +20 -13
- package/src/SegmentedControl.tsx +18 -13
- package/src/TabGroup.tsx +33 -12
- package/src/Textarea.tsx +29 -16
- package/src/base.scss +4 -3
- package/src/hooks/useOptionIconsInvalid.ts +49 -0
- package/src/textarea.scss +5 -2
- package/dist/hooks/useNavOptions.js +0 -43
- package/dist/hooks/useNavOptions.js.map +0 -1
- package/src/hooks/useNavOptions.ts +0 -76
package/dist/Textarea.js
CHANGED
|
@@ -3,24 +3,31 @@ import { styleAdd } from './utils/styleAdd';
|
|
|
3
3
|
styleAdd(`[data-bspk=textarea]{/*!
|
|
4
4
|
--min-rows: is set via inline style
|
|
5
5
|
--max-rows: is set via inline style
|
|
6
|
-
*/display:grid;width:100%;--font: var(--body-base);--line-height: 20px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea][data-size=small]{--font: var(--body-small);--line-height: 20px;--padding: var(--spacing-sizing-02)}[data-bspk=textarea][data-size=large]{--font: var(--body-large);--line-height: 24px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea] [data-replicated-value]{white-space:pre-wrap;visibility:hidden}[data-bspk=textarea] textarea,[data-bspk=textarea] [data-replicated-value]{width:100%;font:var(--font);border:1px solid var(--border-color);padding:var(--padding);grid-area:1/1/2/2;min-height:calc(var(--line-height)*var(--min-rows) + var(--padding)*2);max-height:calc(var(--line-height)*var(--max-rows) + var(--padding)*2)}[data-bspk=textarea] textarea{--border-color: var(--stroke-neutral-base);resize:
|
|
6
|
+
*/display:grid;width:100%;--font: var(--body-base);--line-height: 20px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea][data-size=small]{--font: var(--body-small);--line-height: 20px;--padding: var(--spacing-sizing-02)}[data-bspk=textarea][data-size=large]{--font: var(--body-large);--line-height: 24px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea] [data-replicated-value]{white-space:pre-wrap;visibility:hidden}[data-bspk=textarea] textarea,[data-bspk=textarea] [data-replicated-value]{width:100%;font:var(--font);border:1px solid var(--border-color);padding:var(--padding);grid-area:1/1/2/2;min-height:calc(var(--line-height)*var(--min-rows) + var(--padding)*2);max-height:calc(var(--line-height)*var(--max-rows) + var(--padding)*2)}[data-bspk=textarea] textarea{--border-color: var(--stroke-neutral-base);resize:none;color:var(--foreground-neutral-on-surface);background-color:var(--surface-neutral-t1-base);border-radius:var(--radius-small)}[data-bspk=textarea] textarea::placeholder{color:var(--foreground-neutral-on-surface-variant-03)}[data-bspk=textarea] textarea:focus-within{--border-color: var(--stroke-neutral-focus);outline:none;color:var(--foreground-neutral-on-surface)}[data-bspk=textarea] textarea:disabled{pointer-events:none;background:linear-gradient(var(--interactions-disabled-opacity), var(--interactions-disabled-opacity)),linear-gradient(var(--surface-neutral-t1-base), var(--surface-neutral-t1-base));color:var(--foreground-neutral-disabled-on-surface)}[data-bspk=textarea] textarea:read-only{background:linear-gradient(var(--interactions-disabled-opacity), var(--interactions-disabled-opacity)),linear-gradient(var(--surface-neutral-t1-base), var(--surface-neutral-t1-base));cursor:not-allowed}[data-bspk=textarea] textarea[aria-invalid]{--border-color: var(--status-error)}`);;
|
|
7
7
|
import { useId } from './hooks/useId';
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const DEFAULT = {
|
|
9
|
+
minRows: 3,
|
|
10
|
+
maxRows: 10,
|
|
11
|
+
textSize: 'medium',
|
|
12
|
+
};
|
|
10
13
|
/**
|
|
11
14
|
* A component that allows users to input large amounts of text that could span multiple lines.
|
|
12
15
|
*
|
|
16
|
+
* This component gives you a textarea HTML element that automatically adjusts its height to match the length of the
|
|
17
|
+
* content within maximum and minimum rows. A character counter when a maxLength is set to show the number of characters
|
|
18
|
+
* remaining below the limit.
|
|
19
|
+
*
|
|
13
20
|
* @element
|
|
14
21
|
*
|
|
15
22
|
* @name Textarea
|
|
16
23
|
*/
|
|
17
|
-
function Textarea({ invalid: invalidProp, onChange,
|
|
24
|
+
function Textarea({ invalid: invalidProp, onChange, textSize = DEFAULT.textSize, value = '', name, 'aria-label': ariaLabel, innerRef, placeholder, id: idProp, minRows: minRowsProp = DEFAULT.minRows, maxRows: maxRowsProp = DEFAULT.maxRows, errorMessage, ...otherProps }) {
|
|
18
25
|
const id = useId(idProp);
|
|
19
26
|
const invalid = !otherProps.readOnly && !otherProps.disabled && invalidProp;
|
|
20
27
|
// ensure minRows and maxRows are within bounds
|
|
21
|
-
const minRows = Math.min(
|
|
22
|
-
const maxRows = Math.max(
|
|
23
|
-
return (_jsxs("div", { "data-bspk": "textarea", "data-size":
|
|
28
|
+
const minRows = Math.min(DEFAULT.maxRows, Math.max(minRowsProp, DEFAULT.minRows));
|
|
29
|
+
const maxRows = Math.max(DEFAULT.minRows, Math.min(maxRowsProp, DEFAULT.maxRows));
|
|
30
|
+
return (_jsxs("div", { "data-bspk": "textarea", "data-size": textSize, style: {
|
|
24
31
|
'--min-rows': minRows,
|
|
25
32
|
'--max-rows': maxRows,
|
|
26
33
|
}, children: [_jsx("textarea", { ...otherProps, "aria-errormessage": errorMessage || undefined, "aria-invalid": invalid || undefined, "aria-label": ariaLabel, id: id, name: name, onBlur: (event) => {
|
package/dist/Textarea.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../src/Textarea.tsx"],"names":[],"mappings":";AAAA,OAAO,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Textarea.js","sourceRoot":"","sources":["../src/Textarea.tsx"],"names":[],"mappings":";AAAA,OAAO,iBAAiB,CAAC;AAGzB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAItC,MAAM,OAAO,GAAG;IACZ,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,QAAQ;CACZ,CAAC;AAyDX;;;;;;;;;;GAUG;AACH,SAAS,QAAQ,CAAC,EACd,OAAO,EAAE,WAAW,EACpB,QAAQ,EACR,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAC3B,KAAK,GAAG,EAAE,EACV,IAAI,EACJ,YAAY,EAAE,SAAS,EACvB,QAAQ,EACR,WAAW,EACX,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EACtC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EACtC,YAAY,EACZ,GAAG,UAAU,EACD;IACZ,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IACzB,MAAM,OAAO,GAAG,CAAC,UAAU,CAAC,QAAQ,IAAI,CAAC,UAAU,CAAC,QAAQ,IAAI,WAAW,CAAC;IAC5E,+CAA+C;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAClF,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAElF,OAAO,CACH,4BACc,UAAU,eACT,QAAQ,EACnB,KAAK,EACD;YACI,YAAY,EAAE,OAAO;YACrB,YAAY,EAAE,OAAO;SACP,aAGtB,sBACQ,UAAU,uBACK,YAAY,IAAI,SAAS,kBAC9B,OAAO,IAAI,SAAS,gBACtB,SAAS,EACrB,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;oBACd,MAAM,MAAM,GAAG,KAAK,CAAC,MAA6B,CAAC;oBACnD,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC;gBACzB,CAAC,EACD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EACxD,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;oBACf,MAAM,MAAM,GAAG,KAAK,CAAC,MAA6B,CAAC;oBACnD,qEAAqE;oBACrE,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM;wBAAE,OAAO;oBAC/B,MAAM,CAAC,WAA2B,CAAC,SAAS,GAAG,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;gBACxE,CAAC,EACD,WAAW,EAAE,WAAW,EACxB,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,KAAK,GACd,EACF,mEAAyC,IACvC,CACT,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,QAAQ,GAAG,UAAU,CAAC;AAE/B,OAAO,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/base.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
[data-color=grey]{--foreground: var(--foreground-neutral-on-surface-variant-01);--background: var(--surface-neutral-t2-lowest)}[data-color=white]{--foreground: var(--foreground-neutral-on-surface-variant-01);--background: var(--surface-neutral-t1-base)}[data-color=primary]{--foreground: var(--foreground-brand-primary-depth);--background: var(--surface-brand-primary-highlight)}[data-color=secondary]{--foreground: var(--foreground-brand-secondary-depth);--background: var(--surface-brand-secondary-highlight)}[data-color=blue]{--foreground: var(--foreground-spectrum-blue);--background: var(--surface-spectrum-blue)}[data-color=green]{--foreground: var(--foreground-spectrum-green);--background: var(--surface-spectrum-green)}[data-color=lime]{--foreground: var(--foreground-spectrum-lime);--background: var(--surface-spectrum-lime)}[data-color=magenta]{--foreground: var(--foreground-spectrum-magenta);--background: var(--surface-spectrum-magenta)}[data-color=orange]{--foreground: var(--foreground-spectrum-orange);--background: var(--surface-spectrum-orange)}[data-color=pink]{--foreground: var(--foreground-spectrum-pink);--background: var(--surface-spectrum-pink)}[data-color=purple]{--foreground: var(--foreground-spectrum-purple);--background: var(--surface-spectrum-purple)}[data-color=red]{--foreground: var(--foreground-spectrum-red);--background: var(--surface-spectrum-red)}[data-color=teal]{--foreground: var(--foreground-spectrum-teal);--background: var(--surface-spectrum-teal)}[data-color=yellow]{--foreground: var(--foreground-spectrum-yellow);--background: var(--surface-spectrum-yellow)}:root{--z-index-tooltip-popover: 1100;--z-index-dialog: 1000;--z-index-dropdown: 900;--z-index-fab: 800;--z-index-navbar: 700;--z-index-footer: 600}*,*::before,*::after{box-sizing:border-box}*{margin:0;padding:0}@media(prefers-reduced-motion){[data-animated]{animation:none !important}}body,html{height:100%;scroll-behavior:smooth}body{font:var(--body-base);background-color:var(--background-base);color:var(--foreground-neutral-on-surface)}a{color:var(--foreground-link-text-default)}a:hover{color:var(--foreground-link-text-default-hovered)}a:visited{color:var(--foreground-link-text-default-visited)}a
|
|
1
|
+
[data-color=grey]{--foreground: var(--foreground-neutral-on-surface-variant-01);--background: var(--surface-neutral-t2-lowest)}[data-color=white]{--foreground: var(--foreground-neutral-on-surface-variant-01);--background: var(--surface-neutral-t1-base)}[data-color=primary]{--foreground: var(--foreground-brand-primary-depth);--background: var(--surface-brand-primary-highlight)}[data-color=secondary]{--foreground: var(--foreground-brand-secondary-depth);--background: var(--surface-brand-secondary-highlight)}[data-color=blue]{--foreground: var(--foreground-spectrum-blue);--background: var(--surface-spectrum-blue)}[data-color=green]{--foreground: var(--foreground-spectrum-green);--background: var(--surface-spectrum-green)}[data-color=lime]{--foreground: var(--foreground-spectrum-lime);--background: var(--surface-spectrum-lime)}[data-color=magenta]{--foreground: var(--foreground-spectrum-magenta);--background: var(--surface-spectrum-magenta)}[data-color=orange]{--foreground: var(--foreground-spectrum-orange);--background: var(--surface-spectrum-orange)}[data-color=pink]{--foreground: var(--foreground-spectrum-pink);--background: var(--surface-spectrum-pink)}[data-color=purple]{--foreground: var(--foreground-spectrum-purple);--background: var(--surface-spectrum-purple)}[data-color=red]{--foreground: var(--foreground-spectrum-red);--background: var(--surface-spectrum-red)}[data-color=teal]{--foreground: var(--foreground-spectrum-teal);--background: var(--surface-spectrum-teal)}[data-color=yellow]{--foreground: var(--foreground-spectrum-yellow);--background: var(--surface-spectrum-yellow)}:root{--z-index-tooltip-popover: 1100;--z-index-dialog: 1000;--z-index-dropdown: 900;--z-index-fab: 800;--z-index-navbar: 700;--z-index-footer: 600}*,*::before,*::after{box-sizing:border-box}*{margin:0;padding:0}@media(prefers-reduced-motion){[data-animated]{animation:none !important}}body,html{height:100%;scroll-behavior:smooth}body{font:var(--body-base);background-color:var(--background-base);color:var(--foreground-neutral-on-surface)}a{color:var(--foreground-link-text-default)}a:not([disabled]):hover{color:var(--foreground-link-text-default-hovered)}a:not([disabled]):visited{color:var(--foreground-link-text-default-visited)}a[disabled]{pointer-events:none;cursor:text;color:var(--foreground-link-text-default-disabled)}a[data-subtle]{color:var(--foreground-neutral-on-surface)}a[data-subtle]:hover{color:var(--foreground-link-text-subtle-hovered)}a[data-subtle]:disabled{pointer-events:none;color:var(--foreground-link-text-subtle-disabled)}a[data-subtle-inverse]{color:var(--foreground-neutral-inverse-on-surface)}a[data-subtle-inverse]:hover{color:var(--foreground-link-text-subtle-inverse-hovered)}a[data-subtle-inverse]:disabled{pointer-events:none;color:var(--foreground-link-text-subtle-inversed-disabled)}input:-internal-autofill-previewed,input:-internal-autofill-selected,textarea:-internal-autofill-previewed,textarea:-internal-autofill-selected,select:-internal-autofill-previewed,select:-internal-autofill-selected{transition:color calc(infinity*1s) step-end,background-color calc(infinity*1s) step-end}[data-sr-only]{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);white-space:nowrap;border-width:0}ol{list-style-type:decimal}ol li{list-style-type:decimal;margin-left:var(--spacing-sizing-05)}ul{list-style-type:disc}ul li{list-style-type:disc;margin-left:var(--spacing-sizing-05)}[data-touch-target]{display:none;touch-action:manipulation;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-touch-callout:none;user-select:none;position:absolute;z-index:1;height:100%;width:100%;top:auto;left:auto}[data-touch-target]~*{position:relative;z-index:2}@media(any-pointer: coarse){[data-touch-target]{display:block}}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A utility hook used within navigation components. Returns
|
|
3
|
-
* the icons and sets the icon and iconActive properties.
|
|
2
|
+
* A utility hook used within navigation components. Returns true if the icons are invalid.
|
|
4
3
|
*
|
|
5
|
-
* @param options [NavOption[]] The options to display. Each option has
|
|
6
|
-
* @returns {
|
|
4
|
+
* @param options [NavOption[]] The options to display. Each option has an optional leading icon.
|
|
5
|
+
* @returns {boolean} True if the icons are invalid.
|
|
7
6
|
*/
|
|
8
|
-
export declare function
|
|
7
|
+
export declare function useOptionIconsInvalid<T extends NavOption>(options: T[] | undefined): boolean;
|
|
9
8
|
export type NavOption = {
|
|
10
9
|
/**
|
|
11
10
|
* The label of the option. This is the text that will be displayed on the option.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { isValidIcon } from '../utils/children';
|
|
3
|
+
import { useErrorLogger } from '../utils/errors';
|
|
4
|
+
/**
|
|
5
|
+
* A utility hook used within navigation components. Returns true if the icons are invalid.
|
|
6
|
+
*
|
|
7
|
+
* @param options [NavOption[]] The options to display. Each option has an optional leading icon.
|
|
8
|
+
* @returns {boolean} True if the icons are invalid.
|
|
9
|
+
*/
|
|
10
|
+
export function useOptionIconsInvalid(options) {
|
|
11
|
+
const { logError } = useErrorLogger();
|
|
12
|
+
return useMemo(() => {
|
|
13
|
+
if (!options || !Array.isArray(options))
|
|
14
|
+
return true;
|
|
15
|
+
const iconsInvalid = options.some((o) => o.icon) && !options.every((option) => isValidIcon(option.icon));
|
|
16
|
+
logError(iconsInvalid, 'useNavOptions - Every option either must have a valid icon or none at all. All icons removed.');
|
|
17
|
+
return iconsInvalid;
|
|
18
|
+
}, [logError, options]);
|
|
19
|
+
}
|
|
20
|
+
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
|
21
|
+
//# sourceMappingURL=useOptionIconsInvalid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOptionIconsInvalid.js","sourceRoot":"","sources":["../../src/hooks/useOptionIconsInvalid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAsB,OAAwB;IAC/E,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,EAAE,CAAC;IAEtC,OAAO,OAAO,CAAC,GAAG,EAAE;QAChB,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;QAErD,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QAEzG,QAAQ,CACJ,YAAY,EACZ,+FAA+F,CAClG,CAAC;QAEF,OAAO,YAAY,CAAC;IACxB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAC5B,CAAC;AAsBD,sDAAsD"}
|
package/dist/textarea.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[data-bspk=textarea]{/*!
|
|
2
2
|
--min-rows: is set via inline style
|
|
3
3
|
--max-rows: is set via inline style
|
|
4
|
-
*/display:grid;width:100%;--font: var(--body-base);--line-height: 20px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea][data-size=small]{--font: var(--body-small);--line-height: 20px;--padding: var(--spacing-sizing-02)}[data-bspk=textarea][data-size=large]{--font: var(--body-large);--line-height: 24px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea] [data-replicated-value]{white-space:pre-wrap;visibility:hidden}[data-bspk=textarea] textarea,[data-bspk=textarea] [data-replicated-value]{width:100%;font:var(--font);border:1px solid var(--border-color);padding:var(--padding);grid-area:1/1/2/2;min-height:calc(var(--line-height)*var(--min-rows) + var(--padding)*2);max-height:calc(var(--line-height)*var(--max-rows) + var(--padding)*2)}[data-bspk=textarea] textarea{--border-color: var(--stroke-neutral-base);resize:
|
|
4
|
+
*/display:grid;width:100%;--font: var(--body-base);--line-height: 20px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea][data-size=small]{--font: var(--body-small);--line-height: 20px;--padding: var(--spacing-sizing-02)}[data-bspk=textarea][data-size=large]{--font: var(--body-large);--line-height: 24px;--padding: var(--spacing-sizing-03)}[data-bspk=textarea] [data-replicated-value]{white-space:pre-wrap;visibility:hidden}[data-bspk=textarea] textarea,[data-bspk=textarea] [data-replicated-value]{width:100%;font:var(--font);border:1px solid var(--border-color);padding:var(--padding);grid-area:1/1/2/2;min-height:calc(var(--line-height)*var(--min-rows) + var(--padding)*2);max-height:calc(var(--line-height)*var(--max-rows) + var(--padding)*2)}[data-bspk=textarea] textarea{--border-color: var(--stroke-neutral-base);resize:none;color:var(--foreground-neutral-on-surface);background-color:var(--surface-neutral-t1-base);border-radius:var(--radius-small)}[data-bspk=textarea] textarea::placeholder{color:var(--foreground-neutral-on-surface-variant-03)}[data-bspk=textarea] textarea:focus-within{--border-color: var(--stroke-neutral-focus);outline:none;color:var(--foreground-neutral-on-surface)}[data-bspk=textarea] textarea:disabled{pointer-events:none;background:linear-gradient(var(--interactions-disabled-opacity), var(--interactions-disabled-opacity)),linear-gradient(var(--surface-neutral-t1-base), var(--surface-neutral-t1-base));color:var(--foreground-neutral-disabled-on-surface)}[data-bspk=textarea] textarea:read-only{background:linear-gradient(var(--interactions-disabled-opacity), var(--interactions-disabled-opacity)),linear-gradient(var(--surface-neutral-t1-base), var(--surface-neutral-t1-base));cursor:not-allowed}[data-bspk=textarea] textarea[aria-invalid]{--border-color: var(--status-error)}
|
package/package.json
CHANGED
package/src/Link.tsx
CHANGED
|
@@ -4,31 +4,34 @@ import { SvgOpenInNew } from '@bspk/icons/OpenInNew';
|
|
|
4
4
|
import './link.scss';
|
|
5
5
|
import { AnchorHTMLAttributes } from 'react';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
7
|
+
import { CommonPropsLibrary } from '.';
|
|
8
|
+
|
|
9
|
+
export type LinkProps = Pick<AnchorHTMLAttributes<unknown>, 'target'> &
|
|
10
|
+
Pick<CommonPropsLibrary, 'disabled'> & {
|
|
11
|
+
/**
|
|
12
|
+
* The label of the link.
|
|
13
|
+
*
|
|
14
|
+
* @required
|
|
15
|
+
*/
|
|
16
|
+
label: string;
|
|
17
|
+
/** The variant of the link. Controls the icon that is displayed and link target. */
|
|
18
|
+
trailingIcon?: 'chevron' | 'external' | 'link';
|
|
19
|
+
/** The [href](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/a#href) of the link. */
|
|
20
|
+
href: AnchorHTMLAttributes<unknown>['href'];
|
|
21
|
+
/**
|
|
22
|
+
* The size of the link.
|
|
23
|
+
*
|
|
24
|
+
* @default base
|
|
25
|
+
*/
|
|
26
|
+
size?: 'base' | 'large' | 'small';
|
|
27
|
+
/**
|
|
28
|
+
* Change the color of the link to a subtle color. This is useful for links that are not primary actions, for
|
|
29
|
+
* example footer menus.
|
|
30
|
+
*
|
|
31
|
+
* @default default
|
|
32
|
+
*/
|
|
33
|
+
variant?: 'default' | 'subtle-inverse' | 'subtle';
|
|
34
|
+
};
|
|
32
35
|
|
|
33
36
|
/**
|
|
34
37
|
* This is the standalone link component. Inline links can use the native `a` element.
|
package/src/NumberInput.tsx
CHANGED
|
@@ -7,6 +7,13 @@ import { useLongPress } from './hooks/useLongPress';
|
|
|
7
7
|
|
|
8
8
|
import { CommonProps, InvalidPropsLibrary } from '.';
|
|
9
9
|
|
|
10
|
+
const DEFAULT = {
|
|
11
|
+
align: 'center',
|
|
12
|
+
size: 'medium',
|
|
13
|
+
disabled: false,
|
|
14
|
+
readOnly: false,
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
10
17
|
function isNumber(value: unknown): number | undefined {
|
|
11
18
|
if (typeof value === 'number') return value;
|
|
12
19
|
if (typeof value !== 'string') return undefined;
|
|
@@ -16,14 +23,10 @@ function isNumber(value: unknown): number | undefined {
|
|
|
16
23
|
|
|
17
24
|
export type NumberInputProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'name' | 'readOnly' | 'size'> &
|
|
18
25
|
InvalidPropsLibrary & {
|
|
19
|
-
/**
|
|
20
|
-
* The value of the control.
|
|
21
|
-
*
|
|
22
|
-
* @required
|
|
23
|
-
*/
|
|
26
|
+
/** The value of the control. */
|
|
24
27
|
value?: number;
|
|
25
28
|
/** Callback when the value changes. */
|
|
26
|
-
onChange: (value: number) => void;
|
|
29
|
+
onChange: (value: number | undefined) => void;
|
|
27
30
|
/**
|
|
28
31
|
* The alignment of the input box. Centered between the plus and minus buttons or to the left of the buttons.
|
|
29
32
|
*
|
|
@@ -57,13 +60,12 @@ export type NumberInputProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'n
|
|
|
57
60
|
* @name NumberInput
|
|
58
61
|
*/
|
|
59
62
|
function NumberInput({
|
|
60
|
-
|
|
61
|
-
value = 1,
|
|
63
|
+
value,
|
|
62
64
|
onChange,
|
|
63
|
-
align =
|
|
64
|
-
size =
|
|
65
|
-
disabled =
|
|
66
|
-
readOnly =
|
|
65
|
+
align = DEFAULT.align,
|
|
66
|
+
size = DEFAULT.size,
|
|
67
|
+
disabled = DEFAULT.disabled,
|
|
68
|
+
readOnly = DEFAULT.readOnly,
|
|
67
69
|
name,
|
|
68
70
|
id: inputIdProp,
|
|
69
71
|
invalid,
|
|
@@ -76,7 +78,12 @@ function NumberInput({
|
|
|
76
78
|
const max = isNumber(maxProp);
|
|
77
79
|
const min = isNumber(minProp);
|
|
78
80
|
|
|
79
|
-
const fix = (next: number = value) => {
|
|
81
|
+
const fix = (next: number | undefined = value) => {
|
|
82
|
+
if (typeof next !== 'number') {
|
|
83
|
+
onChange(undefined);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
let fixValue = next;
|
|
81
88
|
if (typeof min !== 'undefined' && next < min) fixValue = min;
|
|
82
89
|
if (typeof max !== 'undefined' && next > max) fixValue = max;
|
package/src/SegmentedControl.tsx
CHANGED
|
@@ -2,11 +2,11 @@ import './segmented-control.scss';
|
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
3
|
|
|
4
4
|
import { Tooltip } from './Tooltip';
|
|
5
|
-
import {
|
|
5
|
+
import { useOptionIconsInvalid } from './hooks/useOptionIconsInvalid';
|
|
6
6
|
|
|
7
7
|
import { ElementProps } from './';
|
|
8
8
|
|
|
9
|
-
export type
|
|
9
|
+
export type SegmentedControlOption = {
|
|
10
10
|
/**
|
|
11
11
|
* The label of the option. This is the text that will be displayed on the option.
|
|
12
12
|
*
|
|
@@ -30,18 +30,22 @@ export type SegmentedControlProps = {
|
|
|
30
30
|
/**
|
|
31
31
|
* The options to display. Each option has a label and an optional leading icon.
|
|
32
32
|
*
|
|
33
|
-
* @type
|
|
33
|
+
* @type SegmentedControlOption[]
|
|
34
34
|
* @required
|
|
35
35
|
*/
|
|
36
|
-
options:
|
|
37
|
-
/**
|
|
38
|
-
|
|
36
|
+
options: SegmentedControlOption[];
|
|
37
|
+
/**
|
|
38
|
+
* The id of the selected option.
|
|
39
|
+
*
|
|
40
|
+
* @required
|
|
41
|
+
*/
|
|
42
|
+
value: SegmentedControlOption['value'];
|
|
39
43
|
/**
|
|
40
44
|
* The function to call when the option is clicked.
|
|
41
45
|
*
|
|
42
46
|
* @required
|
|
43
47
|
*/
|
|
44
|
-
onChange: (value:
|
|
48
|
+
onChange: (value: SegmentedControlOption['value']) => void;
|
|
45
49
|
/**
|
|
46
50
|
* The size of the options.
|
|
47
51
|
*
|
|
@@ -73,18 +77,19 @@ function SegmentedControl({
|
|
|
73
77
|
onChange,
|
|
74
78
|
value,
|
|
75
79
|
size = 'medium',
|
|
76
|
-
options,
|
|
80
|
+
options: optionsProp,
|
|
77
81
|
width = 'hug',
|
|
78
82
|
showLabels: showLabelsProp = true,
|
|
79
83
|
...containerProps
|
|
80
84
|
}: ElementProps<SegmentedControlProps, 'div'>) {
|
|
81
|
-
const
|
|
85
|
+
const options = Array.isArray(optionsProp) ? optionsProp : [];
|
|
86
|
+
useOptionIconsInvalid(options);
|
|
82
87
|
|
|
83
|
-
const hideLabels = showLabelsProp === false &&
|
|
88
|
+
const hideLabels = showLabelsProp === false && options.every((item) => item.icon && item.label);
|
|
84
89
|
|
|
85
90
|
return (
|
|
86
91
|
<div {...containerProps} data-bspk="segmented-control" data-size={size} data-width={width}>
|
|
87
|
-
{
|
|
92
|
+
{options.map((item, index) => {
|
|
88
93
|
const isActive = item.value === value;
|
|
89
94
|
return (
|
|
90
95
|
<Fragment key={item.value}>
|
|
@@ -92,10 +97,10 @@ function SegmentedControl({
|
|
|
92
97
|
<button
|
|
93
98
|
aria-label={item.label}
|
|
94
99
|
data-first={index === 0 || undefined}
|
|
95
|
-
data-last={index ===
|
|
100
|
+
data-last={index === options.length - 1 || undefined}
|
|
96
101
|
data-selected={isActive || undefined}
|
|
97
102
|
disabled={item.disabled || undefined}
|
|
98
|
-
onClick={() => onChange(item.value)}
|
|
103
|
+
onClick={() => onChange(item.value || item.label)}
|
|
99
104
|
>
|
|
100
105
|
<span data-outer>
|
|
101
106
|
<span data-inner>
|
package/src/TabGroup.tsx
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import './tab-group.scss';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
import { Badge } from './Badge';
|
|
5
|
-
import {
|
|
4
|
+
import { Badge, BadgeProps } from './Badge';
|
|
5
|
+
import { useOptionIconsInvalid } from './hooks/useOptionIconsInvalid';
|
|
6
6
|
|
|
7
7
|
import { ElementProps } from './';
|
|
8
8
|
|
|
9
|
+
export type TabGroupSize = 'large' | 'medium' | 'small';
|
|
10
|
+
|
|
11
|
+
const TAB_BADGE_SIZES: Record<TabGroupSize, BadgeProps['size']> = {
|
|
12
|
+
large: 'small',
|
|
13
|
+
medium: 'x-small',
|
|
14
|
+
small: 'x-small',
|
|
15
|
+
};
|
|
16
|
+
|
|
9
17
|
export type TabGroupOption = {
|
|
10
18
|
/**
|
|
11
19
|
* The label of the tab. This is the text that will be displayed on the tab.
|
|
@@ -19,7 +27,11 @@ export type TabGroupOption = {
|
|
|
19
27
|
* @default false
|
|
20
28
|
*/
|
|
21
29
|
disabled?: boolean;
|
|
22
|
-
/**
|
|
30
|
+
/**
|
|
31
|
+
* The value of the tab sent to onChange when selected.
|
|
32
|
+
*
|
|
33
|
+
* If not provided, the label will be used as the value.
|
|
34
|
+
*/
|
|
23
35
|
value?: string;
|
|
24
36
|
/** The icon to display on the left side of the tab. */
|
|
25
37
|
icon?: ReactNode;
|
|
@@ -37,20 +49,24 @@ export type TabGroupProps = {
|
|
|
37
49
|
* @required
|
|
38
50
|
*/
|
|
39
51
|
options: TabGroupOption[];
|
|
40
|
-
/**
|
|
41
|
-
|
|
52
|
+
/**
|
|
53
|
+
* The id of the selected tab.
|
|
54
|
+
*
|
|
55
|
+
* @required
|
|
56
|
+
*/
|
|
57
|
+
value: TabGroupOption['value'];
|
|
42
58
|
/**
|
|
43
59
|
* The function to call when the tab is clicked.
|
|
44
60
|
*
|
|
45
61
|
* @required
|
|
46
62
|
*/
|
|
47
|
-
onChange: (
|
|
63
|
+
onChange: (tabValue: string, index: number) => void;
|
|
48
64
|
/**
|
|
49
65
|
* The size of the tabs.
|
|
50
66
|
*
|
|
51
67
|
* @default medium
|
|
52
68
|
*/
|
|
53
|
-
size?:
|
|
69
|
+
size?: TabGroupSize;
|
|
54
70
|
/**
|
|
55
71
|
* When 'fill' the options will fill the width of the container. When 'hug', the options will be as wide as their
|
|
56
72
|
* content.
|
|
@@ -77,12 +93,13 @@ function TabGroup({
|
|
|
77
93
|
onChange: onTabChange,
|
|
78
94
|
value,
|
|
79
95
|
size = 'medium',
|
|
80
|
-
options,
|
|
96
|
+
options: optionsProp,
|
|
81
97
|
width = 'hug',
|
|
82
98
|
showTrail = false,
|
|
83
99
|
...containerProps
|
|
84
100
|
}: ElementProps<TabGroupProps, 'div'>) {
|
|
85
|
-
const
|
|
101
|
+
const options = Array.isArray(optionsProp) ? optionsProp : [];
|
|
102
|
+
useOptionIconsInvalid(options);
|
|
86
103
|
|
|
87
104
|
return (
|
|
88
105
|
<div
|
|
@@ -92,7 +109,7 @@ function TabGroup({
|
|
|
92
109
|
data-size={size}
|
|
93
110
|
data-width={width}
|
|
94
111
|
>
|
|
95
|
-
{
|
|
112
|
+
{options.map((item, itemIndex) => {
|
|
96
113
|
const isActive = item.value === value;
|
|
97
114
|
|
|
98
115
|
return (
|
|
@@ -100,12 +117,16 @@ function TabGroup({
|
|
|
100
117
|
data-active={isActive || undefined}
|
|
101
118
|
disabled={item.disabled || undefined}
|
|
102
119
|
key={item.value}
|
|
103
|
-
onClick={() =>
|
|
120
|
+
onClick={() => {
|
|
121
|
+
onTabChange(item.value || item.label, itemIndex);
|
|
122
|
+
}}
|
|
104
123
|
>
|
|
105
124
|
<span>
|
|
106
125
|
{(isActive && item.iconActive) || item.icon}
|
|
107
126
|
{item.label}
|
|
108
|
-
{item.badge &&
|
|
127
|
+
{item.badge && !item.disabled && !isActive && (
|
|
128
|
+
<Badge count={item.badge} size={TAB_BADGE_SIZES[size]} />
|
|
129
|
+
)}
|
|
109
130
|
</span>
|
|
110
131
|
</button>
|
|
111
132
|
);
|
package/src/Textarea.tsx
CHANGED
|
@@ -5,6 +5,12 @@ import { useId } from './hooks/useId';
|
|
|
5
5
|
|
|
6
6
|
import { CommonProps, InvalidPropsLibrary } from './';
|
|
7
7
|
|
|
8
|
+
const DEFAULT = {
|
|
9
|
+
minRows: 3,
|
|
10
|
+
maxRows: 10,
|
|
11
|
+
textSize: 'medium',
|
|
12
|
+
} as const;
|
|
13
|
+
|
|
8
14
|
export type TextareaProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'readOnly' | 'required'> &
|
|
9
15
|
InvalidPropsLibrary & {
|
|
10
16
|
/**
|
|
@@ -15,12 +21,16 @@ export type TextareaProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'read
|
|
|
15
21
|
*/
|
|
16
22
|
onChange: (next: string, event?: ChangeEvent<HTMLTextAreaElement>) => void;
|
|
17
23
|
/**
|
|
18
|
-
* The size of the field.
|
|
24
|
+
* The text size of the field.
|
|
19
25
|
*
|
|
20
26
|
* @default medium
|
|
21
27
|
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
28
|
+
textSize?: 'large' | 'medium' | 'small';
|
|
29
|
+
/**
|
|
30
|
+
* The value of the field.
|
|
31
|
+
*
|
|
32
|
+
* @type multiline
|
|
33
|
+
*/
|
|
24
34
|
value?: string;
|
|
25
35
|
/**
|
|
26
36
|
* The textarea control name of the field.
|
|
@@ -39,27 +49,30 @@ export type TextareaProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'read
|
|
|
39
49
|
*/
|
|
40
50
|
maxLength?: number;
|
|
41
51
|
/**
|
|
42
|
-
* The minimum number of rows that the textarea
|
|
43
|
-
* shrink to fit the content.
|
|
52
|
+
* The minimum number of rows that the textarea will show.
|
|
44
53
|
*
|
|
54
|
+
* @default 3
|
|
45
55
|
* @minimum 3
|
|
56
|
+
* @maximum 10
|
|
46
57
|
*/
|
|
47
58
|
minRows?: number;
|
|
48
59
|
/**
|
|
49
|
-
* The maximum number of rows that the textarea
|
|
50
|
-
* shrink to fit the content.
|
|
60
|
+
* The maximum number of rows that the textarea will show.
|
|
51
61
|
*
|
|
62
|
+
* @default 10
|
|
63
|
+
* @minimum 3
|
|
52
64
|
* @maximum 10
|
|
53
65
|
*/
|
|
54
66
|
maxRows?: number;
|
|
55
67
|
};
|
|
56
68
|
|
|
57
|
-
const MIN_ROWS = 3;
|
|
58
|
-
const MAX_ROWS = 10;
|
|
59
|
-
|
|
60
69
|
/**
|
|
61
70
|
* A component that allows users to input large amounts of text that could span multiple lines.
|
|
62
71
|
*
|
|
72
|
+
* This component gives you a textarea HTML element that automatically adjusts its height to match the length of the
|
|
73
|
+
* content within maximum and minimum rows. A character counter when a maxLength is set to show the number of characters
|
|
74
|
+
* remaining below the limit.
|
|
75
|
+
*
|
|
63
76
|
* @element
|
|
64
77
|
*
|
|
65
78
|
* @name Textarea
|
|
@@ -67,28 +80,28 @@ const MAX_ROWS = 10;
|
|
|
67
80
|
function Textarea({
|
|
68
81
|
invalid: invalidProp,
|
|
69
82
|
onChange,
|
|
70
|
-
|
|
83
|
+
textSize = DEFAULT.textSize,
|
|
71
84
|
value = '',
|
|
72
85
|
name,
|
|
73
86
|
'aria-label': ariaLabel,
|
|
74
87
|
innerRef,
|
|
75
88
|
placeholder,
|
|
76
89
|
id: idProp,
|
|
77
|
-
minRows: minRowsProp =
|
|
78
|
-
maxRows: maxRowsProp =
|
|
90
|
+
minRows: minRowsProp = DEFAULT.minRows,
|
|
91
|
+
maxRows: maxRowsProp = DEFAULT.maxRows,
|
|
79
92
|
errorMessage,
|
|
80
93
|
...otherProps
|
|
81
94
|
}: TextareaProps) {
|
|
82
95
|
const id = useId(idProp);
|
|
83
96
|
const invalid = !otherProps.readOnly && !otherProps.disabled && invalidProp;
|
|
84
97
|
// ensure minRows and maxRows are within bounds
|
|
85
|
-
const minRows = Math.min(
|
|
86
|
-
const maxRows = Math.max(
|
|
98
|
+
const minRows = Math.min(DEFAULT.maxRows, Math.max(minRowsProp, DEFAULT.minRows));
|
|
99
|
+
const maxRows = Math.max(DEFAULT.minRows, Math.min(maxRowsProp, DEFAULT.maxRows));
|
|
87
100
|
|
|
88
101
|
return (
|
|
89
102
|
<div
|
|
90
103
|
data-bspk="textarea"
|
|
91
|
-
data-size={
|
|
104
|
+
data-size={textSize}
|
|
92
105
|
style={
|
|
93
106
|
{
|
|
94
107
|
'--min-rows': minRows,
|
package/src/base.scss
CHANGED
|
@@ -51,16 +51,17 @@ body {
|
|
|
51
51
|
a {
|
|
52
52
|
color: var(--foreground-link-text-default);
|
|
53
53
|
|
|
54
|
-
&:hover {
|
|
54
|
+
&:not([disabled]):hover {
|
|
55
55
|
color: var(--foreground-link-text-default-hovered);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
&:visited {
|
|
58
|
+
&:not([disabled]):visited {
|
|
59
59
|
color: var(--foreground-link-text-default-visited);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
&[disabled] {
|
|
63
63
|
pointer-events: none;
|
|
64
|
+
cursor: text;
|
|
64
65
|
color: var(--foreground-link-text-default-disabled);
|
|
65
66
|
}
|
|
66
67
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isValidIcon } from '../utils/children';
|
|
4
|
+
import { useErrorLogger } from '../utils/errors';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A utility hook used within navigation components. Returns true if the icons are invalid.
|
|
8
|
+
*
|
|
9
|
+
* @param options [NavOption[]] The options to display. Each option has an optional leading icon.
|
|
10
|
+
* @returns {boolean} True if the icons are invalid.
|
|
11
|
+
*/
|
|
12
|
+
export function useOptionIconsInvalid<T extends NavOption>(options: T[] | undefined): boolean {
|
|
13
|
+
const { logError } = useErrorLogger();
|
|
14
|
+
|
|
15
|
+
return useMemo(() => {
|
|
16
|
+
if (!options || !Array.isArray(options)) return true;
|
|
17
|
+
|
|
18
|
+
const iconsInvalid = options.some((o) => o.icon) && !options.every((option) => isValidIcon(option.icon));
|
|
19
|
+
|
|
20
|
+
logError(
|
|
21
|
+
iconsInvalid,
|
|
22
|
+
'useNavOptions - Every option either must have a valid icon or none at all. All icons removed.',
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return iconsInvalid;
|
|
26
|
+
}, [logError, options]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type NavOption = {
|
|
30
|
+
/**
|
|
31
|
+
* The label of the option. This is the text that will be displayed on the option.
|
|
32
|
+
*
|
|
33
|
+
* @required
|
|
34
|
+
*/
|
|
35
|
+
label: string;
|
|
36
|
+
/**
|
|
37
|
+
* Determines if the element is [disabled](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled).
|
|
38
|
+
*
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
/** The value of the option. If not provided, the label will be used as the value. */
|
|
43
|
+
value?: string;
|
|
44
|
+
/** The the icon to display before the label. */
|
|
45
|
+
icon?: React.ReactNode;
|
|
46
|
+
iconActive?: React.ReactNode;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
package/src/textarea.scss
CHANGED
|
@@ -43,7 +43,11 @@
|
|
|
43
43
|
textarea {
|
|
44
44
|
--border-color: var(--stroke-neutral-base);
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
&::placeholder {
|
|
47
|
+
color: var(--foreground-neutral-on-surface-variant-03);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
resize: none;
|
|
47
51
|
color: var(--foreground-neutral-on-surface);
|
|
48
52
|
background-color: var(--surface-neutral-t1-base);
|
|
49
53
|
border-radius: var(--radius-small);
|
|
@@ -71,7 +75,6 @@
|
|
|
71
75
|
|
|
72
76
|
linear-gradient(var(--interactions-disabled-opacity), var(--interactions-disabled-opacity)),
|
|
73
77
|
linear-gradient(var(--surface-neutral-t1-base), var(--surface-neutral-t1-base));
|
|
74
|
-
color: var(--foreground-neutral-on-surface-variant-02);
|
|
75
78
|
cursor: not-allowed;
|
|
76
79
|
}
|
|
77
80
|
|