@kanso-labs/react-native-kanso-ui 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/autocomplete/index.js +15 -1
- package/dist/autocomplete/index.js.map +1 -1
- package/dist/button/group.d.ts +5 -0
- package/dist/button/group.js +115 -0
- package/dist/button/group.js.map +1 -0
- package/dist/button/index.d.ts +4 -1
- package/dist/button/index.js +44 -22
- package/dist/button/index.js.map +1 -1
- package/dist/button/slots.js +2 -1
- package/dist/button/slots.js.map +1 -1
- package/dist/calendar/index.js +351 -182
- package/dist/calendar/index.js.map +1 -1
- package/dist/calendar/styles.js +33 -2
- package/dist/calendar/styles.js.map +1 -1
- package/dist/carousel/arrangement.d.ts +5 -0
- package/dist/carousel/arrangement.js +123 -0
- package/dist/carousel/arrangement.js.map +1 -0
- package/dist/chequer/index.js +95 -0
- package/dist/chequer/index.js.map +1 -0
- package/dist/collection/index.d.ts +157 -0
- package/dist/collection/index.js +1331 -0
- package/dist/collection/index.js.map +1 -0
- package/dist/color-thumb/index.js +56 -0
- package/dist/color-thumb/index.js.map +1 -0
- package/dist/components/button/index.d.ts +4 -2
- package/dist/components/button/index.js +227 -188
- package/dist/components/button/index.js.map +1 -1
- package/dist/components/button-group/index.d.ts +87 -0
- package/dist/components/button-group/index.js +275 -0
- package/dist/components/button-group/index.js.map +1 -0
- package/dist/components/calendar/index.d.ts +11 -3
- package/dist/components/calendar/index.js +60 -45
- package/dist/components/calendar/index.js.map +1 -1
- package/dist/components/carousel/index.d.ts +72 -0
- package/dist/components/carousel/index.js +496 -0
- package/dist/components/carousel/index.js.map +1 -0
- package/dist/components/checkbox-group/index.d.ts +3 -4
- package/dist/components/checkbox-group/index.js +1 -1
- package/dist/components/checkbox-group/index.js.map +1 -1
- package/dist/components/color-area/index.d.ts +85 -0
- package/dist/components/color-area/index.js +630 -0
- package/dist/components/color-area/index.js.map +1 -0
- package/dist/components/color-field/index.d.ts +144 -0
- package/dist/components/color-field/index.js +516 -0
- package/dist/components/color-field/index.js.map +1 -0
- package/dist/components/color-picker/index.d.ts +74 -0
- package/dist/components/color-picker/index.js +317 -0
- package/dist/components/color-picker/index.js.map +1 -0
- package/dist/components/color-slider/index.d.ts +82 -0
- package/dist/components/color-slider/index.js +456 -0
- package/dist/components/color-slider/index.js.map +1 -0
- package/dist/components/color-swatch/index.d.ts +46 -0
- package/dist/components/color-swatch/index.js +127 -0
- package/dist/components/color-swatch/index.js.map +1 -0
- package/dist/components/color-swatch-picker/index.d.ts +81 -0
- package/dist/components/color-swatch-picker/index.js +287 -0
- package/dist/components/color-swatch-picker/index.js.map +1 -0
- package/dist/components/color-wheel/index.d.ts +71 -0
- package/dist/components/color-wheel/index.js +407 -0
- package/dist/components/color-wheel/index.js.map +1 -0
- package/dist/components/combo-box/index.d.ts +193 -0
- package/dist/components/combo-box/index.js +616 -0
- package/dist/components/combo-box/index.js.map +1 -0
- package/dist/components/date-field/index.d.ts +120 -0
- package/dist/components/date-field/index.js +111 -0
- package/dist/components/date-field/index.js.map +1 -0
- package/dist/components/date-picker/index.d.ts +130 -0
- package/dist/components/date-picker/index.js +310 -0
- package/dist/components/date-picker/index.js.map +1 -0
- package/dist/components/date-range-picker/index.d.ts +139 -0
- package/dist/components/date-range-picker/index.js +421 -0
- package/dist/components/date-range-picker/index.js.map +1 -0
- package/dist/components/disclosure/index.d.ts +94 -0
- package/dist/components/disclosure/index.js +365 -0
- package/dist/components/disclosure/index.js.map +1 -0
- package/dist/components/disclosure-group/index.d.ts +61 -0
- package/dist/components/disclosure-group/index.js +100 -0
- package/dist/components/disclosure-group/index.js.map +1 -0
- package/dist/components/drop-zone/index.d.ts +113 -0
- package/dist/components/drop-zone/index.js +304 -0
- package/dist/components/drop-zone/index.js.map +1 -0
- package/dist/components/fab/index.d.ts +1 -1
- package/dist/components/fab/index.js +91 -76
- package/dist/components/fab/index.js.map +1 -1
- package/dist/components/form/index.d.ts +3 -2
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/icon-button/index.d.ts +4 -2
- package/dist/components/icon-button/index.js +204 -171
- package/dist/components/icon-button/index.js.map +1 -1
- package/dist/components/list/index.d.ts +15 -158
- package/dist/components/list/index.js +21 -1047
- package/dist/components/list/index.js.map +1 -1
- package/dist/components/list-box/index.d.ts +73 -0
- package/dist/components/list-box/index.js +82 -0
- package/dist/components/list-box/index.js.map +1 -0
- package/dist/components/loading-indicator/index.d.ts +32 -0
- package/dist/components/loading-indicator/index.js +174 -0
- package/dist/components/loading-indicator/index.js.map +1 -0
- package/dist/components/navigation-tree/index.d.ts +117 -0
- package/dist/components/navigation-tree/index.js +135 -0
- package/dist/components/navigation-tree/index.js.map +1 -0
- package/dist/components/number-field/index.js +132 -143
- package/dist/components/number-field/index.js.map +1 -1
- package/dist/components/range-calendar/index.d.ts +69 -0
- package/dist/components/range-calendar/index.js +144 -0
- package/dist/components/range-calendar/index.js.map +1 -0
- package/dist/components/search-view/index.d.ts +78 -0
- package/dist/components/search-view/index.js +570 -0
- package/dist/components/search-view/index.js.map +1 -0
- package/dist/components/select/index.d.ts +152 -0
- package/dist/components/select/index.js +419 -0
- package/dist/components/select/index.js.map +1 -0
- package/dist/components/slider/index.d.ts +111 -0
- package/dist/components/slider/index.js +673 -0
- package/dist/components/slider/index.js.map +1 -0
- package/dist/components/split-button/index.d.ts +77 -0
- package/dist/components/split-button/index.js +354 -0
- package/dist/components/split-button/index.js.map +1 -0
- package/dist/components/tabs/index.d.ts +196 -0
- package/dist/components/tabs/index.js +1137 -0
- package/dist/components/tabs/index.js.map +1 -0
- package/dist/components/time-field/index.d.ts +43 -0
- package/dist/components/time-field/index.js +107 -0
- package/dist/components/time-field/index.js.map +1 -0
- package/dist/components/time-picker/index.d.ts +65 -0
- package/dist/components/time-picker/index.js +1168 -0
- package/dist/components/time-picker/index.js.map +1 -0
- package/dist/components/tooltip/index.d.ts +101 -0
- package/dist/components/tooltip/index.js +223 -0
- package/dist/components/tooltip/index.js.map +1 -0
- package/dist/components/tree/index.d.ts +112 -0
- package/dist/components/tree/index.js +157 -0
- package/dist/components/tree/index.js.map +1 -0
- package/dist/disclosure/chevron.js +75 -0
- package/dist/disclosure/chevron.js.map +1 -0
- package/dist/disclosure/index.js +7 -0
- package/dist/disclosure/index.js.map +1 -0
- package/dist/drop/index.d.ts +54 -0
- package/dist/drop/index.js +161 -0
- package/dist/drop/index.js.map +1 -0
- package/dist/field/chevron.js +74 -0
- package/dist/field/chevron.js.map +1 -0
- package/dist/field/index.d.ts +2 -1
- package/dist/field/index.js +310 -154
- package/dist/field/index.js.map +1 -1
- package/dist/field/styles.js +9 -0
- package/dist/field/styles.js.map +1 -1
- package/dist/files/index.d.ts +18 -0
- package/dist/files/index.js +107 -0
- package/dist/files/index.js.map +1 -0
- package/dist/form/index.d.ts +5 -2
- package/dist/form/index.js +64 -85
- package/dist/form/index.js.map +1 -1
- package/dist/glyphs/index.js +92 -1
- package/dist/glyphs/index.js.map +1 -1
- package/dist/i18n/index.js +20 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/i18n/messages.js +1394 -34
- package/dist/i18n/messages.js.map +1 -1
- package/dist/index.d.ts +35 -3
- package/dist/index.js +34 -3
- package/dist/indicator/paths.js +23 -1
- package/dist/indicator/paths.js.map +1 -1
- package/dist/modal/index.js +92 -77
- package/dist/modal/index.js.map +1 -1
- package/dist/number/keyboard.js +16 -0
- package/dist/number/keyboard.js.map +1 -0
- package/dist/overlay/anchored.js +68 -38
- package/dist/overlay/anchored.js.map +1 -1
- package/dist/overlay/keyboard.js +57 -0
- package/dist/overlay/keyboard.js.map +1 -0
- package/dist/overlay/panel.js +173 -19
- package/dist/overlay/panel.js.map +1 -1
- package/dist/overlay/placement.js +13 -1
- package/dist/overlay/placement.js.map +1 -1
- package/dist/picker/index.js +322 -0
- package/dist/picker/index.js.map +1 -0
- package/dist/press/index.d.ts +2 -0
- package/dist/press/index.js +102 -100
- package/dist/press/index.js.map +1 -1
- package/dist/row/index.js +77 -69
- package/dist/row/index.js.map +1 -1
- package/dist/row/styles.js +13 -2
- package/dist/row/styles.js.map +1 -1
- package/dist/segments/field.js +277 -0
- package/dist/segments/field.js.map +1 -0
- package/dist/segments/index.js +574 -0
- package/dist/segments/index.js.map +1 -0
- package/dist/segments/styles.js +24 -0
- package/dist/segments/styles.js.map +1 -0
- package/dist/slider/index.js +145 -0
- package/dist/slider/index.js.map +1 -0
- package/dist/swatch/index.js +56 -0
- package/dist/swatch/index.js.map +1 -0
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -99,9 +99,10 @@ the exception: each measures its own insets through safe-area-context's
|
|
|
99
99
|
`SafeAreaView`, so it clears only the edges it actually meets, and on a device
|
|
100
100
|
it needs no provider. Under React Native Web that view reads the provider too.
|
|
101
101
|
|
|
102
|
-
Gesture Handler 2 and 3 both satisfy the peer. A `Switch`
|
|
103
|
-
Handler's root view around
|
|
104
|
-
`GestureHandlerRootView` for
|
|
102
|
+
Gesture Handler 2 and 3 both satisfy the peer. A `Switch` and a `Slider` each
|
|
103
|
+
draw Gesture Handler's root view around their own track, so an app need not wrap
|
|
104
|
+
itself in `GestureHandlerRootView` for them, and one that already does loses
|
|
105
|
+
nothing.
|
|
105
106
|
|
|
106
107
|
## Routing
|
|
107
108
|
|
|
@@ -33,7 +33,21 @@ function containsIn(locale) {
|
|
|
33
33
|
return false;
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* A match at the start of a text, as React Aria's `useFilter` makes it:
|
|
38
|
+
* compared as {@link containsIn} compares, so `a` starts `AM` and `ä` starts
|
|
39
|
+
* `après`. An empty search starts every text.
|
|
40
|
+
*/
|
|
41
|
+
function startsWithIn(locale) {
|
|
42
|
+
const collator = collatorIn(locale);
|
|
43
|
+
return (text, search) => {
|
|
44
|
+
if (search.length === 0) return true;
|
|
45
|
+
const normalized = search.normalize("NFC");
|
|
46
|
+
const start = text.normalize("NFC").slice(0, normalized.length);
|
|
47
|
+
return collator.compare(normalized, start) === 0;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
36
50
|
//#endregion
|
|
37
|
-
export { AutocompleteContext, containsIn };
|
|
51
|
+
export { AutocompleteContext, containsIn, startsWithIn };
|
|
38
52
|
|
|
39
53
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Node","createContext","AutocompleteValue","inputValue","matches","textValue","node","setInputValue","value","AutocompleteContext","collators","Map","Intl","Collator","collatorIn","locale","made","get","undefined","collator","sensitivity","usage","set","containsIn","length","text","normalize","search","start","slice","compare"],"sources":["../../src/autocomplete/index.ts"],"sourcesContent":["import type { Node } from 'react-stately'\n\nimport { createContext } from 'react'\n\n// What Autocomplete hands the search field and the list inside it. The field\n// shows `inputValue` and reports each edit through `setInputValue`; the list\n// keeps the rows `matches` keeps, which is the autocomplete's filter with\n// what has been typed already in it. Neither part takes it as a prop, so a\n// field and a list written out inside an Autocomplete are wired to it\n// wherever the page puts them.\n\ntype AutocompleteValue = {\n /** What has been typed into the search field. */\n readonly inputValue: string\n /** Whether the list keeps a row, given its text and its entry. */\n readonly matches: (textValue: string, node: Node<unknown>) => boolean\n /** Called by the search field with each edit. */\n readonly setInputValue: (value: string) => void\n}\n\nconst AutocompleteContext = createContext<AutocompleteValue | null>(null)\n\n// One collator per locale an app searches in, made the first time a search\n// asks for it.\nconst collators = new Map<string, Intl.Collator>()\n\n// The collator a search in `locale` compares with, made once per locale.\nfunction collatorIn(locale: string) {\n const made = collators.get(locale)\n\n if (made !== undefined) {\n return made\n }\n\n const collator = new Intl.Collator(locale, {\n sensitivity: 'base',\n usage: 'search',\n })\n collators.set(locale, collator)\n return collator\n}\n\n/**\n * A match anywhere in a row's text, as React Aria's `useFilter` makes it:\n * each slice of the text as long as the search is compared with it by an\n * `Intl.Collator` at base sensitivity, which tells letters apart and nothing\n * else, so case and accents are ignored. Both are normalised first, so an\n * accent typed as a combining mark matches one written as a single letter.\n * An empty search matches every row.\n */\nfunction containsIn(locale: string) {\n const collator = collatorIn(locale)\n\n return (textValue: string, inputValue: string) => {\n if (inputValue.length === 0) {\n return true\n }\n\n const text = textValue.normalize('NFC')\n const search = inputValue.normalize('NFC')\n\n for (let start = 0; start + search.length <= text.length; start++) {\n const slice = text.slice(start, start + search.length)\n\n if (collator.compare(search, slice) === 0) {\n return true\n }\n }\n\n return false\n }\n}\n\nexport { AutocompleteContext, containsIn }\nexport type { AutocompleteValue }\n"],"mappings":";;AAoBA,MAAMS,sBAAsBR,cAAwC,IAAI;AAIxE,MAAMS,4BAAY,IAAIC,IAA2B;AAGjD,SAASG,WAAWC,QAAgB;CAClC,MAAMC,OAAON,UAAUO,IAAIF,MAAM;CAEjC,IAAIC,SAASE,KAAAA,GACX,OAAOF;CAGT,MAAMG,WAAW,IAAIP,KAAKC,SAASE,QAAQ;EACzCK,aAAa;EACbC,OAAO;CACT,CAAC;CACDX,UAAUY,IAAIP,QAAQI,QAAQ;CAC9B,OAAOA;AACT;;;;;;;;;AAUA,SAASI,WAAWR,QAAgB;CAClC,MAAMI,WAAWL,WAAWC,MAAM;CAElC,QAAQV,WAAmBF,eAAuB;EAChD,IAAIA,WAAWqB,WAAW,GACxB,OAAO;EAGT,MAAMC,OAAOpB,UAAUqB,UAAU,KAAK;EACtC,MAAMC,SAASxB,WAAWuB,UAAU,KAAK;EAEzC,KAAK,IAAIE,QAAQ,GAAGA,QAAQD,OAAOH,UAAUC,KAAKD,QAAQI,SAAS;GACjE,MAAMC,QAAQJ,KAAKI,MAAMD,OAAOA,QAAQD,OAAOH,MAAM;GAErD,IAAIL,SAASW,QAAQH,QAAQE,KAAK,MAAM,GACtC,OAAO;EAEX;EAEA,OAAO;CACT;AACF"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Node","createContext","AutocompleteValue","inputValue","matches","textValue","node","setInputValue","value","AutocompleteContext","collators","Map","Intl","Collator","collatorIn","locale","made","get","undefined","collator","sensitivity","usage","set","containsIn","length","text","normalize","search","start","slice","compare","startsWithIn","normalized"],"sources":["../../src/autocomplete/index.ts"],"sourcesContent":["import type { Node } from 'react-stately'\n\nimport { createContext } from 'react'\n\n// What Autocomplete hands the search field and the list inside it. The field\n// shows `inputValue` and reports each edit through `setInputValue`; the list\n// keeps the rows `matches` keeps, which is the autocomplete's filter with\n// what has been typed already in it. Neither part takes it as a prop, so a\n// field and a list written out inside an Autocomplete are wired to it\n// wherever the page puts them.\n\ntype AutocompleteValue = {\n /** What has been typed into the search field. */\n readonly inputValue: string\n /** Whether the list keeps a row, given its text and its entry. */\n readonly matches: (textValue: string, node: Node<unknown>) => boolean\n /** Called by the search field with each edit. */\n readonly setInputValue: (value: string) => void\n}\n\nconst AutocompleteContext = createContext<AutocompleteValue | null>(null)\n\n// One collator per locale an app searches in, made the first time a search\n// asks for it.\nconst collators = new Map<string, Intl.Collator>()\n\n// The collator a search in `locale` compares with, made once per locale.\nfunction collatorIn(locale: string) {\n const made = collators.get(locale)\n\n if (made !== undefined) {\n return made\n }\n\n const collator = new Intl.Collator(locale, {\n sensitivity: 'base',\n usage: 'search',\n })\n collators.set(locale, collator)\n return collator\n}\n\n/**\n * A match anywhere in a row's text, as React Aria's `useFilter` makes it:\n * each slice of the text as long as the search is compared with it by an\n * `Intl.Collator` at base sensitivity, which tells letters apart and nothing\n * else, so case and accents are ignored. Both are normalised first, so an\n * accent typed as a combining mark matches one written as a single letter.\n * An empty search matches every row.\n */\nfunction containsIn(locale: string) {\n const collator = collatorIn(locale)\n\n return (textValue: string, inputValue: string) => {\n if (inputValue.length === 0) {\n return true\n }\n\n const text = textValue.normalize('NFC')\n const search = inputValue.normalize('NFC')\n\n for (let start = 0; start + search.length <= text.length; start++) {\n const slice = text.slice(start, start + search.length)\n\n if (collator.compare(search, slice) === 0) {\n return true\n }\n }\n\n return false\n }\n}\n\n/**\n * A match at the start of a text, as React Aria's `useFilter` makes it:\n * compared as {@link containsIn} compares, so `a` starts `AM` and `ä` starts\n * `après`. An empty search starts every text.\n */\nfunction startsWithIn(locale: string) {\n const collator = collatorIn(locale)\n\n return (text: string, search: string) => {\n if (search.length === 0) {\n return true\n }\n\n const normalized = search.normalize('NFC')\n const start = text.normalize('NFC').slice(0, normalized.length)\n\n return collator.compare(normalized, start) === 0\n }\n}\n\nexport { AutocompleteContext, containsIn, startsWithIn }\nexport type { AutocompleteValue }\n"],"mappings":";;AAoBA,MAAMS,sBAAsBR,cAAwC,IAAI;AAIxE,MAAMS,4BAAY,IAAIC,IAA2B;AAGjD,SAASG,WAAWC,QAAgB;CAClC,MAAMC,OAAON,UAAUO,IAAIF,MAAM;CAEjC,IAAIC,SAASE,KAAAA,GACX,OAAOF;CAGT,MAAMG,WAAW,IAAIP,KAAKC,SAASE,QAAQ;EACzCK,aAAa;EACbC,OAAO;CACT,CAAC;CACDX,UAAUY,IAAIP,QAAQI,QAAQ;CAC9B,OAAOA;AACT;;;;;;;;;AAUA,SAASI,WAAWR,QAAgB;CAClC,MAAMI,WAAWL,WAAWC,MAAM;CAElC,QAAQV,WAAmBF,eAAuB;EAChD,IAAIA,WAAWqB,WAAW,GACxB,OAAO;EAGT,MAAMC,OAAOpB,UAAUqB,UAAU,KAAK;EACtC,MAAMC,SAASxB,WAAWuB,UAAU,KAAK;EAEzC,KAAK,IAAIE,QAAQ,GAAGA,QAAQD,OAAOH,UAAUC,KAAKD,QAAQI,SAAS;GACjE,MAAMC,QAAQJ,KAAKI,MAAMD,OAAOA,QAAQD,OAAOH,MAAM;GAErD,IAAIL,SAASW,QAAQH,QAAQE,KAAK,MAAM,GACtC,OAAO;EAEX;EAEA,OAAO;CACT;AACF;;;;;;AAOA,SAASE,aAAahB,QAAgB;CACpC,MAAMI,WAAWL,WAAWC,MAAM;CAElC,QAAQU,MAAcE,WAAmB;EACvC,IAAIA,OAAOH,WAAW,GACpB,OAAO;EAGT,MAAMQ,aAAaL,OAAOD,UAAU,KAAK;EACzC,MAAME,QAAQH,KAAKC,UAAU,KAAK,CAAC,CAACG,MAAM,GAAGG,WAAWR,MAAM;EAE9D,OAAOL,SAASW,QAAQE,YAAYJ,KAAK,MAAM;CACjD;AACF"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useTheme } from "../theme/scope.js";
|
|
2
|
+
import { toggleState } from "./index.js";
|
|
3
|
+
import { StyleSheet } from "react-native";
|
|
4
|
+
import { Easing, useAnimatedStyle, useReducedMotion, withTiming } from "react-native-reanimated";
|
|
5
|
+
import { createContext } from "react";
|
|
6
|
+
//#region src/button/group.ts
|
|
7
|
+
const ButtonGroupItemContext = createContext(null);
|
|
8
|
+
const groupStyles = StyleSheet.create({ target: { minWidth: 48 } });
|
|
9
|
+
/**
|
|
10
|
+
* The corners the group around a button gives it, from the button's press
|
|
11
|
+
* feedback and whether it is selected. Nothing outside a group that shapes
|
|
12
|
+
* its buttons, which leaves the button its own shape.
|
|
13
|
+
*/
|
|
14
|
+
function groupCornersOf(group, { hovered, pressed, ring }, selected) {
|
|
15
|
+
return group?.corners?.({
|
|
16
|
+
active: hovered || pressed || ring !== void 0,
|
|
17
|
+
pressed,
|
|
18
|
+
selected
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The press handlers a button in a group that tracks presses takes: its own,
|
|
23
|
+
* with the group told first, given the width `groupWidthThen` last recorded.
|
|
24
|
+
* Its own alone anywhere else.
|
|
25
|
+
*/
|
|
26
|
+
function groupPressHandlers(group, width, onPressIn, onPressOut) {
|
|
27
|
+
const start = group?.onPressStart;
|
|
28
|
+
if (start === void 0) return {
|
|
29
|
+
onPressIn,
|
|
30
|
+
onPressOut
|
|
31
|
+
};
|
|
32
|
+
const end = group?.onPressEnd;
|
|
33
|
+
return {
|
|
34
|
+
onPressIn: (event) => {
|
|
35
|
+
start(width.get());
|
|
36
|
+
onPressIn?.(event);
|
|
37
|
+
},
|
|
38
|
+
onPressOut: (event) => {
|
|
39
|
+
end?.();
|
|
40
|
+
onPressOut?.(event);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Where a button with this `id` stands in the selecting group around it: how
|
|
46
|
+
* it is announced, whether it is chosen, and what a press on it changes.
|
|
47
|
+
* Nothing outside a group that selects, or for a button without an `id`,
|
|
48
|
+
* which has no key to be chosen by.
|
|
49
|
+
*/
|
|
50
|
+
function groupSelectionOf(group, id) {
|
|
51
|
+
const selection = group?.selection;
|
|
52
|
+
if (selection === void 0) return;
|
|
53
|
+
const { single, state } = selection;
|
|
54
|
+
const selected = id !== void 0 && state.selectedKeys.has(id);
|
|
55
|
+
return {
|
|
56
|
+
accessibility: single ? {
|
|
57
|
+
"aria-checked": selected,
|
|
58
|
+
role: "radio"
|
|
59
|
+
} : {
|
|
60
|
+
...toggleState(selected),
|
|
61
|
+
role: "button"
|
|
62
|
+
},
|
|
63
|
+
selected,
|
|
64
|
+
toggle: () => {
|
|
65
|
+
if (id !== void 0) state.toggleKey(id);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The call site's own `onLayout`, after recording the button's width for the
|
|
71
|
+
* group to widen it from, in a group that tracks presses. The call site's
|
|
72
|
+
* own alone anywhere else.
|
|
73
|
+
*/
|
|
74
|
+
function groupWidthThen(group, width, onLayout) {
|
|
75
|
+
if (group?.onPressStart === void 0) return onLayout;
|
|
76
|
+
return (event) => {
|
|
77
|
+
width.set(event.nativeEvent.layout.width);
|
|
78
|
+
onLayout?.(event);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The style a standard group eases a button's width by: `property` at `base`
|
|
83
|
+
* at rest, moved by the group's shift while a press is held, over the motion
|
|
84
|
+
* tokens' short duration on the emphasised curve, as the corner eases, and at
|
|
85
|
+
* once under reduced motion. Padding takes half the shift on either side, as
|
|
86
|
+
* `paddingLeft` and `paddingRight` rather than `paddingHorizontal`: on web
|
|
87
|
+
* Reanimated writes an animated style straight onto the element, where a
|
|
88
|
+
* React Native shorthand is no CSS property and is dropped. The two sides are
|
|
89
|
+
* equal, so they need no direction, and on native either beats the
|
|
90
|
+
* `paddingHorizontal` a button rests at. Nothing outside a group that tracks
|
|
91
|
+
* presses, which leaves the button's own size alone.
|
|
92
|
+
*/
|
|
93
|
+
function useGroupShift(group, base, property) {
|
|
94
|
+
const { motion } = useTheme();
|
|
95
|
+
const reduceMotion = useReducedMotion();
|
|
96
|
+
const shift = group?.shift ?? 0;
|
|
97
|
+
const target = base + (property === "width" ? shift : shift / 2);
|
|
98
|
+
const timing = {
|
|
99
|
+
duration: motion.durationShort2,
|
|
100
|
+
easing: Easing.bezier(...motion.easingEmphasized)
|
|
101
|
+
};
|
|
102
|
+
const style = useAnimatedStyle(() => {
|
|
103
|
+
"worklet";
|
|
104
|
+
const value = reduceMotion ? target : withTiming(target, timing);
|
|
105
|
+
return property === "width" ? { width: value } : {
|
|
106
|
+
paddingLeft: value,
|
|
107
|
+
paddingRight: value
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
return group?.onPressStart === void 0 ? void 0 : style;
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { ButtonGroupItemContext, groupCornersOf, groupPressHandlers, groupSelectionOf, groupStyles, groupWidthThen, useGroupShift };
|
|
114
|
+
|
|
115
|
+
//# sourceMappingURL=group.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"group.js","names":["GestureResponderEvent","LayoutChangeEvent","PressableProps","StyleProp","ViewStyle","SharedValue","ToggleGroupState","createContext","StyleSheet","Easing","useAnimatedStyle","useReducedMotion","withTiming","ControlSize","PressFeedback","toggleState","TARGET_SIZE","useTheme","ButtonGroupItem","corners","state","GroupButtonState","GroupCorners","held","isDisabled","keepsTarget","onPressEnd","onPressStart","width","selection","single","shift","size","style","active","pressed","selected","end","start","GroupMembership","accessibility","role","toggle","ButtonGroupItemContext","groupStyles","create","target","minWidth","groupCornersOf","group","hovered","ring","undefined","groupPressHandlers","onPressIn","onPressOut","event","get","groupSelectionOf","id","selectedKeys","has","toggleKey","groupWidthThen","onLayout","set","nativeEvent","layout","useGroupShift","base","property","motion","reduceMotion","timing","duration","durationShort2","easing","bezier","easingEmphasized","value","paddingLeft","paddingRight"],"sources":["../../src/button/group.ts"],"sourcesContent":["import type {\n GestureResponderEvent,\n LayoutChangeEvent,\n PressableProps,\n StyleProp,\n ViewStyle,\n} from 'react-native'\nimport type { SharedValue } from 'react-native-reanimated'\nimport type { ToggleGroupState } from 'react-stately/useToggleGroupState'\n\nimport { createContext } from 'react'\nimport { StyleSheet } from 'react-native'\nimport {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { ControlSize } from '.'\nimport type { PressFeedback } from '../press'\n\nimport { toggleState } from '.'\nimport { TARGET_SIZE } from '../press'\nimport { useTheme } from '../theme/scope'\n\n// What a group hands each Button or IconButton in it, as web's\n// src/button/context.ts does, for ButtonGroup's two variants and\n// SplitButton's two halves: the group's size, whether it is disabled, its\n// selection, the corners it gives the button, a standard group's part in a\n// press, and a split button's paddings and held press. Apart from\n// src/components, which holds the public components alone, and from\n// ./index.ts, which the group reads too.\n\ntype ButtonGroupItem = {\n /**\n * The corners a connected group gives the button for its state, which take\n * the place of its own shape.\n */\n readonly corners?: (state: GroupButtonState) => GroupCorners\n /**\n * Whether the button draws its pressed layer though nothing presses it, as\n * a split button's menu half does while its menu is open. Button reads\n * it, which is what a split button's halves are.\n */\n readonly held?: boolean\n /** Whether the group is disabled, which disables every button in it. */\n readonly isDisabled: boolean\n /**\n * Whether the button keeps the 48dp touch target as its least width, as\n * the two smallest do in a connected group.\n */\n readonly keepsTarget: boolean\n /** Called as a press on the button ends, so the group lets go of it. */\n readonly onPressEnd?: () => void\n /**\n * Called as a press on the button starts, with its width, so a standard\n * group can widen it and narrow its neighbours.\n */\n readonly onPressStart?: (width: number) => void\n /**\n * The group's selection, by the buttons' `id`s, when it selects: `single`\n * for one at a time, as a radio group, or several, as toggles.\n */\n readonly selection?: {\n readonly single: boolean\n readonly state: ToggleGroupState\n }\n /**\n * How far the button's width moves while it or a neighbour is pressed, in\n * dp: the pressed button's gain, or a neighbour's loss as a negative. Zero\n * at rest.\n */\n readonly shift: number\n /** The group's size, which a button takes unless it names its own. */\n readonly size: ControlSize | undefined\n /**\n * A style laid over the button's own size, before its corners: a split\n * button's paddings. Button reads it, as it reads `held`.\n */\n readonly style?: StyleProp<ViewStyle>\n}\n\n/** What a group shapes a button's corners from. */\ntype GroupButtonState = {\n /**\n * Whether a pointer is over the button, its focus ring shows or it is\n * pressed, which a split button opens its inner corners out for.\n */\n readonly active: boolean\n readonly pressed: boolean\n readonly selected: boolean\n}\n\n/** A button's corners on its two sides, in dp: start and end. */\ntype GroupCorners = { readonly end: number; readonly start: number }\n\n/** How a button in a selecting group is announced, and whether it is chosen. */\ntype GroupMembership = {\n readonly accessibility: {\n readonly 'aria-checked'?: boolean\n readonly 'aria-pressed'?: boolean\n readonly 'aria-selected'?: boolean\n readonly role: 'button' | 'radio'\n }\n readonly selected: boolean\n readonly toggle: () => void\n}\n\nconst ButtonGroupItemContext = createContext<ButtonGroupItem | null>(null)\n\nconst groupStyles = StyleSheet.create({\n // The touch target the two smallest sizes keep in a connected group.\n target: {\n minWidth: TARGET_SIZE,\n },\n})\n\n/**\n * The corners the group around a button gives it, from the button's press\n * feedback and whether it is selected. Nothing outside a group that shapes\n * its buttons, which leaves the button its own shape.\n */\nfunction groupCornersOf(\n group: ButtonGroupItem | null,\n { hovered, pressed, ring }: PressFeedback,\n selected: boolean,\n): GroupCorners | undefined {\n return group?.corners?.({\n active: hovered || pressed || ring !== undefined,\n pressed,\n selected,\n })\n}\n\n/**\n * The press handlers a button in a group that tracks presses takes: its own,\n * with the group told first, given the width `groupWidthThen` last recorded.\n * Its own alone anywhere else.\n */\nfunction groupPressHandlers(\n group: ButtonGroupItem | null,\n width: SharedValue<number>,\n onPressIn: PressableProps['onPressIn'],\n onPressOut: PressableProps['onPressOut'],\n) {\n const start = group?.onPressStart\n\n if (start === undefined) {\n return { onPressIn, onPressOut }\n }\n\n const end = group?.onPressEnd\n\n return {\n onPressIn: (event: GestureResponderEvent) => {\n start(width.get())\n onPressIn?.(event)\n },\n onPressOut: (event: GestureResponderEvent) => {\n end?.()\n onPressOut?.(event)\n },\n }\n}\n\n/**\n * Where a button with this `id` stands in the selecting group around it: how\n * it is announced, whether it is chosen, and what a press on it changes.\n * Nothing outside a group that selects, or for a button without an `id`,\n * which has no key to be chosen by.\n */\nfunction groupSelectionOf(\n group: ButtonGroupItem | null,\n id: string | undefined,\n): GroupMembership | undefined {\n const selection = group?.selection\n\n if (selection === undefined) {\n return undefined\n }\n\n const { single, state } = selection\n const selected = id !== undefined && state.selectedKeys.has(id)\n\n return {\n accessibility: single\n ? { 'aria-checked': selected, role: 'radio' }\n : { ...toggleState(selected), role: 'button' },\n selected,\n toggle: () => {\n if (id !== undefined) {\n state.toggleKey(id)\n }\n },\n }\n}\n\n/**\n * The call site's own `onLayout`, after recording the button's width for the\n * group to widen it from, in a group that tracks presses. The call site's\n * own alone anywhere else.\n */\nfunction groupWidthThen(\n group: ButtonGroupItem | null,\n width: SharedValue<number>,\n onLayout: PressableProps['onLayout'],\n): PressableProps['onLayout'] {\n if (group?.onPressStart === undefined) {\n return onLayout\n }\n\n return (event: LayoutChangeEvent) => {\n width.set(event.nativeEvent.layout.width)\n onLayout?.(event)\n }\n}\n\n/**\n * The style a standard group eases a button's width by: `property` at `base`\n * at rest, moved by the group's shift while a press is held, over the motion\n * tokens' short duration on the emphasised curve, as the corner eases, and at\n * once under reduced motion. Padding takes half the shift on either side, as\n * `paddingLeft` and `paddingRight` rather than `paddingHorizontal`: on web\n * Reanimated writes an animated style straight onto the element, where a\n * React Native shorthand is no CSS property and is dropped. The two sides are\n * equal, so they need no direction, and on native either beats the\n * `paddingHorizontal` a button rests at. Nothing outside a group that tracks\n * presses, which leaves the button's own size alone.\n */\nfunction useGroupShift(\n group: ButtonGroupItem | null,\n base: number,\n property: 'padding' | 'width',\n) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const shift = group?.shift ?? 0\n const target = base + (property === 'width' ? shift : shift / 2)\n const timing = {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasized),\n }\n const style = useAnimatedStyle(() => {\n 'worklet'\n const value = reduceMotion ? target : withTiming(target, timing)\n\n return property === 'width'\n ? { width: value }\n : { paddingLeft: value, paddingRight: value }\n })\n\n return group?.onPressStart === undefined ? undefined : style\n}\n\nexport {\n ButtonGroupItemContext,\n groupCornersOf,\n groupPressHandlers,\n groupSelectionOf,\n groupStyles,\n groupWidthThen,\n useGroupShift,\n}\nexport type { ButtonGroupItem, GroupButtonState, GroupCorners }\n"],"mappings":";;;;;;AA6GA,MAAM2C,yBAAyBpC,cAAsC,IAAI;AAEzE,MAAMqC,cAAcpC,WAAWqC,OAAO,EAEpCC,QAAQ,EACNC,UAAAA,GACF,EACF,CAAC;;;;;;AAOD,SAASC,eACPC,OACA,EAAEC,SAASf,SAASgB,QACpBf,UAC0B;CAC1B,OAAOa,OAAO9B,UAAU;EACtBe,QAAQgB,WAAWf,WAAWgB,SAASC,KAAAA;EACvCjB;EACAC;CACF,CAAC;AACH;;;;;;AAOA,SAASiB,mBACPJ,OACArB,OACA0B,WACAC,YACA;CACA,MAAMjB,QAAQW,OAAOtB;CAErB,IAAIW,UAAUc,KAAAA,GACZ,OAAO;EAAEE;EAAWC;CAAW;CAGjC,MAAMlB,MAAMY,OAAOvB;CAEnB,OAAO;EACL4B,YAAYE,UAAiC;GAC3ClB,MAAMV,MAAM6B,IAAI,CAAC;GACjBH,YAAYE,KAAK;EACnB;EACAD,aAAaC,UAAiC;GAC5CnB,MAAM;GACNkB,aAAaC,KAAK;EACpB;CACF;AACF;;;;;;;AAQA,SAASE,iBACPT,OACAU,IAC6B;CAC7B,MAAM9B,YAAYoB,OAAOpB;CAEzB,IAAIA,cAAcuB,KAAAA,GAChB;CAGF,MAAM,EAAEtB,QAAQV,UAAUS;CAC1B,MAAMO,WAAWuB,OAAOP,KAAAA,KAAahC,MAAMwC,aAAaC,IAAIF,EAAE;CAE9D,OAAO;EACLnB,eAAeV,SACX;GAAE,gBAAgBM;GAAUK,MAAM;EAAQ,IAC1C;GAAE,GAAG1B,YAAYqB,QAAQ;GAAGK,MAAM;EAAS;EAC/CL;EACAM,cAAc;GACZ,IAAIiB,OAAOP,KAAAA,GACThC,MAAM0C,UAAUH,EAAE;EAEtB;CACF;AACF;;;;;;AAOA,SAASI,eACPd,OACArB,OACAoC,UAC4B;CAC5B,IAAIf,OAAOtB,iBAAiByB,KAAAA,GAC1B,OAAOY;CAGT,QAAQR,UAA6B;EACnC5B,MAAMqC,IAAIT,MAAMU,YAAYC,OAAOvC,KAAK;EACxCoC,WAAWR,KAAK;CAClB;AACF;;;;;;;;;;;;;AAcA,SAAAY,cAAAnB,OAAAoB,MAAAC,UAAA;CAKE,MAAA,EAAAC,WAAmBtD,SAAS;CAC5B,MAAAuD,eAAqB7D,iBAAiB;CACtC,MAAAoB,QAAckB,OAAKlB,SAAL;CACd,MAAAe,SAAeuB,QAAQC,aAAa,UAAbvC,QAA+BA,QAAQ;CAC9D,MAAA0C,SAAe;EAAAC,UACHH,OAAMI;EAAeC,QACvBnE,OAAMoE,OAAO,GAAIN,OAAMO,gBAAiB;CAClD;CACA,MAAA7C,QAAcvB,uBAAiB;EAAA;EAE7B,MAAAqE,QAAcP,eAAA1B,SAAwBlC,WAAWkC,QAAQ2B,MAAM;EAAC,OAEzDH,aAAa,UAAb,EAAA1C,OACMmD,MACiC,IAFvC;GAAAC,aAEYD;GAAKE,cAAgBF;EAAM;CAAC,CAChD;CAAC,OAEK9B,OAAKtB,iBAAmByB,KAAAA,IAAxBA,KAAAA,IAAAnB;AAAqD"}
|
package/dist/button/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./group.js";
|
|
1
2
|
import { HostInstance, LayoutChangeEvent, PressableProps } from "react-native";
|
|
2
3
|
import { Ref } from "react";
|
|
3
4
|
//#region src/button/index.d.ts
|
|
@@ -83,6 +84,8 @@ type ButtonToggleForm = ButtonPending & {
|
|
|
83
84
|
readonly slot?: never;
|
|
84
85
|
readonly type?: never;
|
|
85
86
|
};
|
|
87
|
+
/** The five control heights both pages give, by the names both props take. */
|
|
88
|
+
type ControlSize = 'lg' | 'md' | 'xl' | 'xs' | 'xxl';
|
|
86
89
|
//#endregion
|
|
87
|
-
export type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm };
|
|
90
|
+
export type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm, ControlSize };
|
|
88
91
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/button/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { useTheme } from "../theme/scope.js";
|
|
|
2
2
|
import { useOpenLink } from "../router/index.js";
|
|
3
3
|
import { FormContext } from "../form/index.js";
|
|
4
4
|
import { refsOf } from "../ref/index.js";
|
|
5
|
-
import { ButtonSlotContext } from "./slots.js";
|
|
5
|
+
import { ButtonSlotContext, ButtonTooltipContext } from "./slots.js";
|
|
6
6
|
import { c } from "react/compiler-runtime";
|
|
7
7
|
import { Platform, Pressable, StyleSheet } from "react-native";
|
|
8
8
|
import Animated, { Easing, useAnimatedStyle, useReducedMotion, useSharedValue, withTiming } from "react-native-reanimated";
|
|
@@ -129,17 +129,23 @@ function useButtonAction(t0) {
|
|
|
129
129
|
/**
|
|
130
130
|
* The corner a button eases to, over the motion tokens' short duration on the
|
|
131
131
|
* emphasised curve, and at once for a reader who asks the device for reduced
|
|
132
|
-
* motion
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
132
|
+
* motion: one radius for all four, or a connected ButtonGroup's two, one for
|
|
133
|
+
* the start side and one for the end, which follow the layout's direction on
|
|
134
|
+
* native. On web Reanimated writes an animated style straight onto the
|
|
135
|
+
* element, where only the physical corners are CSS properties, so the start
|
|
136
|
+
* side is the left there, as the web Storybook lays a row out.
|
|
137
|
+
* The pill is the radius scale's `pill`, far larger than any button, so the
|
|
138
|
+
* ease runs from half the button's measured height rather than from that:
|
|
139
|
+
* otherwise the corner would hold the pill for most of the ease and jump at
|
|
140
|
+
* its end. The `onLayout` it hands back measures the button, then calls the
|
|
141
|
+
* call site's own.
|
|
137
142
|
*/
|
|
138
143
|
function useCorner(radius, onLayout) {
|
|
139
144
|
const $ = c(11);
|
|
140
145
|
const { motion } = useTheme();
|
|
141
146
|
const reduceMotion = useReducedMotion();
|
|
142
147
|
const height = useSharedValue(0);
|
|
148
|
+
const web = Platform.OS === "web";
|
|
143
149
|
const t0 = motion.durationShort2;
|
|
144
150
|
let t1;
|
|
145
151
|
if ($[0] !== motion.easingEmphasized) {
|
|
@@ -161,8 +167,23 @@ function useCorner(radius, onLayout) {
|
|
|
161
167
|
const corner = useAnimatedStyle(() => {
|
|
162
168
|
"worklet";
|
|
163
169
|
const measured = height.get();
|
|
164
|
-
const
|
|
165
|
-
|
|
170
|
+
const ease = (value) => {
|
|
171
|
+
const target = measured > 0 ? Math.min(value, measured / 2) : value;
|
|
172
|
+
return reduceMotion ? target : withTiming(target, timing);
|
|
173
|
+
};
|
|
174
|
+
if (typeof radius === "number") return { borderRadius: ease(radius) };
|
|
175
|
+
if (web) return {
|
|
176
|
+
borderBottomLeftRadius: ease(radius.start),
|
|
177
|
+
borderBottomRightRadius: ease(radius.end),
|
|
178
|
+
borderTopLeftRadius: ease(radius.start),
|
|
179
|
+
borderTopRightRadius: ease(radius.end)
|
|
180
|
+
};
|
|
181
|
+
return {
|
|
182
|
+
borderBottomEndRadius: ease(radius.end),
|
|
183
|
+
borderBottomStartRadius: ease(radius.start),
|
|
184
|
+
borderTopEndRadius: ease(radius.end),
|
|
185
|
+
borderTopStartRadius: ease(radius.start)
|
|
186
|
+
};
|
|
166
187
|
});
|
|
167
188
|
let t3;
|
|
168
189
|
if ($[5] !== height || $[6] !== onLayout) {
|
|
@@ -189,26 +210,27 @@ function useCorner(radius, onLayout) {
|
|
|
189
210
|
* be placed against, alongside the call site's own `ref`, `aria-expanded`
|
|
190
211
|
* says whether the overlay is open, and the overlay's hover and long press
|
|
191
212
|
* handlers run after the call site's. A button filling no such slot, or a
|
|
192
|
-
* link, keeps its own props untouched.
|
|
213
|
+
* link, keeps its own props untouched. A button inside a `Tooltip` takes the
|
|
214
|
+
* tooltip's node, hover, press and long press handlers on top of either, and
|
|
215
|
+
* its label as the hint, unless the call site gave a hint of its own.
|
|
193
216
|
*/
|
|
194
|
-
function useSlotTrigger({ delayLongPress, href, onHoverIn, onHoverOut, onLongPress, ref, slot }) {
|
|
217
|
+
function useSlotTrigger({ accessibilityHint, delayLongPress, href, onHoverIn, onHoverOut, onLongPress, onPressIn, onPressOut, ref, slot }) {
|
|
195
218
|
const slots = use(ButtonSlotContext);
|
|
219
|
+
const tooltip = use(ButtonTooltipContext) ?? void 0;
|
|
196
220
|
const trigger = href === void 0 && slot === void 0 ? slots?.trigger : void 0;
|
|
197
221
|
return {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
onHoverOut: andThen(onHoverOut, trigger?.onHoverOut),
|
|
208
|
-
onLongPress: andThen(onLongPress, trigger?.onLongPress)
|
|
222
|
+
accessibilityHint: accessibilityHint ?? tooltip?.hint,
|
|
223
|
+
...trigger === void 0 ? void 0 : { "aria-expanded": trigger.expanded },
|
|
224
|
+
delayLongPress: trigger?.delayLongPress ?? tooltip?.delayLongPress ?? delayLongPress,
|
|
225
|
+
onHoverIn: andThen(andThen(onHoverIn, trigger?.onHoverIn), tooltip?.onHoverIn),
|
|
226
|
+
onHoverOut: andThen(andThen(onHoverOut, trigger?.onHoverOut), tooltip?.onHoverOut),
|
|
227
|
+
onLongPress: andThen(andThen(onLongPress, trigger?.onLongPress), tooltip?.onLongPress),
|
|
228
|
+
onPressIn: andThen(onPressIn, tooltip?.onPressIn),
|
|
229
|
+
onPressOut: andThen(onPressOut, tooltip?.onPressOut),
|
|
230
|
+
ref: trigger === void 0 && tooltip === void 0 ? ref : refsOf(trigger?.ref, tooltip?.ref, ref)
|
|
209
231
|
};
|
|
210
232
|
}
|
|
211
233
|
//#endregion
|
|
212
|
-
export { AnimatedPressable, HEIGHTS, OUTLINE_WIDTHS, PRESSED_CORNERS, buttonStyles, toggleState, useButtonAction, useCorner, useSlotTrigger };
|
|
234
|
+
export { AnimatedPressable, HEIGHTS, OUTLINE_WIDTHS, PRESSED_CORNERS, buttonStyles, pressThen, toggleState, useButtonAction, useCorner, useSlotTrigger };
|
|
213
235
|
|
|
214
236
|
//# sourceMappingURL=index.js.map
|
package/dist/button/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Ref","GestureResponderEvent","HostInstance","LayoutChangeEvent","PressableProps","SharedValue","use","useState","Platform","Pressable","StyleSheet","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","ResolvedTheme","FormContext","refsOf","useOpenLink","useTheme","ButtonSlotContext","ButtonLinkForm","defaultSelected","href","isPending","isSelected","onChange","pendingLabel","slot","type","ButtonPending","ButtonPressForm","ButtonToggleForm","ControlSize","SlotTriggerProps","delayLongPress","onHoverIn","onHoverOut","onLongPress","ref","ToggleState","HEIGHTS","lg","md","xl","xs","xxl","const","Record","OUTLINE_WIDTHS","Readonly","PRESSED_CORNERS","AnimatedPressable","createAnimatedComponent","toggleState","select","selected","default","web","buttonStyles","create","clip","bottom","left","overflow","pointerEvents","position","right","top","andThen","own","event","E","undefined","measureThen","height","onLayout","set","nativeEvent","layout","pressThen","onPress","then","useButtonAction","t0","$","_c","isToggle","openLink","form","slots","ownSelected","setOwnSelected","filled","t1","follow","requestSubmit","t2","t3","useCorner","radius","motion","reduceMotion","durationShort2","easingEmphasized","bezier","duration","easing","timing","corner","measured","get","target","Math","min","borderRadius","t4","useSlotTrigger","trigger","expanded","_c"],"sources":["../../src/button/index.ts"],"sourcesContent":["import type { Ref } from 'react'\nimport type {\n GestureResponderEvent,\n HostInstance,\n LayoutChangeEvent,\n PressableProps,\n} from 'react-native'\nimport type { SharedValue } from 'react-native-reanimated'\n\nimport { use, useState } from 'react'\nimport { Platform, Pressable, StyleSheet } from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { ResolvedTheme } from '../theme/scope'\n\nimport { FormContext } from '../form'\nimport { refsOf } from '../ref'\nimport { useOpenLink } from '../router'\nimport { useTheme } from '../theme/scope'\nimport { ButtonSlotContext } from './slots'\n\n// What every button does past its own styles, shared by Button and\n// IconButton as web's src/button is: the three forms a button takes, the\n// toggle's state and how it is announced, what a press does after the call\n// site's own `onPress`, a submit button's included, and what the slot it\n// fills in an overlay adds to it, the corner that eases between shapes, and\n// the metrics\n// the two components' pages give alike, since the two share their five\n// heights so that they line up in a row. What a pending button draws and\n// says is ./pending.tsx.\n\n/**\n * A plain button, a link, or a toggle, of which a call site writes exactly\n * one. Each form names the props of the other two as `never`, so a toggle\n * given `href` is a compile error rather than a prop that silently does\n * nothing.\n */\ntype ButtonLinkForm = {\n readonly defaultSelected?: never\n /**\n * Where the button leads. Given one, a press opens it through the\n * `RouterProvider` around the button, or through the platform for a URL\n * with a scheme of its own, and the button is announced as a link.\n */\n readonly href: string\n readonly isPending?: never\n readonly isSelected?: never\n readonly onChange?: never\n readonly pendingLabel?: never\n readonly slot?: never\n readonly type?: never\n}\n\n/**\n * Whether the button's action is under way, which the plain button and the\n * toggle both take and the link does not.\n */\ntype ButtonPending = {\n /**\n * Shows that the button's action is under way: a ring takes the place of\n * the label and the icon, which keep their space, and the button keeps its\n * focus and takes no press until it clears. A toggle may be pending, for a\n * state saved somewhere slow.\n * @default false\n */\n readonly isPending?: boolean\n /**\n * The name of the ring shown while the button is pending, for a screen\n * reader. The content it replaces is hidden while it shows. Left out, it is\n * the word for it in the `I18nProvider`'s locale, or the device's outside\n * one, \"Loading\" in English.\n */\n readonly pendingLabel?: string\n}\n\ntype ButtonPressForm = ButtonPending & {\n readonly defaultSelected?: never\n readonly href?: never\n readonly isSelected?: never\n readonly onChange?: never\n /**\n * `close` closes the overlay the button is in, as web's React Aria slot of\n * that name does: a `Dialog` given one inside its content closes when it is\n * pressed, after the button's own `onPress`.\n */\n readonly slot?: 'close'\n /**\n * What a press does past `onPress`. `submit` submits the `Form` the button\n * is in, as a submit button does on web; `button` does nothing more.\n * @default 'button'\n */\n readonly type?: 'button' | 'submit'\n}\n\ntype ButtonToggleForm = ButtonPending & {\n /**\n * Whether a toggle starts selected, when it keeps its own state. Passing\n * this, `isSelected` or `onChange` is what makes the button a toggle.\n */\n readonly defaultSelected?: boolean\n readonly href?: never\n /**\n * Whether a toggle is selected, when the call site holds the state. Pass\n * it with `onChange`; passing this, `defaultSelected` or `onChange` is what\n * makes the button a toggle.\n */\n readonly isSelected?: boolean\n /**\n * Called with the new state when a toggle is pressed, after `onPress`.\n * Passing this, `isSelected` or `defaultSelected` is what makes the button\n * a toggle.\n */\n readonly onChange?: (isSelected: boolean) => void\n readonly slot?: never\n readonly type?: never\n}\n\n/** The five control heights both pages give, by the names both props take. */\ntype ControlSize = 'lg' | 'md' | 'xl' | 'xs' | 'xxl'\n\n/** How a toggle reports its state to a screen reader. */\n// What `useSlotTrigger` hands the Pressable, spelled out because the\n// composed ref's inferred type names a private React Native module, which a\n// declaration file cannot reach.\ntype SlotTriggerProps = {\n readonly 'aria-expanded'?: boolean\n readonly delayLongPress: PressableProps['delayLongPress']\n readonly onHoverIn: PressableProps['onHoverIn']\n readonly onHoverOut: PressableProps['onHoverOut']\n readonly onLongPress: PressableProps['onLongPress']\n readonly ref: Ref<HostInstance> | undefined\n}\n\ntype ToggleState = {\n readonly 'aria-pressed'?: boolean\n readonly 'aria-selected'?: boolean\n}\n\n// The control height each size reads: the page's XS, S, M, L and XL, which\n// the theme's sizing scale names one step apart from this prop.\nconst HEIGHTS = {\n lg: 'controlLg',\n md: 'controlSm',\n xl: 'controlXl',\n xs: 'controlXs',\n xxl: 'controlXxl',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['sizing']>\n\n// The outlined rule thickens with the size, as the buttons page's size tokens\n// have it: 1dp up to M, 2dp at L, 3dp at XL. The icon buttons page keeps its\n// widths in a token set its widget will not open, so the icon button takes\n// these, which a 96dp icon button beside a 96dp outlined button needs anyway.\nconst OUTLINE_WIDTHS: Readonly<Record<ControlSize, number>> = {\n lg: 1,\n md: 1,\n xl: 2,\n xs: 1,\n xxl: 3,\n}\n\n// The corner each size presses to, which both pages give alike.\nconst PRESSED_CORNERS = {\n lg: 'md',\n md: 'sm',\n xl: 'lg',\n xs: 'sm',\n xxl: 'lg',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['radii']>\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable)\n\n// Web's toggle reports `aria-pressed`, which React Native Web writes onto the\n// element. React Native has no pressed state to report, and reports the\n// selected one instead, which VoiceOver and TalkBack both read out.\nconst toggleState = Platform.select<(selected: boolean) => ToggleState>({\n default: (selected) => ({ 'aria-selected': selected }),\n web: (selected) => ({ 'aria-pressed': selected }),\n})\n\nconst buttonStyles = StyleSheet.create({\n // Clips the state layer and the ripple to the corner as it morphs, and\n // takes no touches.\n clip: {\n bottom: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n// A handler that runs the call site's own and then the slot's, which takes no\n// event, or the call site's alone where the slot adds nothing.\nfunction andThen<E>(\n own: ((event: E) => void) | null | undefined,\n slot: (() => void) | undefined,\n) {\n if (slot === undefined) {\n return own ?? undefined\n }\n\n return (event: E) => {\n own?.(event)\n slot()\n }\n}\n\n// Records the button's height for the corner, then calls the call site's own\n// `onLayout`. Built by a call rather than declared in a component, which is\n// what react-perf's jsx-no-new-function-as-prop is after; the React Compiler\n// memoises the result on its inputs.\nfunction measureThen(\n height: SharedValue<number>,\n onLayout: PressableProps['onLayout'],\n) {\n return (event: LayoutChangeEvent) => {\n height.set(event.nativeEvent.layout.height)\n onLayout?.(event)\n }\n}\n\n// The call site's own `onPress`, then what the button does with the press.\n// Built by a call for the reason `measureThen` is.\nfunction pressThen(\n onPress: PressableProps['onPress'],\n then: (event: GestureResponderEvent) => void,\n) {\n return (event: GestureResponderEvent) => {\n onPress?.(event)\n then(event)\n }\n}\n\n/**\n * What a press does, and whether a toggle is selected. A press calls the call\n * site's own `onPress` first, then opens the link, changes the toggle's state\n * or submits the form the button is in, which is the order web reports them\n * in. A toggle keeps its own state from `defaultSelected` unless the call\n * site holds it in `isSelected`. A submit button outside a form submits\n * nothing, as on web.\n *\n * Given a `slot`, a button that is not a link then does what that slot of\n * the overlay around it does, as src/button's slots.ts sets out: opens it\n * from the trigger, or closes it from its content.\n */\nfunction useButtonAction({\n defaultSelected,\n href,\n isSelected,\n isToggle,\n onChange,\n onPress,\n slot,\n type,\n}: {\n readonly defaultSelected: boolean | undefined\n readonly href: string | undefined\n readonly isSelected: boolean | undefined\n readonly isToggle: boolean\n readonly onChange: ((isSelected: boolean) => void) | undefined\n readonly onPress: PressableProps['onPress']\n readonly slot?: 'close' | 'default'\n readonly type: 'button' | 'submit' | undefined\n}) {\n const openLink = useOpenLink()\n const form = use(FormContext)\n const slots = use(ButtonSlotContext)\n const [ownSelected, setOwnSelected] = useState(defaultSelected ?? false)\n const selected = isToggle && (isSelected ?? ownSelected)\n const filled = slot === undefined ? undefined : slots?.[slot]\n\n function follow(event: GestureResponderEvent) {\n if (href !== undefined) {\n openLink(href)\n return\n }\n if (isToggle) {\n setOwnSelected(!selected)\n onChange?.(!selected)\n } else if (type === 'submit') {\n form?.requestSubmit()\n }\n filled?.(event)\n }\n\n return { onPress: pressThen(onPress, follow), selected }\n}\n\n/**\n * The corner a button eases to, over the motion tokens' short duration on the\n * emphasised curve, and at once for a reader who asks the device for reduced\n * motion. The pill is the radius scale's `pill`, far larger than any button,\n * so the ease runs from half the button's measured height rather than from\n * that: otherwise the corner would hold the pill for most of the ease and\n * jump at its end. The `onLayout` it hands back measures the button, then\n * calls the call site's own.\n */\nfunction useCorner(radius: number, onLayout: PressableProps['onLayout']) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const height = useSharedValue(0)\n const timing = {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasized),\n }\n const corner = useAnimatedStyle(() => {\n 'worklet'\n const measured = height.get()\n const target = measured > 0 ? Math.min(radius, measured / 2) : radius\n\n return {\n borderRadius: reduceMotion ? target : withTiming(target, timing),\n }\n })\n\n return { corner, onLayout: measureThen(height, onLayout) }\n}\n\n/**\n * What a button filling the default slot of an overlay drawn beside it takes\n * on, as src/button's slots.ts sets out: its node is kept for the overlay to\n * be placed against, alongside the call site's own `ref`, `aria-expanded`\n * says whether the overlay is open, and the overlay's hover and long press\n * handlers run after the call site's. A button filling no such slot, or a\n * link, keeps its own props untouched.\n */\nfunction useSlotTrigger({\n delayLongPress,\n href,\n onHoverIn,\n onHoverOut,\n onLongPress,\n ref,\n slot,\n}: {\n readonly delayLongPress: PressableProps['delayLongPress']\n readonly href: string | undefined\n readonly onHoverIn: PressableProps['onHoverIn']\n readonly onHoverOut: PressableProps['onHoverOut']\n readonly onLongPress: PressableProps['onLongPress']\n readonly ref: Ref<HostInstance> | undefined\n readonly slot: 'close' | undefined\n}): SlotTriggerProps {\n const slots = use(ButtonSlotContext)\n const trigger =\n href === undefined && slot === undefined ? slots?.trigger : undefined\n\n return {\n ...(trigger === undefined\n ? { delayLongPress, ref }\n : {\n 'aria-expanded': trigger.expanded,\n delayLongPress: trigger.delayLongPress ?? delayLongPress,\n ref: refsOf(trigger.ref, ref),\n }),\n onHoverIn: andThen(onHoverIn, trigger?.onHoverIn),\n onHoverOut: andThen(onHoverOut, trigger?.onHoverOut),\n onLongPress: andThen(onLongPress, trigger?.onLongPress),\n }\n}\n\nexport {\n AnimatedPressable,\n buttonStyles,\n HEIGHTS,\n OUTLINE_WIDTHS,\n PRESSED_CORNERS,\n toggleState,\n useButtonAction,\n useCorner,\n useSlotTrigger,\n}\nexport type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm, ControlSize }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Ref","GestureResponderEvent","HostInstance","LayoutChangeEvent","PressableProps","SharedValue","use","useState","Platform","Pressable","StyleSheet","Animated","Easing","useAnimatedStyle","useReducedMotion","useSharedValue","withTiming","ResolvedTheme","GroupCorners","FormContext","refsOf","useOpenLink","useTheme","ButtonSlotContext","ButtonTooltipContext","ButtonLinkForm","defaultSelected","href","isPending","isSelected","onChange","pendingLabel","slot","type","ButtonPending","ButtonPressForm","ButtonToggleForm","ControlSize","SlotTriggerProps","accessibilityHint","delayLongPress","onHoverIn","onHoverOut","onLongPress","onPressIn","onPressOut","ref","ToggleState","HEIGHTS","lg","md","xl","xs","xxl","const","Record","OUTLINE_WIDTHS","Readonly","PRESSED_CORNERS","AnimatedPressable","createAnimatedComponent","toggleState","select","selected","default","web","buttonStyles","create","clip","bottom","left","overflow","pointerEvents","position","right","top","andThen","own","event","E","undefined","measureThen","height","onLayout","set","nativeEvent","layout","pressThen","onPress","then","useButtonAction","t0","$","_c","isToggle","openLink","form","slots","ownSelected","setOwnSelected","filled","t1","follow","requestSubmit","t2","t3","useCorner","radius","motion","reduceMotion","OS","durationShort2","easingEmphasized","bezier","duration","easing","timing","corner","measured","get","ease","value","target","Math","min","borderRadius","borderBottomLeftRadius","start","borderBottomRightRadius","end","borderTopLeftRadius","borderTopRightRadius","borderBottomEndRadius","borderBottomStartRadius","borderTopEndRadius","borderTopStartRadius","t4","useSlotTrigger","tooltip","trigger","hint","expanded","_c"],"sources":["../../src/button/index.ts"],"sourcesContent":["import type { Ref } from 'react'\nimport type {\n GestureResponderEvent,\n HostInstance,\n LayoutChangeEvent,\n PressableProps,\n} from 'react-native'\nimport type { SharedValue } from 'react-native-reanimated'\n\nimport { use, useState } from 'react'\nimport { Platform, Pressable, StyleSheet } from 'react-native'\nimport Animated, {\n Easing,\n useAnimatedStyle,\n useReducedMotion,\n useSharedValue,\n withTiming,\n} from 'react-native-reanimated'\n\nimport type { ResolvedTheme } from '../theme/scope'\nimport type { GroupCorners } from './group'\n\nimport { FormContext } from '../form'\nimport { refsOf } from '../ref'\nimport { useOpenLink } from '../router'\nimport { useTheme } from '../theme/scope'\nimport { ButtonSlotContext, ButtonTooltipContext } from './slots'\n\n// What every button does past its own styles, shared by Button and\n// IconButton as web's src/button is: the three forms a button takes, the\n// toggle's state and how it is announced, what a press does after the call\n// site's own `onPress`, a submit button's included, and what the slot it\n// fills in an overlay adds to it, the corner that eases between shapes, and\n// the metrics the two components' pages give alike, since the two share\n// their five heights so that they line up in a row. What a pending button\n// draws and says is ./pending.tsx, and what a ButtonGroup hands the buttons\n// in it is ./group.ts.\n\n/**\n * A plain button, a link, or a toggle, of which a call site writes exactly\n * one. Each form names the props of the other two as `never`, so a toggle\n * given `href` is a compile error rather than a prop that silently does\n * nothing.\n */\ntype ButtonLinkForm = {\n readonly defaultSelected?: never\n /**\n * Where the button leads. Given one, a press opens it through the\n * `RouterProvider` around the button, or through the platform for a URL\n * with a scheme of its own, and the button is announced as a link.\n */\n readonly href: string\n readonly isPending?: never\n readonly isSelected?: never\n readonly onChange?: never\n readonly pendingLabel?: never\n readonly slot?: never\n readonly type?: never\n}\n\n/**\n * Whether the button's action is under way, which the plain button and the\n * toggle both take and the link does not.\n */\ntype ButtonPending = {\n /**\n * Shows that the button's action is under way: a ring takes the place of\n * the label and the icon, which keep their space, and the button keeps its\n * focus and takes no press until it clears. A toggle may be pending, for a\n * state saved somewhere slow.\n * @default false\n */\n readonly isPending?: boolean\n /**\n * The name of the ring shown while the button is pending, for a screen\n * reader. The content it replaces is hidden while it shows. Left out, it is\n * the word for it in the `I18nProvider`'s locale, or the device's outside\n * one, \"Loading\" in English.\n */\n readonly pendingLabel?: string\n}\n\ntype ButtonPressForm = ButtonPending & {\n readonly defaultSelected?: never\n readonly href?: never\n readonly isSelected?: never\n readonly onChange?: never\n /**\n * `close` closes the overlay the button is in, as web's React Aria slot of\n * that name does: a `Dialog` given one inside its content closes when it is\n * pressed, after the button's own `onPress`.\n */\n readonly slot?: 'close'\n /**\n * What a press does past `onPress`. `submit` submits the `Form` the button\n * is in, as a submit button does on web; `button` does nothing more.\n * @default 'button'\n */\n readonly type?: 'button' | 'submit'\n}\n\ntype ButtonToggleForm = ButtonPending & {\n /**\n * Whether a toggle starts selected, when it keeps its own state. Passing\n * this, `isSelected` or `onChange` is what makes the button a toggle.\n */\n readonly defaultSelected?: boolean\n readonly href?: never\n /**\n * Whether a toggle is selected, when the call site holds the state. Pass\n * it with `onChange`; passing this, `defaultSelected` or `onChange` is what\n * makes the button a toggle.\n */\n readonly isSelected?: boolean\n /**\n * Called with the new state when a toggle is pressed, after `onPress`.\n * Passing this, `isSelected` or `defaultSelected` is what makes the button\n * a toggle.\n */\n readonly onChange?: (isSelected: boolean) => void\n readonly slot?: never\n readonly type?: never\n}\n\n/** The five control heights both pages give, by the names both props take. */\ntype ControlSize = 'lg' | 'md' | 'xl' | 'xs' | 'xxl'\n\n/** How a toggle reports its state to a screen reader. */\n// What `useSlotTrigger` hands the Pressable, spelled out because the\n// composed ref's inferred type names a private React Native module, which a\n// declaration file cannot reach.\ntype SlotTriggerProps = {\n readonly accessibilityHint: string | undefined\n readonly 'aria-expanded'?: boolean\n readonly delayLongPress: PressableProps['delayLongPress']\n readonly onHoverIn: PressableProps['onHoverIn']\n readonly onHoverOut: PressableProps['onHoverOut']\n readonly onLongPress: PressableProps['onLongPress']\n readonly onPressIn: PressableProps['onPressIn']\n readonly onPressOut: PressableProps['onPressOut']\n readonly ref: Ref<HostInstance> | undefined\n}\n\ntype ToggleState = {\n readonly 'aria-pressed'?: boolean\n readonly 'aria-selected'?: boolean\n}\n\n// The control height each size reads: the page's XS, S, M, L and XL, which\n// the theme's sizing scale names one step apart from this prop.\nconst HEIGHTS = {\n lg: 'controlLg',\n md: 'controlSm',\n xl: 'controlXl',\n xs: 'controlXs',\n xxl: 'controlXxl',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['sizing']>\n\n// The outlined rule thickens with the size, as the buttons page's size tokens\n// have it: 1dp up to M, 2dp at L, 3dp at XL. The icon buttons page keeps its\n// widths in a token set its widget will not open, so the icon button takes\n// these, which a 96dp icon button beside a 96dp outlined button needs anyway.\nconst OUTLINE_WIDTHS: Readonly<Record<ControlSize, number>> = {\n lg: 1,\n md: 1,\n xl: 2,\n xs: 1,\n xxl: 3,\n}\n\n// The corner each size presses to, which both pages give alike.\nconst PRESSED_CORNERS = {\n lg: 'md',\n md: 'sm',\n xl: 'lg',\n xs: 'sm',\n xxl: 'lg',\n} as const satisfies Record<ControlSize, keyof ResolvedTheme['radii']>\n\nconst AnimatedPressable = Animated.createAnimatedComponent(Pressable)\n\n// Web's toggle reports `aria-pressed`, which React Native Web writes onto the\n// element. React Native has no pressed state to report, and reports the\n// selected one instead, which VoiceOver and TalkBack both read out.\nconst toggleState = Platform.select<(selected: boolean) => ToggleState>({\n default: (selected) => ({ 'aria-selected': selected }),\n web: (selected) => ({ 'aria-pressed': selected }),\n})\n\nconst buttonStyles = StyleSheet.create({\n // Clips the state layer and the ripple to the corner as it morphs, and\n // takes no touches.\n clip: {\n bottom: 0,\n left: 0,\n overflow: 'hidden',\n pointerEvents: 'none',\n position: 'absolute',\n right: 0,\n top: 0,\n },\n})\n\n// A handler that runs the call site's own and then the slot's, which takes no\n// event, or the call site's alone where the slot adds nothing.\nfunction andThen<E>(\n own: ((event: E) => void) | null | undefined,\n slot: (() => void) | undefined,\n) {\n if (slot === undefined) {\n return own ?? undefined\n }\n\n return (event: E) => {\n own?.(event)\n slot()\n }\n}\n\n// Records the button's height for the corner, then calls the call site's own\n// `onLayout`. Built by a call rather than declared in a component, which is\n// what react-perf's jsx-no-new-function-as-prop is after; the React Compiler\n// memoises the result on its inputs.\nfunction measureThen(\n height: SharedValue<number>,\n onLayout: PressableProps['onLayout'],\n) {\n return (event: LayoutChangeEvent) => {\n height.set(event.nativeEvent.layout.height)\n onLayout?.(event)\n }\n}\n\n// The call site's own `onPress`, then what the button does with the press.\n// Built by a call for the reason `measureThen` is.\nfunction pressThen(\n onPress: PressableProps['onPress'],\n then: (event: GestureResponderEvent) => void,\n) {\n return (event: GestureResponderEvent) => {\n onPress?.(event)\n then(event)\n }\n}\n\n/**\n * What a press does, and whether a toggle is selected. A press calls the call\n * site's own `onPress` first, then opens the link, changes the toggle's state\n * or submits the form the button is in, which is the order web reports them\n * in. A toggle keeps its own state from `defaultSelected` unless the call\n * site holds it in `isSelected`. A submit button outside a form submits\n * nothing, as on web.\n *\n * Given a `slot`, a button that is not a link then does what that slot of\n * the overlay around it does, as src/button's slots.ts sets out: opens it\n * from the trigger, or closes it from its content.\n */\nfunction useButtonAction({\n defaultSelected,\n href,\n isSelected,\n isToggle,\n onChange,\n onPress,\n slot,\n type,\n}: {\n readonly defaultSelected: boolean | undefined\n readonly href: string | undefined\n readonly isSelected: boolean | undefined\n readonly isToggle: boolean\n readonly onChange: ((isSelected: boolean) => void) | undefined\n readonly onPress: PressableProps['onPress']\n readonly slot?: 'close' | 'default'\n readonly type: 'button' | 'submit' | undefined\n}) {\n const openLink = useOpenLink()\n const form = use(FormContext)\n const slots = use(ButtonSlotContext)\n const [ownSelected, setOwnSelected] = useState(defaultSelected ?? false)\n const selected = isToggle && (isSelected ?? ownSelected)\n const filled = slot === undefined ? undefined : slots?.[slot]\n\n function follow(event: GestureResponderEvent) {\n if (href !== undefined) {\n openLink(href)\n return\n }\n if (isToggle) {\n setOwnSelected(!selected)\n onChange?.(!selected)\n } else if (type === 'submit') {\n form?.requestSubmit()\n }\n filled?.(event)\n }\n\n return { onPress: pressThen(onPress, follow), selected }\n}\n\n/**\n * The corner a button eases to, over the motion tokens' short duration on the\n * emphasised curve, and at once for a reader who asks the device for reduced\n * motion: one radius for all four, or a connected ButtonGroup's two, one for\n * the start side and one for the end, which follow the layout's direction on\n * native. On web Reanimated writes an animated style straight onto the\n * element, where only the physical corners are CSS properties, so the start\n * side is the left there, as the web Storybook lays a row out.\n * The pill is the radius scale's `pill`, far larger than any button, so the\n * ease runs from half the button's measured height rather than from that:\n * otherwise the corner would hold the pill for most of the ease and jump at\n * its end. The `onLayout` it hands back measures the button, then calls the\n * call site's own.\n */\nfunction useCorner(\n radius: GroupCorners | number,\n onLayout: PressableProps['onLayout'],\n) {\n const { motion } = useTheme()\n const reduceMotion = useReducedMotion()\n const height = useSharedValue(0)\n const web = Platform.OS === 'web'\n const timing = {\n duration: motion.durationShort2,\n easing: Easing.bezier(...motion.easingEmphasized),\n }\n const corner = useAnimatedStyle(() => {\n 'worklet'\n const measured = height.get()\n const ease = (value: number) => {\n const target = measured > 0 ? Math.min(value, measured / 2) : value\n\n return reduceMotion ? target : withTiming(target, timing)\n }\n\n if (typeof radius === 'number') {\n return { borderRadius: ease(radius) }\n }\n if (web) {\n return {\n borderBottomLeftRadius: ease(radius.start),\n borderBottomRightRadius: ease(radius.end),\n borderTopLeftRadius: ease(radius.start),\n borderTopRightRadius: ease(radius.end),\n }\n }\n\n return {\n borderBottomEndRadius: ease(radius.end),\n borderBottomStartRadius: ease(radius.start),\n borderTopEndRadius: ease(radius.end),\n borderTopStartRadius: ease(radius.start),\n }\n })\n\n return { corner, onLayout: measureThen(height, onLayout) }\n}\n\n/**\n * What a button filling the default slot of an overlay drawn beside it takes\n * on, as src/button's slots.ts sets out: its node is kept for the overlay to\n * be placed against, alongside the call site's own `ref`, `aria-expanded`\n * says whether the overlay is open, and the overlay's hover and long press\n * handlers run after the call site's. A button filling no such slot, or a\n * link, keeps its own props untouched. A button inside a `Tooltip` takes the\n * tooltip's node, hover, press and long press handlers on top of either, and\n * its label as the hint, unless the call site gave a hint of its own.\n */\nfunction useSlotTrigger({\n accessibilityHint,\n delayLongPress,\n href,\n onHoverIn,\n onHoverOut,\n onLongPress,\n onPressIn,\n onPressOut,\n ref,\n slot,\n}: {\n readonly accessibilityHint: string | undefined\n readonly delayLongPress: PressableProps['delayLongPress']\n readonly href: string | undefined\n readonly onHoverIn: PressableProps['onHoverIn']\n readonly onHoverOut: PressableProps['onHoverOut']\n readonly onLongPress: PressableProps['onLongPress']\n readonly onPressIn: PressableProps['onPressIn']\n readonly onPressOut: PressableProps['onPressOut']\n readonly ref: Ref<HostInstance> | undefined\n readonly slot: 'close' | undefined\n}): SlotTriggerProps {\n const slots = use(ButtonSlotContext)\n const tooltip = use(ButtonTooltipContext) ?? undefined\n const trigger =\n href === undefined && slot === undefined ? slots?.trigger : undefined\n\n return {\n accessibilityHint: accessibilityHint ?? tooltip?.hint,\n ...(trigger === undefined\n ? undefined\n : { 'aria-expanded': trigger.expanded }),\n delayLongPress:\n trigger?.delayLongPress ?? tooltip?.delayLongPress ?? delayLongPress,\n onHoverIn: andThen(\n andThen(onHoverIn, trigger?.onHoverIn),\n tooltip?.onHoverIn,\n ),\n onHoverOut: andThen(\n andThen(onHoverOut, trigger?.onHoverOut),\n tooltip?.onHoverOut,\n ),\n onLongPress: andThen(\n andThen(onLongPress, trigger?.onLongPress),\n tooltip?.onLongPress,\n ),\n onPressIn: andThen(onPressIn, tooltip?.onPressIn),\n onPressOut: andThen(onPressOut, tooltip?.onPressOut),\n ref:\n trigger === undefined && tooltip === undefined\n ? ref\n : refsOf(trigger?.ref, tooltip?.ref, ref),\n }\n}\n\nexport {\n AnimatedPressable,\n buttonStyles,\n HEIGHTS,\n OUTLINE_WIDTHS,\n PRESSED_CORNERS,\n pressThen,\n toggleState,\n useButtonAction,\n useCorner,\n useSlotTrigger,\n}\nexport type { ButtonLinkForm, ButtonPressForm, ButtonToggleForm, ControlSize }\n"],"mappings":"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"}
|
package/dist/button/slots.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createContext } from "react";
|
|
2
2
|
//#region src/button/slots.ts
|
|
3
3
|
const ButtonSlotContext = createContext(null);
|
|
4
|
+
const ButtonTooltipContext = createContext(null);
|
|
4
5
|
//#endregion
|
|
5
|
-
export { ButtonSlotContext };
|
|
6
|
+
export { ButtonSlotContext, ButtonTooltipContext };
|
|
6
7
|
|
|
7
8
|
//# sourceMappingURL=slots.js.map
|
package/dist/button/slots.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slots.js","names":["RefObject","GestureResponderEvent","HostInstance","createContext","ButtonSlots","close","event","default","trigger","ButtonSlotTrigger","delayLongPress","expanded","onHoverIn","onHoverOut","onLongPress","ref","ButtonSlotContext"],"sources":["../../src/button/slots.ts"],"sourcesContent":["import type { RefObject } from 'react'\nimport type { GestureResponderEvent, HostInstance } from 'react-native'\n\nimport { createContext } from 'react'\n\n/**\n * What a button does past its own press when it sits in an overlay's trigger\n * or content, as web's React Aria hands the same through a button's `slot`:\n * the default slot opens the overlay, which a button inside `Dialog`,\n * `Popover` or `Sheet` and outside its content takes, and `close` closes it,\n * which one given `slot=\"close\"` inside the content takes. The overlay\n * provides the slots it fills, and resets the default one inside its content,\n * so a button there opens nothing.\n */\ntype ButtonSlots = {\n readonly close?: (event: GestureResponderEvent) => void\n readonly default?: (event: GestureResponderEvent) => void\n /**\n * What the button filling the default slot takes on beside its press, for\n * an overlay drawn beside it.\n */\n readonly trigger?: ButtonSlotTrigger\n}\n\n/**\n * What a button filling the default slot takes on for an overlay drawn beside\n * it: its node, which the overlay is placed against, whether the overlay is\n * open, which a screen reader announces, and the hover and long press a\n * hovered popover opens on. The handlers run after the button's own.\n */\ntype ButtonSlotTrigger = {\n /** How long a press is held before it is a long press, in milliseconds. */\n readonly delayLongPress?: number\n /** Whether the overlay the button opens is open. */\n readonly expanded: boolean\n readonly onHoverIn?: () => void\n readonly onHoverOut?: () => void\n readonly onLongPress?: () => void\n /** Where the button's node is kept, for the overlay to be placed against. */\n readonly ref: RefObject<HostInstance | null>\n}\n\nconst ButtonSlotContext = createContext<ButtonSlots | null>(null)\n\nexport { ButtonSlotContext }\nexport type { ButtonSlots, ButtonSlotTrigger }\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"slots.js","names":["RefObject","GestureResponderEvent","HostInstance","createContext","ButtonSlots","close","event","default","trigger","ButtonSlotTrigger","delayLongPress","expanded","onHoverIn","onHoverOut","onLongPress","ref","ButtonTooltip","hint","onPressIn","onPressOut","ButtonSlotContext","ButtonTooltipContext"],"sources":["../../src/button/slots.ts"],"sourcesContent":["import type { RefObject } from 'react'\nimport type { GestureResponderEvent, HostInstance } from 'react-native'\n\nimport { createContext } from 'react'\n\n/**\n * What a button does past its own press when it sits in an overlay's trigger\n * or content, as web's React Aria hands the same through a button's `slot`:\n * the default slot opens the overlay, which a button inside `Dialog`,\n * `Popover` or `Sheet` and outside its content takes, and `close` closes it,\n * which one given `slot=\"close\"` inside the content takes. The overlay\n * provides the slots it fills, and resets the default one inside its content,\n * so a button there opens nothing.\n */\ntype ButtonSlots = {\n readonly close?: (event: GestureResponderEvent) => void\n readonly default?: (event: GestureResponderEvent) => void\n /**\n * What the button filling the default slot takes on beside its press, for\n * an overlay drawn beside it.\n */\n readonly trigger?: ButtonSlotTrigger\n}\n\n/**\n * What a button filling the default slot takes on for an overlay drawn beside\n * it: its node, which the overlay is placed against, whether the overlay is\n * open, which a screen reader announces, and the hover and long press a\n * hovered popover opens on. The handlers run after the button's own.\n */\ntype ButtonSlotTrigger = {\n /** How long a press is held before it is a long press, in milliseconds. */\n readonly delayLongPress?: number\n /** Whether the overlay the button opens is open. */\n readonly expanded: boolean\n readonly onHoverIn?: () => void\n readonly onHoverOut?: () => void\n readonly onLongPress?: () => void\n /** Where the button's node is kept, for the overlay to be placed against. */\n readonly ref: RefObject<HostInstance | null>\n}\n\n/**\n * What a button takes on inside a `Tooltip`, whichever slot it fills: its\n * node, which the tooltip is placed against, the tooltip's label as its\n * accessibility hint, and the pointer and the held press that open and close\n * the tooltip. The handlers run after the button's own and an overlay\n * trigger's. Kept apart from the slots so that a tooltip and the overlay a\n * button opens can wrap it in either order, and a close button and a link\n * take one too.\n */\ntype ButtonTooltip = {\n /** How long a press is held before it opens the tooltip, in milliseconds. */\n readonly delayLongPress: number\n /** The tooltip's label, read after the button's name. */\n readonly hint: string\n readonly onHoverIn?: (() => void) | undefined\n readonly onHoverOut?: (() => void) | undefined\n readonly onLongPress: () => void\n readonly onPressIn?: (() => void) | undefined\n readonly onPressOut: () => void\n /** Where the button's node is kept, for the tooltip to be placed against. */\n readonly ref: RefObject<HostInstance | null>\n}\n\nconst ButtonSlotContext = createContext<ButtonSlots | null>(null)\nconst ButtonTooltipContext = createContext<ButtonTooltip | null>(null)\n\nexport { ButtonSlotContext, ButtonTooltipContext }\nexport type { ButtonSlots, ButtonSlotTrigger, ButtonTooltip }\n"],"mappings":";;AAiEA,MAAMoB,oBAAoBjB,cAAkC,IAAI;AAChE,MAAMkB,uBAAuBlB,cAAoC,IAAI"}
|