@kanso-labs/kanso-ui 0.40.8 → 0.40.9
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/components/avatar/index.d.ts +7 -0
- package/dist/components/avatar/index.js.map +1 -1
- package/dist/components/badge/index.js +7 -3
- package/dist/components/badge/index.js.map +1 -1
- package/dist/components/popover/index.d.ts +4 -1
- package/dist/components/popover/index.js +239 -137
- package/dist/components/popover/index.js.map +1 -1
- package/dist/components/product-icon/index.d.ts +3 -1
- package/dist/components/product-icon/index.js.map +1 -1
- package/dist/components/progress-indicator/index.js +1 -1
- package/dist/components/progress-indicator/index.js.map +1 -1
- package/dist/components/search-field/index.js +7 -3
- package/dist/components/search-field/index.js.map +1 -1
- package/dist/components/segmented-button/index.d.ts +17 -1
- package/dist/components/segmented-button/index.js +132 -59
- package/dist/components/segmented-button/index.js.map +1 -1
- package/dist/field/index.js +28 -49
- package/dist/field/index.js.map +1 -1
- package/dist/field/press.js +27 -0
- package/dist/field/press.js.map +1 -0
- package/dist/mark/initials.js +41 -26
- package/dist/mark/initials.js.map +1 -1
- package/dist/styles.css +20 -8
- package/package.json +1 -1
|
@@ -5,6 +5,13 @@ type AvatarProps = Omit<RenderComponentProps<'span'>, 'children'> & {
|
|
|
5
5
|
* The person this represents. Supplies both the initials and the accessible
|
|
6
6
|
* name, so a screen reader announces who it is rather than reading two
|
|
7
7
|
* letters aloud.
|
|
8
|
+
*
|
|
9
|
+
* A display name, as a reader would see it written: the initials are its
|
|
10
|
+
* first and last words. A title before the name ("Dr."), a generational
|
|
11
|
+
* suffix after it ("Jr.", "III") and an aside in brackets at its end
|
|
12
|
+
* ("(she/her)") are passed over, as is punctuation a word opens with. A
|
|
13
|
+
* name written without spaces between its words, as Japanese and Chinese
|
|
14
|
+
* names are, is one word, and takes one initial.
|
|
8
15
|
*/
|
|
9
16
|
name: string;
|
|
10
17
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["c","_c","RenderComponentProps","Mark","AvatarProps","Omit","name","size","src","tone","Avatar","t0","$","props","t1","t2","undefined","t3","_c"],"sources":["../../../src/components/avatar/index.tsx"],"sourcesContent":["'use client'\n\nimport type { RenderComponentProps } from '../../render/useRender'\n\nimport Mark from '../../mark'\n\n// A person, drawn as a circle with the photo cropped to fill it. The box, the\n// sizes, the tones, the photo's loading and the labelling are the mark's in\n// `src/mark`, which ProductIcon draws a thing through as well, so a person\n// and a thing fill the same slot.\n//\n// Which tone a given person gets is the consuming app's decision — the design\n// cycles colours per person, and that cycle belongs where the list of people\n// is, not in this component.\n\ntype AvatarProps = Omit<RenderComponentProps<'span'>, 'children'> & {\n /**\n * The person this represents. Supplies both the initials and the accessible\n * name, so a screen reader announces who it is rather than reading two\n * letters aloud.\n */\n name: string\n /**\n * Diameter: `sm` 36px, `md` 40px, `lg` 56px.\n * @default 'md'\n */\n size?: 'lg' | 'md' | 'sm'\n /**\n * A photo to show in place of the initials. The initials stay until it has\n * loaded, and come back if it fails.\n */\n src?: string\n /**\n * Which container/on-container colour pair to tint with.\n * @default 'primary'\n */\n tone?: 'negative' | 'positive' | 'primary' | 'secondary' | 'tertiary'\n}\n\n/**\n * A person, as a circle holding their photo or, until it loads or when there\n * is none, their initials. `name` is both where the initials come from and\n * what a screen reader announces, so it is required even with a photo.\n *\n * ```tsx\n * <Avatar name=\"Ada Lovelace\" src={photo} size=\"lg\" />\n * ```\n *\n * Reach for ProductIcon when what is drawn is not a person; the two share\n * their sizes, so either fills the same slot in a row or a card.\n *\n * The call site's `className` and `style` land on the circle.\n */\nfunction Avatar({ size = 'md', tone = 'primary', ...props }: AvatarProps) {\n return <Mark {...props} kind=\"person\" size={size} tone={tone} />\n}\n\nexport type { AvatarProps }\n\nexport default Avatar\n"],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","names":["c","_c","RenderComponentProps","Mark","AvatarProps","Omit","name","size","src","tone","Avatar","t0","$","props","t1","t2","undefined","t3","_c"],"sources":["../../../src/components/avatar/index.tsx"],"sourcesContent":["'use client'\n\nimport type { RenderComponentProps } from '../../render/useRender'\n\nimport Mark from '../../mark'\n\n// A person, drawn as a circle with the photo cropped to fill it. The box, the\n// sizes, the tones, the photo's loading and the labelling are the mark's in\n// `src/mark`, which ProductIcon draws a thing through as well, so a person\n// and a thing fill the same slot.\n//\n// Which tone a given person gets is the consuming app's decision — the design\n// cycles colours per person, and that cycle belongs where the list of people\n// is, not in this component.\n\ntype AvatarProps = Omit<RenderComponentProps<'span'>, 'children'> & {\n /**\n * The person this represents. Supplies both the initials and the accessible\n * name, so a screen reader announces who it is rather than reading two\n * letters aloud.\n *\n * A display name, as a reader would see it written: the initials are its\n * first and last words. A title before the name (\"Dr.\"), a generational\n * suffix after it (\"Jr.\", \"III\") and an aside in brackets at its end\n * (\"(she/her)\") are passed over, as is punctuation a word opens with. A\n * name written without spaces between its words, as Japanese and Chinese\n * names are, is one word, and takes one initial.\n */\n name: string\n /**\n * Diameter: `sm` 36px, `md` 40px, `lg` 56px.\n * @default 'md'\n */\n size?: 'lg' | 'md' | 'sm'\n /**\n * A photo to show in place of the initials. The initials stay until it has\n * loaded, and come back if it fails.\n */\n src?: string\n /**\n * Which container/on-container colour pair to tint with.\n * @default 'primary'\n */\n tone?: 'negative' | 'positive' | 'primary' | 'secondary' | 'tertiary'\n}\n\n/**\n * A person, as a circle holding their photo or, until it loads or when there\n * is none, their initials. `name` is both where the initials come from and\n * what a screen reader announces, so it is required even with a photo.\n *\n * ```tsx\n * <Avatar name=\"Ada Lovelace\" src={photo} size=\"lg\" />\n * ```\n *\n * Reach for ProductIcon when what is drawn is not a person; the two share\n * their sizes, so either fills the same slot in a row or a card.\n *\n * The call site's `className` and `style` land on the circle.\n */\nfunction Avatar({ size = 'md', tone = 'primary', ...props }: AvatarProps) {\n return <Mark {...props} kind=\"person\" size={size} tone={tone} />\n}\n\nexport type { AvatarProps }\n\nexport default Avatar\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA4DA,SAAAU,OAAAC,IAAA;CAAA,MAAAC,IAAAX,EAAA,CAAA;CAAA,IAAAY;CAAA,IAAAC;CAAA,IAAAC;CAAA,IAAAH,EAAA,OAAAD,IAAA;EAAgB,CAAA,CAAAJ,MAAAO,IAAAL,MAAAM,IAAA,GAAAF,SAAAF;EAAwDC,EAAA,KAAAD;EAAAC,EAAA,KAAAC;EAAAD,EAAA,KAAAE;EAAAF,EAAA,KAAAG;CAAA,OAAA;EAAAF,QAAAD,EAAA;EAAAE,KAAAF,EAAA;EAAAG,KAAAH,EAAA;CAAA;CAAtD,MAAAL,OAAAO,OAAAE,KAAAA,IAAA,OAAAF;CAAa,MAAAL,OAAAM,OAAAC,KAAAA,IAAA,YAAAD;CAAgB,IAAAE;CAAA,IAAAL,EAAA,OAAAC,SAAAD,EAAA,OAAAL,QAAAK,EAAA,OAAAH,MAAA;EACtCQ,KAAA,oBAAC,MAAD;GAAK,GAAKJ;GAAY,MAAA;GAAeN;GAAYE;EAAI,CAAA;EAAIG,EAAA,KAAAC;EAAAD,EAAA,KAAAL;EAAAK,EAAA,KAAAH;EAAAG,EAAA,KAAAK;CAAA,OAAAA,KAAAL,EAAA;CAAA,OAAzDK;AAAyD"}
|
|
@@ -9,6 +9,10 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
import { useLocale } from "react-aria-components";
|
|
10
10
|
//#region src/components/badge/index.tsx
|
|
11
11
|
const styles = {
|
|
12
|
+
count: {
|
|
13
|
+
kgeoSG: "xvss7es",
|
|
14
|
+
$$css: true
|
|
15
|
+
},
|
|
12
16
|
inFlow: {
|
|
13
17
|
kVAEAm: "x1uhb9sk",
|
|
14
18
|
$$css: true
|
|
@@ -91,7 +95,7 @@ function Badge(t0) {
|
|
|
91
95
|
const t4 = mergeStyles({ className: "x9f619 x3nfvp2 x1n2onr6" }, props$1);
|
|
92
96
|
let t5;
|
|
93
97
|
if ($[4] !== children || $[5] !== label || $[6] !== placed) {
|
|
94
|
-
t5 = props(styles.mark, label === void 0 ? styles.small : styles.large, (children === void 0 || children === null) && styles.inFlow, label !== void 0 && placed);
|
|
98
|
+
t5 = props(styles.mark, label === void 0 ? styles.small : [styles.large, styles.count], (children === void 0 || children === null) && styles.inFlow, label !== void 0 && placed);
|
|
95
99
|
$[4] = children;
|
|
96
100
|
$[5] = label;
|
|
97
101
|
$[6] = placed;
|
|
@@ -101,8 +105,8 @@ function Badge(t0) {
|
|
|
101
105
|
if ($[8] !== label || $[9] !== t5) {
|
|
102
106
|
t6 = /* @__PURE__ */ jsx("span", {
|
|
103
107
|
"aria-hidden": "true",
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
"data-count": label,
|
|
109
|
+
...t5
|
|
106
110
|
});
|
|
107
111
|
$[8] = label;
|
|
108
112
|
$[9] = t5;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["c","_c","stylex","useContext","useLocale","RenderComponentProps","BadgeMarkContext","useRender","mergeStyles","colors","radii","spacing","typography","FORCED_COLORS","styles","
|
|
1
|
+
{"version":3,"file":"index.js","names":["c","_c","stylex","useContext","useLocale","RenderComponentProps","BadgeMarkContext","useRender","mergeStyles","colors","radii","spacing","typography","FORCED_COLORS","styles","count","kgeoSG","$$css","inFlow","kVAEAm","large","kGNEyG","kZKoxP","kMwMTN","k1xSpc","kMv6JI","kGuDYH","kcqcaj","k63SB2","k87sOh","kLqNvP","kjj79g","kb6lSQ","kLWn49","k7Eaqz","kg3NbH","khDVqt","mark","kWkggS","kVAM5u","kaIpWk","ksu8eU","kMzoRj","kB7OPa","kfzvcC","small","kzqmXN","kt4wiu","BadgeProps","max","Badge","t0","$","children","t1","render","props","undefined","locale","placed","t2","numbers","Intl","NumberFormat","format","label","t3","t4","className","t5","t6","t7","t8","t9","defaultTagName","_c","props","stylex.props"],"sources":["../../../src/components/badge/index.tsx"],"sourcesContent":["'use client';\n\nimport * as stylex from '@stylexjs/stylex';\nimport { useContext } from 'react';\nimport { useLocale } from 'react-aria-components';\nimport type { RenderComponentProps } from '../../render/useRender';\nimport { BadgeMarkContext } from '../../badge';\nimport { useRender } from '../../render/useRender';\nimport { mergeStyles } from '../../styles/merge';\nimport '../../tokens/design.tokens.stylex';\nimport { colors, radii, spacing, typography } from '../../tokens/design.tokens.stylex';\n\n// The badges page's badge: a mark on the corner of an icon that flags\n// something new there, either as the 6dp small badge or as the 16dp large\n// badge holding a count. Both are the error pair — the error role, with the\n// count in on error — on a full corner, and the count is label small, capped\n// at four characters with a plus, as \"999+\".\n//\n// The badge wraps what it is drawn on, usually an icon, and places the mark\n// against that element's box, which is what the page measures from. The\n// small badge fills the icon's top trailing 6dp square. The large badge's\n// bottom leading corner sits 14dp down and 12dp in from the icon's top\n// trailing corner, so it stands 2dp above the icon and grows toward the end\n// as its count lengthens, to the page's 34dp at \"999+\". Both are logical\n// insets, so the mark stays on the trailing corner under right-to-left.\n//\n// Given nothing to wrap, the badge is the mark alone, standing in the line\n// rather than over a corner. That is the form the tabs page draws after a\n// label with no icon above it.\n//\n// The mark is hidden from assistive technology. A count read on its own is a\n// number with no subject; it belongs in the name of the control it sits on,\n// where a reader meets it with what it counts — \"Notifications, 3 new\" — and\n// the stories show it there.\n//\n// The count is drawn as the mark's generated content rather than written as\n// its text, so it is no part of the text of what it sits on. Written as text\n// it was, hidden or not: a destination labelled \"Second item\" with a count of\n// 3 read \"3Second item\", which is no name a speech-input user could say and\n// which axe's label-in-name check fails against the name \"Second item, 3 new\"\n// the library recommends. It also ran into anything that copied the\n// control's text.\n//\n// Under forced colours the error fill is painted over in the mode's\n// background, which left the small badge as nothing at all. It takes a 1px\n// border there instead, which the mode keeps and paints in its text colour.\n\n// Windows High Contrast and the rest of the forced-colours modes. Spelled\n// here rather than imported, for the reason src/field/styles.ts records: the\n// StyleX compiler resolves a constant across files only out of a `.stylex.ts`\n// module, and the generated one holds design tokens rather than queries.\nconst FORCED_COLORS = '@media (forced-colors: active)';\nconst styles = {\n count: {\n kgeoSG: \"xvss7es\",\n $$css: true\n },\n inFlow: {\n kVAEAm: \"x1uhb9sk\",\n $$css: true\n },\n large: {\n kGNEyG: \"x6s0dn4\",\n kZKoxP: \"xlup9mm\",\n kMwMTN: \"x1l7c2jh\",\n k1xSpc: \"x78zum5\",\n kMv6JI: \"xs1w0rn\",\n kGuDYH: \"x1n35her\",\n kcqcaj: \"xss6m8b\",\n k63SB2: \"x1su64ma\",\n k87sOh: \"x1qiirwl\",\n kLqNvP: \"xty0069\",\n kjj79g: \"xl56j7k\",\n kb6lSQ: \"xdlnduh\",\n kLWn49: \"xaomhve\",\n k7Eaqz: \"x15kz4h8\",\n kg3NbH: \"ximhkdq\",\n khDVqt: \"xuxw1ft\",\n $$css: true\n },\n mark: {\n kWkggS: \"xwi4nbo\",\n kVAM5u: \"x1w1tqly\",\n kaIpWk: \"x1dkaolv\",\n ksu8eU: \"xx61lck\",\n kMzoRj: \"x13rsnn2\",\n kB7OPa: \"x9f619\",\n kfzvcC: \"x47corl\",\n kVAEAm: \"x10l6tqk\",\n $$css: true\n },\n small: {\n kZKoxP: \"xols6we\",\n kzqmXN: \"x1v4s8kt\",\n k87sOh: \"x13vifvy\",\n kt4wiu: \"xtijo5x\",\n $$css: true\n }\n};\ntype BadgeProps = RenderComponentProps<'span'> & {\n /**\n * The number to show, which draws the page's large badge. Left out, the\n * badge is the small 6dp dot, which flags without counting. A count of 0\n * still draws, so leave the badge out when there is nothing to count.\n */\n count?: number;\n /**\n * The largest count drawn as itself. Above it the badge reads `max` and a\n * plus, which is the page's 999+ at the default.\n * @default 999\n */\n max?: number;\n};\n\n/**\n * A badge on the corner of what it wraps, usually an icon: the small dot,\n * or with `count` the large badge holding a number. The mark is hidden from\n * assistive technology, so put what it says in the name of the control it\n * sits on.\n *\n * ```tsx\n * <IconButton aria-label=\"Notifications, 3 new\">\n * <Badge count={3}>\n * <BellIcon />\n * </Badge>\n * </IconButton>\n * ```\n *\n * Given no children, the mark stands alone in the line, as a tab draws it\n * after its label.\n *\n * The call site's `className` and `style` land on the element wrapping what\n * the badge is drawn on.\n */\nfunction Badge({\n children,\n count,\n max = 999,\n render,\n ...props\n}: BadgeProps) {\n const {\n locale\n } = useLocale();\n const placed = useContext(BadgeMarkContext);\n const numbers = new Intl.NumberFormat(locale);\n const label = count === undefined ? undefined : count > max ? `${numbers.format(max)}+` : numbers.format(count);\n return useRender({\n defaultTagName: 'span',\n props: {\n ...props,\n ...mergeStyles({\n className: \"x9f619 x3nfvp2 x1n2onr6\"\n }, props),\n children: <>\n {children}\n <span aria-hidden=\"true\" data-count={label} {...stylex.props(styles.mark, label === undefined ? styles.small : [styles.large, styles.count], (children === undefined || children === null) && styles.inFlow,\n // What a destination drawing it asks of a count's mark — see\n // src/badge. The small mark stays inside its icon's box, so\n // nothing has to make room for it.\n label !== undefined && placed)} />\n </>\n },\n render\n });\n}\nexport type { BadgeProps };\nexport default Badge;"],"mappings":"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"}
|
|
@@ -48,7 +48,10 @@ type PopoverTrigger = 'hover' | 'press';
|
|
|
48
48
|
* keep its own.
|
|
49
49
|
*
|
|
50
50
|
* Non-modal by design — the page behind stays scrollable and clickable, which
|
|
51
|
-
* is the difference from `Sheet`. Pass `modal` to change that.
|
|
51
|
+
* is the difference from `Sheet`. Pass `modal` to change that. Escape, a press
|
|
52
|
+
* anywhere outside the panel and a button given `slot="close"` close it, and
|
|
53
|
+
* a press on its trigger toggles it. Inside a dialog, a press on the scrim
|
|
54
|
+
* closes the popover first and the dialog on the next, as a menu does.
|
|
52
55
|
*
|
|
53
56
|
* Composed rather than configured by props, because a popover's contents are
|
|
54
57
|
* arbitrary — the parts are `Popover.Content`, `Popover.Title`, and
|