tempest-react-sdk 0.64.0 → 0.65.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 -4
- package/dist/components/Command/Command.module.cjs.map +1 -1
- package/dist/components/Command/Command.module.js.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.module.cjs.map +1 -1
- package/dist/components/ContextMenu/ContextMenu.module.js.map +1 -1
- package/dist/components/DataTable/DataTable.cjs +1 -1
- package/dist/components/DataTable/DataTable.cjs.map +1 -1
- package/dist/components/DataTable/DataTable.js +1 -0
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DataTable/use-dev-warnings.cjs +1 -1
- package/dist/components/DataTable/use-dev-warnings.cjs.map +1 -1
- package/dist/components/DataTable/use-dev-warnings.js +4 -3
- package/dist/components/DataTable/use-dev-warnings.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.cjs.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.module.js.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.cjs +1 -1
- package/dist/components/InstallBanner/InstallBanner.cjs.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.js +62 -37
- package/dist/components/InstallBanner/InstallBanner.js.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.module.cjs +1 -1
- package/dist/components/InstallBanner/InstallBanner.module.cjs.map +1 -1
- package/dist/components/InstallBanner/InstallBanner.module.js +4 -3
- package/dist/components/InstallBanner/InstallBanner.module.js.map +1 -1
- package/dist/components/InstallButton/InstallButton.cjs +1 -1
- package/dist/components/InstallButton/InstallButton.cjs.map +1 -1
- package/dist/components/InstallButton/InstallButton.js +34 -10
- package/dist/components/InstallButton/InstallButton.js.map +1 -1
- package/dist/components/InstallButton/InstallButton.module.cjs +3 -0
- package/dist/components/InstallButton/InstallButton.module.cjs.map +1 -0
- package/dist/components/InstallButton/InstallButton.module.js +10 -0
- package/dist/components/InstallButton/InstallButton.module.js.map +1 -0
- package/dist/components/InstallButton/install-hints.cjs +2 -0
- package/dist/components/InstallButton/install-hints.cjs.map +1 -0
- package/dist/components/InstallButton/install-hints.js +32 -0
- package/dist/components/InstallButton/install-hints.js.map +1 -0
- package/dist/components/SegmentedControl/SegmentedControl.module.cjs.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.module.js.map +1 -1
- package/dist/components/Select/Select.cjs +1 -1
- package/dist/components/Select/Select.cjs.map +1 -1
- package/dist/components/Select/Select.js +43 -40
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/Select.module.cjs +1 -1
- package/dist/components/Select/Select.module.cjs.map +1 -1
- package/dist/components/Select/Select.module.js +5 -3
- package/dist/components/Select/Select.module.js.map +1 -1
- package/dist/hooks/use-install-prompt.cjs +1 -1
- package/dist/hooks/use-install-prompt.cjs.map +1 -1
- package/dist/hooks/use-install-prompt.js +9 -2
- package/dist/hooks/use-install-prompt.js.map +1 -1
- package/dist/http/api-client.cjs +1 -1
- package/dist/http/api-client.cjs.map +1 -1
- package/dist/http/api-client.js +80 -74
- package/dist/http/api-client.js.map +1 -1
- package/dist/http/decode-response.cjs +2 -0
- package/dist/http/decode-response.cjs.map +1 -0
- package/dist/http/decode-response.js +28 -0
- package/dist/http/decode-response.js.map +1 -0
- package/dist/styles/InstallButton.css +2 -0
- package/dist/styles/actions.css +6 -1
- package/dist/styles/advanced.css +2 -2
- package/dist/styles/component/Command.css +1 -1
- package/dist/styles/component/ContextMenu.css +1 -1
- package/dist/styles/component/DropdownMenu.css +1 -1
- package/dist/styles/component/InstallBanner.css +2 -0
- package/dist/styles/component/InstallButton.css +4 -0
- package/dist/styles/component/SegmentedControl.css +1 -1
- package/dist/styles/component/Select.css +4 -0
- package/dist/styles/core.css +2 -2
- package/dist/styles/forms.css +4 -0
- package/dist/styles/manifest.json +2 -1
- package/dist/styles/navigation.css +1 -1
- package/dist/styles/tokens.css +2 -2
- package/dist/styles.css +1 -1
- package/dist/tempest-react-sdk.cjs +1 -1
- package/dist/tempest-react-sdk.d.ts +258 -26
- package/dist/tempest-react-sdk.js +319 -318
- package/dist/theme/ThemeProvider.cjs +1 -1
- package/dist/theme/ThemeProvider.cjs.map +1 -1
- package/dist/theme/ThemeProvider.js +40 -45
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/create-theme.cjs +1 -1
- package/dist/theme/create-theme.cjs.map +1 -1
- package/dist/theme/create-theme.js +1 -1
- package/dist/theme/create-theme.js.map +1 -1
- package/dist/theme/initial-theme.cjs +2 -2
- package/dist/theme/initial-theme.cjs.map +1 -1
- package/dist/theme/initial-theme.js +7 -12
- package/dist/theme/initial-theme.js.map +1 -1
- package/dist/utils/format.cjs +1 -1
- package/dist/utils/format.cjs.map +1 -1
- package/dist/utils/format.js +5 -3
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/storage.cjs +1 -1
- package/dist/utils/storage.cjs.map +1 -1
- package/dist/utils/storage.js +13 -0
- package/dist/utils/storage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isDevBuild as e } from "../../utils/dev-mode.js";
|
|
2
2
|
import { useEffect as t } from "react";
|
|
3
3
|
//#region src/components/DataTable/use-dev-warnings.ts
|
|
4
|
-
function n({ serverMode: n, controlledPage: r, onPageChange: i, sortIsManual: a,
|
|
4
|
+
function n({ serverMode: n, controlledPage: r, onPageChange: i, sortIsManual: a, hasSortableColumn: o, onSortChange: s, searchable: c, onSearchChange: l }) {
|
|
5
5
|
t(() => {
|
|
6
|
-
e() && (n && r === void 0 && console.warn("[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. Without it the pager moves the internal page while `data` keeps showing page 1."), r !== void 0 && !i && console.warn("[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything."), n &&
|
|
6
|
+
e() && (n && r === void 0 && console.warn("[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. Without it the pager moves the internal page while `data` keeps showing page 1."), r !== void 0 && !i && console.warn("[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything."), n && c && !l && console.warn("[tempest] <DataTable totalItems searchable> delegates searching, so the box needs `onSearchChange`. Without it the user types and nothing filters and nothing is reported."), a && o && !s && console.warn("[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else."));
|
|
7
7
|
}, [
|
|
8
8
|
n,
|
|
9
9
|
r,
|
|
@@ -11,7 +11,8 @@ function n({ serverMode: n, controlledPage: r, onPageChange: i, sortIsManual: a,
|
|
|
11
11
|
a,
|
|
12
12
|
o,
|
|
13
13
|
s,
|
|
14
|
-
c
|
|
14
|
+
c,
|
|
15
|
+
l
|
|
15
16
|
]);
|
|
16
17
|
}
|
|
17
18
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-dev-warnings.js","names":[],"sources":["../../../src/components/DataTable/use-dev-warnings.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { isDevBuild } from \"../../utils/dev-mode\";\n\n/** What {@link useDevWarnings} needs to judge the prop combination. */\nexport interface DataTableDevWarningsInput {\n /** Whether `totalItems` was passed. */\n serverMode: boolean;\n /** The controlled `page`, if any. */\n controlledPage: number | undefined;\n /** The page callback, if any. */\n onPageChange: ((page: number) => void) | undefined;\n /** Whether sorting is delegated. */\n sortIsManual: boolean;\n /** The sort callback, if any. */\n onSortChange: ((sort: never) => void) | undefined;\n /** Whether a search box is rendered at all. */\n searchable: boolean;\n /** The search callback, if any. */\n onSearchChange: ((term: string) => void) | undefined;\n}\n\n/**\n * Warn, in development only, about prop combinations that render a table which\n * silently lies.\n *\n * Each of these produces a screen that looks like it works: the header sorts and\n * nothing moves, the pager renders and clicking it does nothing.\n *\n * Three of them **are** type errors now — `DataTablePagingProps`,\n * `DataTableSortProps` and `DataTableSearchProps` reject them at the call site —\n * so those warnings only reach a caller the types cannot: plain JavaScript, or\n * props arriving through an `any`-typed spread.\n *\n * The fourth is the one the types genuinely cannot afford. `totalItems` *implies*\n * `manualSearch`, so `searchable` with no `onSearchChange` is an inert search box\n * in server mode without anybody writing `manualSearch`. Expressing that means\n * the search axis has to read the paging axis, crossing two three-member unions\n * into nine — so it stays here, which is what \"runtime is the cheaper check\"\n * actually looks like when it is true.\n *\n * @param input - The resolved prop combination.\n */\nexport function useDevWarnings({\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n onSortChange,\n searchable,\n onSearchChange,\n}: DataTableDevWarningsInput): void {\n useEffect(() => {\n if (!isDevBuild()) return;\n\n if (serverMode && controlledPage === undefined) {\n console.warn(\n \"[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. \" +\n \"Without it the pager moves the internal page while `data` keeps showing page 1.\",\n );\n }\n if (controlledPage !== undefined && !onPageChange) {\n console.warn(\n \"[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything.\",\n );\n }\n if (serverMode && searchable && !onSearchChange) {\n console.warn(\n \"[tempest] <DataTable totalItems searchable> delegates searching, so the box needs \" +\n \"`onSearchChange`. Without it the user types and nothing filters and nothing is reported.\",\n );\n }\n if (sortIsManual && !onSortChange) {\n console.warn(\n \"[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else.\",\n );\n }\n }, [\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n onSortChange,\n searchable,\n onSearchChange,\n ]);\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"use-dev-warnings.js","names":[],"sources":["../../../src/components/DataTable/use-dev-warnings.ts"],"sourcesContent":["import { useEffect } from \"react\";\n\nimport { isDevBuild } from \"../../utils/dev-mode\";\n\n/** What {@link useDevWarnings} needs to judge the prop combination. */\nexport interface DataTableDevWarningsInput {\n /** Whether `totalItems` was passed. */\n serverMode: boolean;\n /** The controlled `page`, if any. */\n controlledPage: number | undefined;\n /** The page callback, if any. */\n onPageChange: ((page: number) => void) | undefined;\n /** Whether sorting is delegated. */\n sortIsManual: boolean;\n /** Whether any column renders a header the user can click to sort. */\n hasSortableColumn: boolean;\n /** The sort callback, if any. */\n onSortChange: ((sort: never) => void) | undefined;\n /** Whether a search box is rendered at all. */\n searchable: boolean;\n /** The search callback, if any. */\n onSearchChange: ((term: string) => void) | undefined;\n}\n\n/**\n * Warn, in development only, about prop combinations that render a table which\n * silently lies.\n *\n * Each of these produces a screen that looks like it works: the header sorts and\n * nothing moves, the pager renders and clicking it does nothing.\n *\n * Three of them **are** type errors now — `DataTablePagingProps`,\n * `DataTableSortProps` and `DataTableSearchProps` reject them at the call site —\n * so those warnings only reach a caller the types cannot: plain JavaScript, or\n * props arriving through an `any`-typed spread.\n *\n * Each one also has to be reachable. `totalItems` implies `manualSort`, so the\n * sort warning fired on every server-mode table — including one whose columns are\n * all plain, where no header is clickable and the sentence it prints (\"clicking a\n * sortable header changes the arrow and nothing else\") describes a state the user\n * cannot reach. A warning nobody can act on is not a warning: it teaches the\n * reader to skim past the two next to it, which *are* real, and the way out it\n * suggests is `onSortChange={() => {}}` — a callback that does nothing, that the\n * type-checker accepts, and that would hide the warning on the day a `sortable`\n * column does appear. So the sort warning asks for a sortable column first, the\n * way the search one already asks for `searchable`.\n *\n * The fourth is the one the types genuinely cannot afford. `totalItems` *implies*\n * `manualSearch`, so `searchable` with no `onSearchChange` is an inert search box\n * in server mode without anybody writing `manualSearch`. Expressing that means\n * the search axis has to read the paging axis, crossing two three-member unions\n * into nine — so it stays here, which is what \"runtime is the cheaper check\"\n * actually looks like when it is true.\n *\n * @param input - The resolved prop combination.\n */\nexport function useDevWarnings({\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n hasSortableColumn,\n onSortChange,\n searchable,\n onSearchChange,\n}: DataTableDevWarningsInput): void {\n useEffect(() => {\n if (!isDevBuild()) return;\n\n if (serverMode && controlledPage === undefined) {\n console.warn(\n \"[tempest] <DataTable totalItems> is server mode, which needs a controlled `page`. \" +\n \"Without it the pager moves the internal page while `data` keeps showing page 1.\",\n );\n }\n if (controlledPage !== undefined && !onPageChange) {\n console.warn(\n \"[tempest] <DataTable page> is controlled but `onPageChange` is missing, so the pager cannot do anything.\",\n );\n }\n if (serverMode && searchable && !onSearchChange) {\n console.warn(\n \"[tempest] <DataTable totalItems searchable> delegates searching, so the box needs \" +\n \"`onSearchChange`. Without it the user types and nothing filters and nothing is reported.\",\n );\n }\n if (sortIsManual && hasSortableColumn && !onSortChange) {\n console.warn(\n \"[tempest] <DataTable> is sorting manually but `onSortChange` is missing: clicking a sortable header changes the arrow and nothing else.\",\n );\n }\n }, [\n serverMode,\n controlledPage,\n onPageChange,\n sortIsManual,\n hasSortableColumn,\n onSortChange,\n searchable,\n onSearchChange,\n ]);\n}\n"],"mappings":";;;AAwDA,SAAgB,EAAe,EAC3B,eACA,mBACA,iBACA,iBACA,sBACA,iBACA,eACA,qBACgC;CAChC,QAAgB;EACP,EAAW,MAEZ,KAAc,MAAmB,KAAA,KACjC,QAAQ,KACJ,mKAEJ,GAEA,MAAmB,KAAA,KAAa,CAAC,KACjC,QAAQ,KACJ,0GACJ,GAEA,KAAc,KAAc,CAAC,KAC7B,QAAQ,KACJ,4KAEJ,GAEA,KAAgB,KAAqB,CAAC,KACtC,QAAQ,KACJ,yIACJ;CAER,GAAG;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACJ,CAAC;AACL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.module.cjs","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.module.css"],"sourcesContent":[".root {\n position: relative;\n display: inline-flex;\n}\n\n.menu {\n position: absolute;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n list-style: none;\n margin: 0;\n animation: tempest-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.bottomStart {\n top: calc(100% + 4px);\n left: 0;\n}\n.bottomEnd {\n top: calc(100% + 4px);\n right: 0;\n}\n.topStart {\n bottom: calc(100% + 4px);\n left: 0;\n}\n.topEnd {\n bottom: calc(100% + 4px);\n right: 0;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus,\n.item.active {\n outline: none;\n background-color: var(--tempest-
|
|
1
|
+
{"version":3,"file":"DropdownMenu.module.cjs","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.module.css"],"sourcesContent":[".root {\n position: relative;\n display: inline-flex;\n}\n\n.menu {\n position: absolute;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n list-style: none;\n margin: 0;\n animation: tempest-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.bottomStart {\n top: calc(100% + 4px);\n left: 0;\n}\n.bottomEnd {\n top: calc(100% + 4px);\n right: 0;\n}\n.topStart {\n bottom: calc(100% + 4px);\n left: 0;\n}\n.topEnd {\n bottom: calc(100% + 4px);\n right: 0;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus,\n.item.active {\n outline: none;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n box-shadow: inset 3px 0 0 0 var(--tempest-selected-indicator);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.item.danger {\n color: var(--tempest-danger);\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n.label {\n padding: var(--tempest-space-1) var(--tempest-space-3);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text-muted);\n text-transform: uppercase;\n letter-spacing: var(--tempest-tracking-wider);\n}\n\n@keyframes tempest-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .menu {\n animation: none;\n }\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DropdownMenu.module.js","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.module.css"],"sourcesContent":[".root {\n position: relative;\n display: inline-flex;\n}\n\n.menu {\n position: absolute;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n list-style: none;\n margin: 0;\n animation: tempest-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.bottomStart {\n top: calc(100% + 4px);\n left: 0;\n}\n.bottomEnd {\n top: calc(100% + 4px);\n right: 0;\n}\n.topStart {\n bottom: calc(100% + 4px);\n left: 0;\n}\n.topEnd {\n bottom: calc(100% + 4px);\n right: 0;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus,\n.item.active {\n outline: none;\n background-color: var(--tempest-
|
|
1
|
+
{"version":3,"file":"DropdownMenu.module.js","names":[],"sources":["../../../src/components/DropdownMenu/DropdownMenu.module.css"],"sourcesContent":[".root {\n position: relative;\n display: inline-flex;\n}\n\n.menu {\n position: absolute;\n z-index: var(--tempest-z-dropdown);\n min-width: 180px;\n max-width: 320px;\n background-color: var(--tempest-bg);\n border: 1px solid var(--tempest-border);\n border-radius: var(--tempest-radius-lg);\n box-shadow: var(--tempest-shadow-lg);\n padding: var(--tempest-space-1);\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-base);\n list-style: none;\n margin: 0;\n animation: tempest-menu-in var(--tempest-duration-fast) var(--tempest-ease-out);\n}\n\n.bottomStart {\n top: calc(100% + 4px);\n left: 0;\n}\n.bottomEnd {\n top: calc(100% + 4px);\n right: 0;\n}\n.topStart {\n bottom: calc(100% + 4px);\n left: 0;\n}\n.topEnd {\n bottom: calc(100% + 4px);\n right: 0;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-2);\n width: 100%;\n padding: var(--tempest-space-2) var(--tempest-space-3);\n background: none;\n border: none;\n border-radius: var(--tempest-radius-sm);\n color: var(--tempest-text);\n font-family: inherit;\n font-size: inherit;\n line-height: var(--tempest-leading-snug);\n text-align: left;\n cursor: pointer;\n transition: var(--tempest-transition-color);\n}\n\n@media (hover: hover) and (pointer: fine) {\n .item:hover:not(:disabled) {\n background-color: var(--tempest-surface);\n }\n}\n\n.item:focus,\n.item.active {\n outline: none;\n background-color: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n box-shadow: inset 3px 0 0 0 var(--tempest-selected-indicator);\n}\n\n.item:disabled {\n opacity: 0.55;\n cursor: not-allowed;\n}\n\n.item.danger {\n color: var(--tempest-danger);\n}\n\n.separator {\n height: 1px;\n background-color: var(--tempest-border);\n margin: var(--tempest-space-1) 0;\n}\n\n.label {\n padding: var(--tempest-space-1) var(--tempest-space-3);\n font-size: var(--tempest-text-xs);\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text-muted);\n text-transform: uppercase;\n letter-spacing: var(--tempest-tracking-wider);\n}\n\n@keyframes tempest-menu-in {\n from {\n opacity: 0;\n transform: translateY(-4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .menu {\n animation: none;\n }\n}\n"],"mappings":";"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../utils/cn.cjs"),t=require("../Button/Button.cjs"),n=require("../../hooks/use-
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Button/Button.cjs"),n=require("../../hooks/use-install-prompt.cjs"),r=require("../InstallButton/install-hints.cjs"),i=require("./InstallBanner.module.cjs");let a=require("react"),o=require("react/jsx-runtime"),s=require("lucide-react");function c(e,t){if(!e||typeof window>`u`)return!1;try{let n=window.localStorage.getItem(e);if(!n)return!1;if(n===`1`)return!0;let r=Number(n);return Number.isFinite(r)?t===void 0||Date.now()-r<t:!1}catch{return!1}}function l({title:l=`Instale o app`,description:u,installLabel:d=`Instalar`,hintLabel:f=`Como instalar`,dismissLabel:p=`Dispensar`,icon:m,storageKey:h,declineCooldownMs:g,renderHint:_=r.defaultInstallHint,manualFallbackDelayMs:v,onResult:y,className:b}){let{method:x,openInChromeIntent:S,install:C}=n.useInstallPrompt({manualFallbackDelayMs:v}),[w,T]=(0,a.useState)(()=>c(h,g)),[E,D]=(0,a.useState)(!1),O=(0,a.useId)();if(x===`none`||w)return null;let k=()=>{if(T(!0),h&&typeof window<`u`)try{window.localStorage.setItem(h,g===void 0?`1`:String(Date.now()))}catch{}},A=x===`native`?null:_({method:x,openInChromeIntent:S});return(0,o.jsxs)(`div`,{className:e.cn(i.default.banner,b),role:`region`,"aria-label":String(l),children:[m&&(0,o.jsx)(`span`,{className:i.default.icon,children:m}),(0,o.jsxs)(`div`,{className:i.default.body,children:[(0,o.jsx)(`p`,{className:i.default.title,children:l}),u&&(0,o.jsx)(`p`,{className:i.default.description,children:u}),E&&A?(0,o.jsx)(`p`,{className:i.default.hint,id:O,children:A}):null]}),x===`native`?(0,o.jsx)(t.Button,{size:`sm`,onClick:async()=>{let e=await C();y?.(e?`accepted`:`dismissed`)},children:d}):(0,o.jsx)(t.Button,{size:`sm`,"aria-expanded":E,"aria-controls":E&&A?O:void 0,onClick:()=>D(e=>!e),children:f}),(0,o.jsx)(`button`,{type:`button`,className:i.default.close,"aria-label":p,onClick:k,children:(0,o.jsx)(s.X,{size:18,"aria-hidden":!0})})]})}exports.InstallBanner=l;
|
|
2
2
|
//# sourceMappingURL=InstallBanner.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallBanner.cjs","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — title, description, installLabel and dismissLabel\n * are four strings the app has to write, icon and className the look, storageKey the\n * dismissal memory and onResult the outcome. An install prompt with SDK-authored\n * copy is the one thing nobody ships.\n */\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"@/components/Button\";\nimport {
|
|
1
|
+
{"version":3,"file":"InstallBanner.cjs","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — title, description, installLabel and dismissLabel\n * are four strings the app has to write, icon and className the look, storageKey the\n * dismissal memory and onResult the outcome. An install prompt with SDK-authored\n * copy is the one thing nobody ships.\n */\nimport { useId, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"@/components/Button\";\nimport { useInstallPrompt } from \"@/hooks/use-install-prompt\";\nimport { defaultInstallHint } from \"@/components/InstallButton/install-hints\";\nimport type { RenderInstallHint } from \"@/components/InstallButton/install-hints\";\nimport type { InstallOutcome } from \"@/components/InstallButton\";\nimport styles from \"./InstallBanner.module.css\";\n\nexport interface InstallBannerProps {\n /** Headline. Default `\"Instale o app\"`. */\n title?: ReactNode;\n /** Supporting copy under the title. */\n description?: ReactNode;\n /** Install button label. Default `\"Instalar\"`. */\n installLabel?: string;\n /** Accessible label for the dismiss button. Default `\"Dispensar\"`. */\n dismissLabel?: string;\n /** Optional leading icon. */\n icon?: ReactNode;\n /**\n * `localStorage` key used to remember dismissal across reloads. Omit to\n * make dismissal last only for the current session (component state).\n */\n storageKey?: string;\n /**\n * How long, in ms, a dismissal lasts before the banner may return.\n *\n * Omitted, a dismissal written to `storageKey` is permanent, which is what\n * the component always did and what a stored `\"1\"` keeps meaning. With a\n * cooldown the dismissal is stored as a timestamp instead, so the banner\n * comes back after the window — the same shape `useInstallPrompt` uses for\n * its own decline.\n */\n declineCooldownMs?: number;\n /**\n * Install button label used when the browser cannot be prompted and the\n * banner shows the manual instruction. Default `\"Como instalar\"`.\n */\n hintLabel?: string;\n /**\n * Replaces the instruction shown for the `\"ios\"` and `\"manual\"` methods.\n *\n * The default copy is in PT-BR and names the real menu entries.\n */\n renderHint?: RenderInstallHint;\n /**\n * How long, in ms, to wait for `beforeinstallprompt` before falling back to\n * the manual instruction. Forwarded to {@link useInstallPrompt}; defaults to\n * 3000 there.\n */\n manualFallbackDelayMs?: number;\n /** Called with the user's choice after the install prompt resolves. */\n onResult?: (outcome: InstallOutcome) => void;\n className?: string;\n}\n\n/**\n * Whether the user already dismissed this banner.\n *\n * Two stored shapes, and both have to keep working: `\"1\"` is the permanent\n * dismissal the component has always written, and a timestamp is what a banner\n * with `declineCooldownMs` writes. A `\"1\"` found while a cooldown is configured\n * still counts as dismissed forever — it was written under the old contract, and\n * reviving a banner the user turned off is worse than keeping it off.\n *\n * @param storageKey - The key holding the dismissal, if any.\n * @param declineCooldownMs - How long a timestamped dismissal lasts.\n * @returns Whether the banner should stay hidden.\n */\nfunction readDismissed(storageKey?: string, declineCooldownMs?: number): boolean {\n if (!storageKey || typeof window === \"undefined\") return false;\n try {\n const raw = window.localStorage.getItem(storageKey);\n if (!raw) return false;\n if (raw === \"1\") return true;\n const dismissedAt = Number(raw);\n if (!Number.isFinite(dismissedAt)) return false;\n if (declineCooldownMs === undefined) return true;\n return Date.now() - dismissedAt < declineCooldownMs;\n } catch {\n return false;\n }\n}\n\n/**\n * Dismissible bottom banner that invites the user to install the PWA, wired to\n * {@link useInstallPrompt}.\n *\n * Where the browser fires `beforeinstallprompt`, the button installs. Where it\n * does not — iOS Safari, and the Android Chromium forks that strip the API — the\n * banner shows that platform's instruction instead of disappearing. It used to\n * disappear: built on `useBeforeInstallPrompt`, it rendered `null` exactly where\n * the user has no other way to find the install entry, which is why every app\n * kept a local copy of this component just to add the two missing paths.\n *\n * @tempest-limits empty-catch — persisting the dismissal is a courtesy, not the\n * feature. When `localStorage` refuses the write (quota, private mode) the banner\n * still hides for this session; the worst case is that it comes back next visit,\n * which beats an error thrown out of a click handler that only closed a banner.\n *\n * @example\n * <InstallBanner\n * title=\"Instale o FAMACHApp\"\n * description=\"Acesso offline e atalho na tela inicial.\"\n * storageKey=\"famacha:install-dismissed\"\n * />\n */\nexport function InstallBanner({\n title = \"Instale o app\",\n description,\n installLabel = \"Instalar\",\n hintLabel = \"Como instalar\",\n dismissLabel = \"Dispensar\",\n icon,\n storageKey,\n declineCooldownMs,\n renderHint = defaultInstallHint,\n manualFallbackDelayMs,\n onResult,\n className,\n}: InstallBannerProps) {\n const { method, openInChromeIntent, install } = useInstallPrompt({ manualFallbackDelayMs });\n const [dismissed, setDismissed] = useState<boolean>(() =>\n readDismissed(storageKey, declineCooldownMs),\n );\n const [hintOpen, setHintOpen] = useState(false);\n const hintId = useId();\n\n if (method === \"none\" || dismissed) return null;\n\n const dismiss = (): void => {\n setDismissed(true);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(\n storageKey,\n declineCooldownMs === undefined ? \"1\" : String(Date.now()),\n );\n } catch {\n /* empty */\n }\n }\n };\n\n const hint = method === \"native\" ? null : renderHint({ method, openInChromeIntent });\n\n return (\n <div className={cn(styles.banner, className)} role=\"region\" aria-label={String(title)}>\n {icon && <span className={styles.icon}>{icon}</span>}\n <div className={styles.body}>\n <p className={styles.title}>{title}</p>\n {description && <p className={styles.description}>{description}</p>}\n {hintOpen && hint ? (\n <p className={styles.hint} id={hintId}>\n {hint}\n </p>\n ) : null}\n </div>\n {method === \"native\" ? (\n <Button\n size=\"sm\"\n onClick={async () => {\n const accepted = await install();\n onResult?.(accepted ? \"accepted\" : \"dismissed\");\n }}\n >\n {installLabel}\n </Button>\n ) : (\n <Button\n size=\"sm\"\n aria-expanded={hintOpen}\n aria-controls={hintOpen && hint ? hintId : undefined}\n onClick={() => setHintOpen((open) => !open)}\n >\n {hintLabel}\n </Button>\n )}\n <button\n type=\"button\"\n className={styles.close}\n aria-label={dismissLabel}\n onClick={dismiss}\n >\n <X size={18} aria-hidden />\n </button>\n </div>\n );\n}\n"],"mappings":"gSA8EA,SAAS,EAAc,EAAqB,EAAqC,CAC7E,GAAI,CAAC,GAAc,OAAO,OAAW,IAAa,MAAO,GACzD,GAAI,CACA,IAAM,EAAM,OAAO,aAAa,QAAQ,CAAU,EAClD,GAAI,CAAC,EAAK,MAAO,GACjB,GAAI,IAAQ,IAAK,MAAO,GACxB,IAAM,EAAc,OAAO,CAAG,EAG9B,OAFK,OAAO,SAAS,CAAW,EAC5B,IAAsB,IAAA,IACnB,KAAK,IAAI,EAAI,EAAc,EAFQ,EAG9C,MAAQ,CACJ,MAAO,EACX,CACJ,CAyBA,SAAgB,EAAc,CAC1B,QAAQ,gBACR,cACA,eAAe,WACf,YAAY,gBACZ,eAAe,YACf,OACA,aACA,oBACA,aAAa,EAAA,mBACb,wBACA,WACA,aACmB,CACnB,GAAM,CAAE,SAAQ,qBAAoB,WAAY,EAAA,iBAAiB,CAAE,uBAAsB,CAAC,EACpF,CAAC,EAAW,IAAA,EAAgB,EAAA,SAAA,KAC9B,EAAc,EAAY,CAAiB,CAC/C,EACM,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAS,EAAK,EACxC,GAAA,EAAS,EAAA,MAAA,CAAM,EAErB,GAAI,IAAW,QAAU,EAAW,OAAO,KAE3C,IAAM,MAAsB,CAExB,GADA,EAAa,EAAI,EACb,GAAc,OAAO,OAAW,IAChC,GAAI,CACA,OAAO,aAAa,QAChB,EACA,IAAsB,IAAA,GAAY,IAAM,OAAO,KAAK,IAAI,CAAC,CAC7D,CACJ,MAAQ,CAER,CAER,EAEM,EAAO,IAAW,SAAW,KAAO,EAAW,CAAE,SAAQ,oBAAmB,CAAC,EAEnF,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,OAAQ,CAAS,EAAG,KAAK,SAAS,aAAY,OAAO,CAAK,EAApF,SAAA,CACK,IAAQ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAA,QAAO,KAAO,SAAA,CAAW,CAAA,GACnD,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,QAAO,KAAvB,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,MAAQ,SAAA,CAAS,CAAA,EACrC,IAAe,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,YAAc,SAAA,CAAe,CAAA,EACjE,GAAY,GACT,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,KAAM,GAAI,EAC1B,SAAA,CACF,CAAA,EACH,IACH,IACJ,IAAW,UACR,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,KAAK,KACL,QAAS,SAAY,CACjB,IAAM,EAAW,MAAM,EAAQ,EAC/B,IAAW,EAAW,WAAa,WAAW,CAClD,EAEC,SAAA,CACG,CAAA,GAER,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,KAAK,KACL,gBAAe,EACf,gBAAe,GAAY,EAAO,EAAS,IAAA,GAC3C,YAAe,EAAa,GAAS,CAAC,CAAI,EAEzC,SAAA,CACG,CAAA,GAEZ,EAAA,EAAA,IAAA,CAAC,SAAD,CACI,KAAK,SACL,UAAW,EAAA,QAAO,MAClB,aAAY,EACZ,QAAS,EAET,UAAA,EAAA,EAAA,IAAA,CAAC,EAAA,EAAD,CAAG,KAAM,GAAI,cAAA,EAAa,CAAA,CACtB,CAAA,CACP,GAEb"}
|
|
@@ -1,57 +1,82 @@
|
|
|
1
1
|
import { cn as e } from "../../utils/cn.js";
|
|
2
2
|
import { Button as t } from "../Button/Button.js";
|
|
3
|
-
import {
|
|
4
|
-
import r from "
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { useInstallPrompt as n } from "../../hooks/use-install-prompt.js";
|
|
4
|
+
import { defaultInstallHint as r } from "../InstallButton/install-hints.js";
|
|
5
|
+
import i from "./InstallBanner.module.js";
|
|
6
|
+
import { useId as a, useState as o } from "react";
|
|
7
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
8
|
+
import { X as l } from "lucide-react";
|
|
8
9
|
//#region src/components/InstallBanner/InstallBanner.tsx
|
|
9
|
-
function
|
|
10
|
+
function u(e, t) {
|
|
10
11
|
if (!e || typeof window > "u") return !1;
|
|
11
12
|
try {
|
|
12
|
-
|
|
13
|
+
let n = window.localStorage.getItem(e);
|
|
14
|
+
if (!n) return !1;
|
|
15
|
+
if (n === "1") return !0;
|
|
16
|
+
let r = Number(n);
|
|
17
|
+
return Number.isFinite(r) ? t === void 0 || Date.now() - r < t : !1;
|
|
13
18
|
} catch {
|
|
14
19
|
return !1;
|
|
15
20
|
}
|
|
16
21
|
}
|
|
17
|
-
function
|
|
18
|
-
let {
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
function d({ title: d = "Instale o app", description: f, installLabel: p = "Instalar", hintLabel: m = "Como instalar", dismissLabel: h = "Dispensar", icon: g, storageKey: _, declineCooldownMs: v, renderHint: y = r, manualFallbackDelayMs: b, onResult: x, className: S }) {
|
|
23
|
+
let { method: C, openInChromeIntent: w, install: T } = n({ manualFallbackDelayMs: b }), [E, D] = o(() => u(_, v)), [O, k] = o(!1), A = a();
|
|
24
|
+
if (C === "none" || E) return null;
|
|
25
|
+
let j = () => {
|
|
26
|
+
if (D(!0), _ && typeof window < "u") try {
|
|
27
|
+
window.localStorage.setItem(_, v === void 0 ? "1" : String(Date.now()));
|
|
28
|
+
} catch {}
|
|
29
|
+
}, M = C === "native" ? null : y({
|
|
30
|
+
method: C,
|
|
31
|
+
openInChromeIntent: w
|
|
32
|
+
});
|
|
33
|
+
return /* @__PURE__ */ c("div", {
|
|
34
|
+
className: e(i.banner, S),
|
|
21
35
|
role: "region",
|
|
22
|
-
"aria-label": String(
|
|
36
|
+
"aria-label": String(d),
|
|
23
37
|
children: [
|
|
24
|
-
|
|
25
|
-
className:
|
|
26
|
-
children:
|
|
38
|
+
g && /* @__PURE__ */ s("span", {
|
|
39
|
+
className: i.icon,
|
|
40
|
+
children: g
|
|
27
41
|
}),
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
-
className:
|
|
30
|
-
children: [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
/* @__PURE__ */ c("div", {
|
|
43
|
+
className: i.body,
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ s("p", {
|
|
46
|
+
className: i.title,
|
|
47
|
+
children: d
|
|
48
|
+
}),
|
|
49
|
+
f && /* @__PURE__ */ s("p", {
|
|
50
|
+
className: i.description,
|
|
51
|
+
children: f
|
|
52
|
+
}),
|
|
53
|
+
O && M ? /* @__PURE__ */ s("p", {
|
|
54
|
+
className: i.hint,
|
|
55
|
+
id: A,
|
|
56
|
+
children: M
|
|
57
|
+
}) : null
|
|
58
|
+
]
|
|
37
59
|
}),
|
|
38
|
-
/* @__PURE__ */
|
|
60
|
+
C === "native" ? /* @__PURE__ */ s(t, {
|
|
39
61
|
size: "sm",
|
|
40
62
|
onClick: async () => {
|
|
41
|
-
|
|
63
|
+
let e = await T();
|
|
64
|
+
x?.(e ? "accepted" : "dismissed");
|
|
42
65
|
},
|
|
43
|
-
children:
|
|
66
|
+
children: p
|
|
67
|
+
}) : /* @__PURE__ */ s(t, {
|
|
68
|
+
size: "sm",
|
|
69
|
+
"aria-expanded": O,
|
|
70
|
+
"aria-controls": O && M ? A : void 0,
|
|
71
|
+
onClick: () => k((e) => !e),
|
|
72
|
+
children: m
|
|
44
73
|
}),
|
|
45
|
-
/* @__PURE__ */
|
|
74
|
+
/* @__PURE__ */ s("button", {
|
|
46
75
|
type: "button",
|
|
47
|
-
className:
|
|
48
|
-
"aria-label":
|
|
49
|
-
onClick:
|
|
50
|
-
|
|
51
|
-
window.localStorage.setItem(m, "1");
|
|
52
|
-
} catch {}
|
|
53
|
-
},
|
|
54
|
-
children: /* @__PURE__ */ a(s, {
|
|
76
|
+
className: i.close,
|
|
77
|
+
"aria-label": h,
|
|
78
|
+
onClick: j,
|
|
79
|
+
children: /* @__PURE__ */ s(l, {
|
|
55
80
|
size: 18,
|
|
56
81
|
"aria-hidden": !0
|
|
57
82
|
})
|
|
@@ -60,6 +85,6 @@ function l({ title: l = "Instale o app", description: u, installLabel: d = "Inst
|
|
|
60
85
|
});
|
|
61
86
|
}
|
|
62
87
|
//#endregion
|
|
63
|
-
export {
|
|
88
|
+
export { d as InstallBanner };
|
|
64
89
|
|
|
65
90
|
//# sourceMappingURL=InstallBanner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallBanner.js","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — title, description, installLabel and dismissLabel\n * are four strings the app has to write, icon and className the look, storageKey the\n * dismissal memory and onResult the outcome. An install prompt with SDK-authored\n * copy is the one thing nobody ships.\n */\nimport { useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"@/components/Button\";\nimport {
|
|
1
|
+
{"version":3,"file":"InstallBanner.js","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.tsx"],"sourcesContent":["/**\n * @tempest-limits props-count — title, description, installLabel and dismissLabel\n * are four strings the app has to write, icon and className the look, storageKey the\n * dismissal memory and onResult the outcome. An install prompt with SDK-authored\n * copy is the one thing nobody ships.\n */\nimport { useId, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@/utils/cn\";\nimport { Button } from \"@/components/Button\";\nimport { useInstallPrompt } from \"@/hooks/use-install-prompt\";\nimport { defaultInstallHint } from \"@/components/InstallButton/install-hints\";\nimport type { RenderInstallHint } from \"@/components/InstallButton/install-hints\";\nimport type { InstallOutcome } from \"@/components/InstallButton\";\nimport styles from \"./InstallBanner.module.css\";\n\nexport interface InstallBannerProps {\n /** Headline. Default `\"Instale o app\"`. */\n title?: ReactNode;\n /** Supporting copy under the title. */\n description?: ReactNode;\n /** Install button label. Default `\"Instalar\"`. */\n installLabel?: string;\n /** Accessible label for the dismiss button. Default `\"Dispensar\"`. */\n dismissLabel?: string;\n /** Optional leading icon. */\n icon?: ReactNode;\n /**\n * `localStorage` key used to remember dismissal across reloads. Omit to\n * make dismissal last only for the current session (component state).\n */\n storageKey?: string;\n /**\n * How long, in ms, a dismissal lasts before the banner may return.\n *\n * Omitted, a dismissal written to `storageKey` is permanent, which is what\n * the component always did and what a stored `\"1\"` keeps meaning. With a\n * cooldown the dismissal is stored as a timestamp instead, so the banner\n * comes back after the window — the same shape `useInstallPrompt` uses for\n * its own decline.\n */\n declineCooldownMs?: number;\n /**\n * Install button label used when the browser cannot be prompted and the\n * banner shows the manual instruction. Default `\"Como instalar\"`.\n */\n hintLabel?: string;\n /**\n * Replaces the instruction shown for the `\"ios\"` and `\"manual\"` methods.\n *\n * The default copy is in PT-BR and names the real menu entries.\n */\n renderHint?: RenderInstallHint;\n /**\n * How long, in ms, to wait for `beforeinstallprompt` before falling back to\n * the manual instruction. Forwarded to {@link useInstallPrompt}; defaults to\n * 3000 there.\n */\n manualFallbackDelayMs?: number;\n /** Called with the user's choice after the install prompt resolves. */\n onResult?: (outcome: InstallOutcome) => void;\n className?: string;\n}\n\n/**\n * Whether the user already dismissed this banner.\n *\n * Two stored shapes, and both have to keep working: `\"1\"` is the permanent\n * dismissal the component has always written, and a timestamp is what a banner\n * with `declineCooldownMs` writes. A `\"1\"` found while a cooldown is configured\n * still counts as dismissed forever — it was written under the old contract, and\n * reviving a banner the user turned off is worse than keeping it off.\n *\n * @param storageKey - The key holding the dismissal, if any.\n * @param declineCooldownMs - How long a timestamped dismissal lasts.\n * @returns Whether the banner should stay hidden.\n */\nfunction readDismissed(storageKey?: string, declineCooldownMs?: number): boolean {\n if (!storageKey || typeof window === \"undefined\") return false;\n try {\n const raw = window.localStorage.getItem(storageKey);\n if (!raw) return false;\n if (raw === \"1\") return true;\n const dismissedAt = Number(raw);\n if (!Number.isFinite(dismissedAt)) return false;\n if (declineCooldownMs === undefined) return true;\n return Date.now() - dismissedAt < declineCooldownMs;\n } catch {\n return false;\n }\n}\n\n/**\n * Dismissible bottom banner that invites the user to install the PWA, wired to\n * {@link useInstallPrompt}.\n *\n * Where the browser fires `beforeinstallprompt`, the button installs. Where it\n * does not — iOS Safari, and the Android Chromium forks that strip the API — the\n * banner shows that platform's instruction instead of disappearing. It used to\n * disappear: built on `useBeforeInstallPrompt`, it rendered `null` exactly where\n * the user has no other way to find the install entry, which is why every app\n * kept a local copy of this component just to add the two missing paths.\n *\n * @tempest-limits empty-catch — persisting the dismissal is a courtesy, not the\n * feature. When `localStorage` refuses the write (quota, private mode) the banner\n * still hides for this session; the worst case is that it comes back next visit,\n * which beats an error thrown out of a click handler that only closed a banner.\n *\n * @example\n * <InstallBanner\n * title=\"Instale o FAMACHApp\"\n * description=\"Acesso offline e atalho na tela inicial.\"\n * storageKey=\"famacha:install-dismissed\"\n * />\n */\nexport function InstallBanner({\n title = \"Instale o app\",\n description,\n installLabel = \"Instalar\",\n hintLabel = \"Como instalar\",\n dismissLabel = \"Dispensar\",\n icon,\n storageKey,\n declineCooldownMs,\n renderHint = defaultInstallHint,\n manualFallbackDelayMs,\n onResult,\n className,\n}: InstallBannerProps) {\n const { method, openInChromeIntent, install } = useInstallPrompt({ manualFallbackDelayMs });\n const [dismissed, setDismissed] = useState<boolean>(() =>\n readDismissed(storageKey, declineCooldownMs),\n );\n const [hintOpen, setHintOpen] = useState(false);\n const hintId = useId();\n\n if (method === \"none\" || dismissed) return null;\n\n const dismiss = (): void => {\n setDismissed(true);\n if (storageKey && typeof window !== \"undefined\") {\n try {\n window.localStorage.setItem(\n storageKey,\n declineCooldownMs === undefined ? \"1\" : String(Date.now()),\n );\n } catch {\n /* empty */\n }\n }\n };\n\n const hint = method === \"native\" ? null : renderHint({ method, openInChromeIntent });\n\n return (\n <div className={cn(styles.banner, className)} role=\"region\" aria-label={String(title)}>\n {icon && <span className={styles.icon}>{icon}</span>}\n <div className={styles.body}>\n <p className={styles.title}>{title}</p>\n {description && <p className={styles.description}>{description}</p>}\n {hintOpen && hint ? (\n <p className={styles.hint} id={hintId}>\n {hint}\n </p>\n ) : null}\n </div>\n {method === \"native\" ? (\n <Button\n size=\"sm\"\n onClick={async () => {\n const accepted = await install();\n onResult?.(accepted ? \"accepted\" : \"dismissed\");\n }}\n >\n {installLabel}\n </Button>\n ) : (\n <Button\n size=\"sm\"\n aria-expanded={hintOpen}\n aria-controls={hintOpen && hint ? hintId : undefined}\n onClick={() => setHintOpen((open) => !open)}\n >\n {hintLabel}\n </Button>\n )}\n <button\n type=\"button\"\n className={styles.close}\n aria-label={dismissLabel}\n onClick={dismiss}\n >\n <X size={18} aria-hidden />\n </button>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AA8EA,SAAS,EAAc,GAAqB,GAAqC;CAC7E,IAAI,CAAC,KAAc,OAAO,SAAW,KAAa,OAAO;CACzD,IAAI;EACA,IAAM,IAAM,OAAO,aAAa,QAAQ,CAAU;EAClD,IAAI,CAAC,GAAK,OAAO;EACjB,IAAI,MAAQ,KAAK,OAAO;EACxB,IAAM,IAAc,OAAO,CAAG;EAG9B,OAFK,OAAO,SAAS,CAAW,IAC5B,MAAsB,KAAA,KACnB,KAAK,IAAI,IAAI,IAAc,IAFQ;CAG9C,QAAQ;EACJ,OAAO;CACX;AACJ;AAyBA,SAAgB,EAAc,EAC1B,WAAQ,iBACR,gBACA,kBAAe,YACf,eAAY,iBACZ,kBAAe,aACf,SACA,eACA,sBACA,gBAAa,GACb,0BACA,aACA,gBACmB;CACnB,IAAM,EAAE,WAAQ,uBAAoB,eAAY,EAAiB,EAAE,yBAAsB,CAAC,GACpF,CAAC,GAAW,KAAgB,QAC9B,EAAc,GAAY,CAAiB,CAC/C,GACM,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,IAAS,EAAM;CAErB,IAAI,MAAW,UAAU,GAAW,OAAO;CAE3C,IAAM,UAAsB;EAExB,IADA,EAAa,EAAI,GACb,KAAc,OAAO,SAAW,KAChC,IAAI;GACA,OAAO,aAAa,QAChB,GACA,MAAsB,KAAA,IAAY,MAAM,OAAO,KAAK,IAAI,CAAC,CAC7D;EACJ,QAAQ,CAER;CAER,GAEM,IAAO,MAAW,WAAW,OAAO,EAAW;EAAE;EAAQ;CAAmB,CAAC;CAEnF,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,QAAQ,CAAS;EAAG,MAAK;EAAS,cAAY,OAAO,CAAK;EAApF,UAAA;GACK,KAAQ,kBAAC,QAAD;IAAM,WAAW,EAAO;IAAO,UAAA;GAAW,CAAA;GACnD,kBAAC,OAAD;IAAK,WAAW,EAAO;IAAvB,UAAA;KACI,kBAAC,KAAD;MAAG,WAAW,EAAO;MAAQ,UAAA;KAAS,CAAA;KACrC,KAAe,kBAAC,KAAD;MAAG,WAAW,EAAO;MAAc,UAAA;KAAe,CAAA;KACjE,KAAY,IACT,kBAAC,KAAD;MAAG,WAAW,EAAO;MAAM,IAAI;MAC1B,UAAA;KACF,CAAA,IACH;IACH;;GACJ,MAAW,WACR,kBAAC,GAAD;IACI,MAAK;IACL,SAAS,YAAY;KACjB,IAAM,IAAW,MAAM,EAAQ;KAC/B,IAAW,IAAW,aAAa,WAAW;IAClD;IAEC,UAAA;GACG,CAAA,IAER,kBAAC,GAAD;IACI,MAAK;IACL,iBAAe;IACf,iBAAe,KAAY,IAAO,IAAS,KAAA;IAC3C,eAAe,GAAa,MAAS,CAAC,CAAI;IAEzC,UAAA;GACG,CAAA;GAEZ,kBAAC,UAAD;IACI,MAAK;IACL,WAAW,EAAO;IAClB,cAAY;IACZ,SAAS;IAET,UAAA,kBAAC,GAAD;KAAG,MAAM;KAAI,eAAA;IAAa,CAAA;GACtB,CAAA;EACP;;AAEb"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
require("../../styles/component/InstallBanner.css");
|
|
2
|
-
var e=`tempest_banner_0oCIc`,t=`tempest_icon_izjRL`,n=`tempest_body_9JAQv`,r=`tempest_title_1E50X`,i=`tempest_description_QY25-`,a=`tempest_close_RN6FO`,o={banner:e,icon:t,body:n,title:r,description:i,close:a};exports.banner=e,exports.body=n,exports.close=a,exports.default=
|
|
2
|
+
var e=`tempest_banner_0oCIc`,t=`tempest_icon_izjRL`,n=`tempest_body_9JAQv`,r=`tempest_title_1E50X`,i=`tempest_description_QY25-`,a=`tempest_close_RN6FO`,o=`tempest_hint_aYJgR`,s={banner:e,icon:t,body:n,title:r,description:i,close:a,hint:o};exports.banner=e,exports.body=n,exports.close=a,exports.default=s,exports.description=i,exports.hint=o,exports.icon=t,exports.title=r;
|
|
3
3
|
//# sourceMappingURL=InstallBanner.module.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallBanner.module.cjs","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.module.css"],"sourcesContent":[".banner {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-radius: var(--tempest-radius-lg);\n border: 1px solid var(--tempest-border);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n box-shadow: var(--tempest-shadow-lg);\n width: 100%;\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-snug);\n}\n\n.icon {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n line-height: 1;\n color: var(--tempest-primary);\n}\n\n.body {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.title {\n margin: 0;\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n}\n\n.description {\n margin: var(--tempest-space-1) 0 0;\n color: var(--tempest-text-muted);\n}\n\n.close {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: 0;\n color: var(--tempest-text-muted);\n cursor: pointer;\n padding: 0;\n width: 28px;\n height: 28px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.close:hover {\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"InstallBanner.module.cjs","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.module.css"],"sourcesContent":[".banner {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-radius: var(--tempest-radius-lg);\n border: 1px solid var(--tempest-border);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n box-shadow: var(--tempest-shadow-lg);\n width: 100%;\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-snug);\n}\n\n.icon {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n line-height: 1;\n color: var(--tempest-primary);\n}\n\n.body {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.title {\n margin: 0;\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n}\n\n.description {\n margin: var(--tempest-space-1) 0 0;\n color: var(--tempest-text-muted);\n}\n\n.close {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: 0;\n color: var(--tempest-text-muted);\n cursor: pointer;\n padding: 0;\n width: 28px;\n height: 28px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.close:hover {\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n\n.hint {\n margin: var(--tempest-space-2) 0 0;\n color: var(--tempest-text-muted);\n}\n\n.hint a {\n color: var(--tempest-primary);\n}\n"],"mappings":";"}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import "../../styles/component/InstallBanner.css";
|
|
2
2
|
//#region src/components/InstallBanner/InstallBanner.module.css
|
|
3
|
-
var e = "tempest_banner_0oCIc", t = "tempest_icon_izjRL", n = "tempest_body_9JAQv", r = "tempest_title_1E50X", i = "tempest_description_QY25-", a = "tempest_close_RN6FO", o = {
|
|
3
|
+
var e = "tempest_banner_0oCIc", t = "tempest_icon_izjRL", n = "tempest_body_9JAQv", r = "tempest_title_1E50X", i = "tempest_description_QY25-", a = "tempest_close_RN6FO", o = "tempest_hint_aYJgR", s = {
|
|
4
4
|
banner: e,
|
|
5
5
|
icon: t,
|
|
6
6
|
body: n,
|
|
7
7
|
title: r,
|
|
8
8
|
description: i,
|
|
9
|
-
close: a
|
|
9
|
+
close: a,
|
|
10
|
+
hint: o
|
|
10
11
|
};
|
|
11
12
|
//#endregion
|
|
12
|
-
export { e as banner, n as body, a as close,
|
|
13
|
+
export { e as banner, n as body, a as close, s as default, i as description, o as hint, t as icon, r as title };
|
|
13
14
|
|
|
14
15
|
//# sourceMappingURL=InstallBanner.module.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallBanner.module.js","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.module.css"],"sourcesContent":[".banner {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-radius: var(--tempest-radius-lg);\n border: 1px solid var(--tempest-border);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n box-shadow: var(--tempest-shadow-lg);\n width: 100%;\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-snug);\n}\n\n.icon {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n line-height: 1;\n color: var(--tempest-primary);\n}\n\n.body {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.title {\n margin: 0;\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n}\n\n.description {\n margin: var(--tempest-space-1) 0 0;\n color: var(--tempest-text-muted);\n}\n\n.close {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: 0;\n color: var(--tempest-text-muted);\n cursor: pointer;\n padding: 0;\n width: 28px;\n height: 28px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.close:hover {\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n"],"mappings":";"}
|
|
1
|
+
{"version":3,"file":"InstallBanner.module.js","names":[],"sources":["../../../src/components/InstallBanner/InstallBanner.module.css"],"sourcesContent":[".banner {\n display: flex;\n align-items: center;\n gap: var(--tempest-space-3);\n padding: var(--tempest-space-3) var(--tempest-space-4);\n border-radius: var(--tempest-radius-lg);\n border: 1px solid var(--tempest-border);\n background: var(--tempest-surface);\n color: var(--tempest-text);\n box-shadow: var(--tempest-shadow-lg);\n width: 100%;\n font-family: var(--tempest-font-sans);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-snug);\n}\n\n.icon {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n line-height: 1;\n color: var(--tempest-primary);\n}\n\n.body {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.title {\n margin: 0;\n font-weight: var(--tempest-weight-semibold);\n color: var(--tempest-text);\n}\n\n.description {\n margin: var(--tempest-space-1) 0 0;\n color: var(--tempest-text-muted);\n}\n\n.close {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: 0;\n color: var(--tempest-text-muted);\n cursor: pointer;\n padding: 0;\n width: 28px;\n height: 28px;\n border-radius: var(--tempest-radius-sm);\n}\n\n.close:hover {\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n\n.hint {\n margin: var(--tempest-space-2) 0 0;\n color: var(--tempest-text-muted);\n}\n\n.hint a {\n color: var(--tempest-primary);\n}\n"],"mappings":";"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../Button/Button.cjs"),
|
|
1
|
+
const e=require("../../utils/cn.cjs"),t=require("../Button/Button.cjs"),n=require("../../hooks/use-install-prompt.cjs"),r=require("./install-hints.cjs"),i=require("./InstallButton.module.cjs");let a=require("react"),o=require("react/jsx-runtime");function s({label:s=`Instalar app`,hintLabel:c=`Como instalar`,onResult:l,renderHint:u=r.defaultInstallHint,manualFallbackDelayMs:d,wrapperClassName:f,...p}){let{method:m,openInChromeIntent:h,install:g}=n.useInstallPrompt({manualFallbackDelayMs:d}),[_,v]=(0,a.useState)(!1),y=(0,a.useId)();if(m===`none`)return null;if(m===`native`)return(0,o.jsx)(t.Button,{onClick:async()=>{let e=await g();l?.(e?`accepted`:`dismissed`)},...p,children:s});let b=u({method:m,openInChromeIntent:h});return(0,o.jsxs)(`div`,{className:e.cn(i.default.wrapper,f),children:[(0,o.jsx)(t.Button,{"aria-expanded":_,"aria-controls":_&&b?y:void 0,onClick:()=>v(e=>!e),...p,children:c}),_&&b?(0,o.jsx)(`p`,{className:i.default.hint,id:y,children:b}):null]})}exports.InstallButton=s;
|
|
2
2
|
//# sourceMappingURL=InstallButton.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallButton.cjs","names":[],"sources":["../../../src/components/InstallButton/InstallButton.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Button, type ButtonProps } from \"@/components/Button\";\nimport {
|
|
1
|
+
{"version":3,"file":"InstallButton.cjs","names":[],"sources":["../../../src/components/InstallButton/InstallButton.tsx"],"sourcesContent":["import { useId, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Button, type ButtonProps } from \"@/components/Button\";\nimport { useInstallPrompt } from \"@/hooks/use-install-prompt\";\nimport { cn } from \"@/utils/cn\";\nimport { defaultInstallHint, type RenderInstallHint } from \"./install-hints\";\nimport styles from \"./InstallButton.module.css\";\n\nexport type InstallOutcome = \"accepted\" | \"dismissed\" | \"unsupported\";\n\nexport interface InstallButtonProps extends Omit<ButtonProps, \"onClick\" | \"children\"> {\n /** Button label. Default `\"Instalar app\"`. */\n label?: ReactNode;\n /**\n * Label used when the browser cannot be prompted and the button reveals the\n * manual instruction instead. Default `\"Como instalar\"`.\n */\n hintLabel?: ReactNode;\n /** Called with the user's choice after the install prompt resolves. */\n onResult?: (outcome: InstallOutcome) => void;\n /**\n * Replaces the instruction shown for the `\"ios\"` and `\"manual\"` methods.\n *\n * The default copy is in PT-BR and names the real menu entries. Pass this to\n * translate it, to shorten it, or to point at a screenshot of your own.\n */\n renderHint?: RenderInstallHint;\n /**\n * How long, in ms, to wait for `beforeinstallprompt` before falling back to\n * the manual instruction. Forwarded to {@link useInstallPrompt}; defaults to\n * 3000 there.\n */\n manualFallbackDelayMs?: number;\n /** Class applied to the wrapper that holds the button and the instruction. */\n wrapperClassName?: string;\n}\n\n/**\n * Button wired to PWA installation, through {@link useInstallPrompt}.\n *\n * Where the browser fires `beforeinstallprompt`, the click installs. Where it\n * does not — iOS Safari, and the Android Chromium forks (Mi Browser, UC, Opera\n * Mini, Huawei) that strip the API — the click reveals the instruction for that\n * platform instead of the button disappearing.\n *\n * That disappearance was the defect: the component was built on\n * `useBeforeInstallPrompt`, so it rendered `null` exactly where the user needs\n * the most help, and every app re-implemented the iOS and manual paths locally.\n * Nothing renders only when there is genuinely nothing to offer — already\n * installed, running standalone, or inside the decline cooldown.\n *\n * Inherits every {@link Button} prop (`variant`, `size`, `leftIcon`, …).\n *\n * @example\n * <InstallButton variant=\"primary\" leftIcon={<Download />} />\n *\n * @example\n * <InstallButton renderHint={({ method }) => (method === \"ios\" ? <IOSSteps /> : <MenuSteps />)} />\n */\nexport function InstallButton({\n label = \"Instalar app\",\n hintLabel = \"Como instalar\",\n onResult,\n renderHint = defaultInstallHint,\n manualFallbackDelayMs,\n wrapperClassName,\n ...props\n}: InstallButtonProps) {\n const { method, openInChromeIntent, install } = useInstallPrompt({ manualFallbackDelayMs });\n const [hintOpen, setHintOpen] = useState(false);\n const hintId = useId();\n\n if (method === \"none\") return null;\n\n if (method === \"native\") {\n return (\n <Button\n onClick={async () => {\n const accepted = await install();\n onResult?.(accepted ? \"accepted\" : \"dismissed\");\n }}\n {...props}\n >\n {label}\n </Button>\n );\n }\n\n const hint = renderHint({ method, openInChromeIntent });\n\n return (\n <div className={cn(styles.wrapper, wrapperClassName)}>\n <Button\n aria-expanded={hintOpen}\n aria-controls={hintOpen && hint ? hintId : undefined}\n onClick={() => setHintOpen((open) => !open)}\n {...props}\n >\n {hintLabel}\n </Button>\n {hintOpen && hint ? (\n <p className={styles.hint} id={hintId}>\n {hint}\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":"uPA2DA,SAAgB,EAAc,CAC1B,QAAQ,eACR,YAAY,gBACZ,WACA,aAAa,EAAA,mBACb,wBACA,mBACA,GAAG,GACgB,CACnB,GAAM,CAAE,SAAQ,qBAAoB,WAAY,EAAA,iBAAiB,CAAE,uBAAsB,CAAC,EACpF,CAAC,EAAU,IAAA,EAAe,EAAA,SAAA,CAAS,EAAK,EACxC,GAAA,EAAS,EAAA,MAAA,CAAM,EAErB,GAAI,IAAW,OAAQ,OAAO,KAE9B,GAAI,IAAW,SACX,OACI,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,QAAS,SAAY,CACjB,IAAM,EAAW,MAAM,EAAQ,EAC/B,IAAW,EAAW,WAAa,WAAW,CAClD,EACA,GAAI,EAEH,SAAA,CACG,CAAA,EAIhB,IAAM,EAAO,EAAW,CAAE,SAAQ,oBAAmB,CAAC,EAEtD,OACI,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAA,GAAG,EAAA,QAAO,QAAS,CAAgB,EAAnD,SAAA,EACI,EAAA,EAAA,IAAA,CAAC,EAAA,OAAD,CACI,gBAAe,EACf,gBAAe,GAAY,EAAO,EAAS,IAAA,GAC3C,YAAe,EAAa,GAAS,CAAC,CAAI,EAC1C,GAAI,EAEH,SAAA,CACG,CAAA,EACP,GAAY,GACT,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAA,QAAO,KAAM,GAAI,EAC1B,SAAA,CACF,CAAA,EACH,IACH,GAEb"}
|
|
@@ -1,18 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { cn as e } from "../../utils/cn.js";
|
|
2
|
+
import { Button as t } from "../Button/Button.js";
|
|
3
|
+
import { useInstallPrompt as n } from "../../hooks/use-install-prompt.js";
|
|
4
|
+
import { defaultInstallHint as r } from "./install-hints.js";
|
|
5
|
+
import i from "./InstallButton.module.js";
|
|
6
|
+
import { useId as a, useState as o } from "react";
|
|
7
|
+
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
4
8
|
//#region src/components/InstallButton/InstallButton.tsx
|
|
5
|
-
function
|
|
6
|
-
let {
|
|
7
|
-
|
|
9
|
+
function l({ label: l = "Instalar app", hintLabel: u = "Como instalar", onResult: d, renderHint: f = r, manualFallbackDelayMs: p, wrapperClassName: m, ...h }) {
|
|
10
|
+
let { method: g, openInChromeIntent: _, install: v } = n({ manualFallbackDelayMs: p }), [y, b] = o(!1), x = a();
|
|
11
|
+
if (g === "none") return null;
|
|
12
|
+
if (g === "native") return /* @__PURE__ */ s(t, {
|
|
8
13
|
onClick: async () => {
|
|
9
|
-
|
|
14
|
+
let e = await v();
|
|
15
|
+
d?.(e ? "accepted" : "dismissed");
|
|
10
16
|
},
|
|
11
|
-
...
|
|
12
|
-
children:
|
|
17
|
+
...h,
|
|
18
|
+
children: l
|
|
19
|
+
});
|
|
20
|
+
let S = f({
|
|
21
|
+
method: g,
|
|
22
|
+
openInChromeIntent: _
|
|
23
|
+
});
|
|
24
|
+
return /* @__PURE__ */ c("div", {
|
|
25
|
+
className: e(i.wrapper, m),
|
|
26
|
+
children: [/* @__PURE__ */ s(t, {
|
|
27
|
+
"aria-expanded": y,
|
|
28
|
+
"aria-controls": y && S ? x : void 0,
|
|
29
|
+
onClick: () => b((e) => !e),
|
|
30
|
+
...h,
|
|
31
|
+
children: u
|
|
32
|
+
}), y && S ? /* @__PURE__ */ s("p", {
|
|
33
|
+
className: i.hint,
|
|
34
|
+
id: x,
|
|
35
|
+
children: S
|
|
36
|
+
}) : null]
|
|
13
37
|
});
|
|
14
38
|
}
|
|
15
39
|
//#endregion
|
|
16
|
-
export {
|
|
40
|
+
export { l as InstallButton };
|
|
17
41
|
|
|
18
42
|
//# sourceMappingURL=InstallButton.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InstallButton.js","names":[],"sources":["../../../src/components/InstallButton/InstallButton.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { Button, type ButtonProps } from \"@/components/Button\";\nimport {
|
|
1
|
+
{"version":3,"file":"InstallButton.js","names":[],"sources":["../../../src/components/InstallButton/InstallButton.tsx"],"sourcesContent":["import { useId, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { Button, type ButtonProps } from \"@/components/Button\";\nimport { useInstallPrompt } from \"@/hooks/use-install-prompt\";\nimport { cn } from \"@/utils/cn\";\nimport { defaultInstallHint, type RenderInstallHint } from \"./install-hints\";\nimport styles from \"./InstallButton.module.css\";\n\nexport type InstallOutcome = \"accepted\" | \"dismissed\" | \"unsupported\";\n\nexport interface InstallButtonProps extends Omit<ButtonProps, \"onClick\" | \"children\"> {\n /** Button label. Default `\"Instalar app\"`. */\n label?: ReactNode;\n /**\n * Label used when the browser cannot be prompted and the button reveals the\n * manual instruction instead. Default `\"Como instalar\"`.\n */\n hintLabel?: ReactNode;\n /** Called with the user's choice after the install prompt resolves. */\n onResult?: (outcome: InstallOutcome) => void;\n /**\n * Replaces the instruction shown for the `\"ios\"` and `\"manual\"` methods.\n *\n * The default copy is in PT-BR and names the real menu entries. Pass this to\n * translate it, to shorten it, or to point at a screenshot of your own.\n */\n renderHint?: RenderInstallHint;\n /**\n * How long, in ms, to wait for `beforeinstallprompt` before falling back to\n * the manual instruction. Forwarded to {@link useInstallPrompt}; defaults to\n * 3000 there.\n */\n manualFallbackDelayMs?: number;\n /** Class applied to the wrapper that holds the button and the instruction. */\n wrapperClassName?: string;\n}\n\n/**\n * Button wired to PWA installation, through {@link useInstallPrompt}.\n *\n * Where the browser fires `beforeinstallprompt`, the click installs. Where it\n * does not — iOS Safari, and the Android Chromium forks (Mi Browser, UC, Opera\n * Mini, Huawei) that strip the API — the click reveals the instruction for that\n * platform instead of the button disappearing.\n *\n * That disappearance was the defect: the component was built on\n * `useBeforeInstallPrompt`, so it rendered `null` exactly where the user needs\n * the most help, and every app re-implemented the iOS and manual paths locally.\n * Nothing renders only when there is genuinely nothing to offer — already\n * installed, running standalone, or inside the decline cooldown.\n *\n * Inherits every {@link Button} prop (`variant`, `size`, `leftIcon`, …).\n *\n * @example\n * <InstallButton variant=\"primary\" leftIcon={<Download />} />\n *\n * @example\n * <InstallButton renderHint={({ method }) => (method === \"ios\" ? <IOSSteps /> : <MenuSteps />)} />\n */\nexport function InstallButton({\n label = \"Instalar app\",\n hintLabel = \"Como instalar\",\n onResult,\n renderHint = defaultInstallHint,\n manualFallbackDelayMs,\n wrapperClassName,\n ...props\n}: InstallButtonProps) {\n const { method, openInChromeIntent, install } = useInstallPrompt({ manualFallbackDelayMs });\n const [hintOpen, setHintOpen] = useState(false);\n const hintId = useId();\n\n if (method === \"none\") return null;\n\n if (method === \"native\") {\n return (\n <Button\n onClick={async () => {\n const accepted = await install();\n onResult?.(accepted ? \"accepted\" : \"dismissed\");\n }}\n {...props}\n >\n {label}\n </Button>\n );\n }\n\n const hint = renderHint({ method, openInChromeIntent });\n\n return (\n <div className={cn(styles.wrapper, wrapperClassName)}>\n <Button\n aria-expanded={hintOpen}\n aria-controls={hintOpen && hint ? hintId : undefined}\n onClick={() => setHintOpen((open) => !open)}\n {...props}\n >\n {hintLabel}\n </Button>\n {hintOpen && hint ? (\n <p className={styles.hint} id={hintId}>\n {hint}\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AA2DA,SAAgB,EAAc,EAC1B,WAAQ,gBACR,eAAY,iBACZ,aACA,gBAAa,GACb,0BACA,qBACA,GAAG,KACgB;CACnB,IAAM,EAAE,WAAQ,uBAAoB,eAAY,EAAiB,EAAE,yBAAsB,CAAC,GACpF,CAAC,GAAU,KAAe,EAAS,EAAK,GACxC,IAAS,EAAM;CAErB,IAAI,MAAW,QAAQ,OAAO;CAE9B,IAAI,MAAW,UACX,OACI,kBAAC,GAAD;EACI,SAAS,YAAY;GACjB,IAAM,IAAW,MAAM,EAAQ;GAC/B,IAAW,IAAW,aAAa,WAAW;EAClD;EACA,GAAI;EAEH,UAAA;CACG,CAAA;CAIhB,IAAM,IAAO,EAAW;EAAE;EAAQ;CAAmB,CAAC;CAEtD,OACI,kBAAC,OAAD;EAAK,WAAW,EAAG,EAAO,SAAS,CAAgB;EAAnD,UAAA,CACI,kBAAC,GAAD;GACI,iBAAe;GACf,iBAAe,KAAY,IAAO,IAAS,KAAA;GAC3C,eAAe,GAAa,MAAS,CAAC,CAAI;GAC1C,GAAI;GAEH,UAAA;EACG,CAAA,GACP,KAAY,IACT,kBAAC,KAAD;GAAG,WAAW,EAAO;GAAM,IAAI;GAC1B,UAAA;EACF,CAAA,IACH,IACH;;AAEb"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InstallButton.module.cjs","names":[],"sources":["../../../src/components/InstallButton/InstallButton.module.css"],"sourcesContent":[".wrapper {\n display: inline-flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--tempest-space-2);\n font-family: var(--tempest-font-sans);\n}\n\n.hint {\n margin: 0;\n max-width: 36ch;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-snug);\n}\n\n.hint a {\n color: var(--tempest-primary);\n}\n"],"mappings":";"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "../../styles/component/InstallButton.css";
|
|
2
|
+
//#region src/components/InstallButton/InstallButton.module.css
|
|
3
|
+
var e = "tempest_wrapper_M6K7s", t = "tempest_hint_4lawL", n = {
|
|
4
|
+
wrapper: e,
|
|
5
|
+
hint: t
|
|
6
|
+
};
|
|
7
|
+
//#endregion
|
|
8
|
+
export { n as default, t as hint, e as wrapper };
|
|
9
|
+
|
|
10
|
+
//# sourceMappingURL=InstallButton.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InstallButton.module.js","names":[],"sources":["../../../src/components/InstallButton/InstallButton.module.css"],"sourcesContent":[".wrapper {\n display: inline-flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--tempest-space-2);\n font-family: var(--tempest-font-sans);\n}\n\n.hint {\n margin: 0;\n max-width: 36ch;\n color: var(--tempest-text-muted);\n font-size: var(--tempest-text-sm);\n line-height: var(--tempest-leading-snug);\n}\n\n.hint a {\n color: var(--tempest-primary);\n}\n"],"mappings":";"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("react/jsx-runtime");function t({method:t,openInChromeIntent:n}){return t===`ios`?(0,e.jsxs)(e.Fragment,{children:[`Toque em `,(0,e.jsx)(`strong`,{children:`Compartilhar`}),` e escolha`,` `,(0,e.jsx)(`strong`,{children:`Adicionar à Tela de Início`}),`.`]}):t===`manual`?(0,e.jsxs)(e.Fragment,{children:[`Abra o menu do navegador (`,(0,e.jsx)(`strong`,{children:`⋮`}),`) e escolha`,` `,(0,e.jsx)(`strong`,{children:`Instalar app`}),`.`,n?(0,e.jsxs)(e.Fragment,{children:[` `,`Se não houver essa opção, `,(0,e.jsx)(`a`,{href:n,children:`abra no Chrome`}),`.`]}):null]}):null}exports.defaultInstallHint=t;
|
|
2
|
+
//# sourceMappingURL=install-hints.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-hints.cjs","names":[],"sources":["../../../src/components/InstallButton/install-hints.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport type { InstallMethod } from \"@/hooks/use-install-prompt\";\n\n/** What an install affordance needs to render the instruction for a method. */\nexport interface InstallHintInput {\n /** The strategy `useInstallPrompt` resolved. */\n method: InstallMethod;\n /** An `intent://` URL that re-opens the page in Chrome on Android, if any. */\n openInChromeIntent: string | null;\n}\n\n/** Renders the instruction shown when the browser has no install prompt. */\nexport type RenderInstallHint = (input: InstallHintInput) => ReactNode;\n\n/**\n * The default instruction for a browser that cannot be prompted.\n *\n * Written out rather than left to the app because every app wrote the same two\n * sentences, and getting them wrong is invisible: an iOS user who is told to\n * look for an \"Install\" menu never finds one, because Safari puts it behind the\n * Share sheet.\n *\n * On an Android fork the copy also offers the `intent://` link the SDK already\n * builds — reopening the page in Chrome is the only path that reaches a real\n * install on a browser whose menu has no install entry at all.\n *\n * @param input - The resolved method and the optional Chrome intent.\n * @returns The instruction, or `null` when the method needs none.\n */\nexport function defaultInstallHint({ method, openInChromeIntent }: InstallHintInput): ReactNode {\n if (method === \"ios\") {\n return (\n <>\n Toque em <strong>Compartilhar</strong> e escolha{\" \"}\n <strong>Adicionar à Tela de Início</strong>.\n </>\n );\n }\n if (method === \"manual\") {\n return (\n <>\n Abra o menu do navegador (<strong>⋮</strong>) e escolha{\" \"}\n <strong>Instalar app</strong>.\n {openInChromeIntent ? (\n <>\n {\" \"}\n Se não houver essa opção, <a href={openInChromeIntent}>abra no Chrome</a>.\n </>\n ) : null}\n </>\n );\n }\n return null;\n}\n"],"mappings":"mCA8BA,SAAgB,EAAmB,CAAE,SAAQ,sBAAmD,CAuB5F,OAtBI,IAAW,OAEP,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CAAE,aACW,EAAA,EAAA,IAAA,CAAC,SAAD,CAAA,SAAQ,cAAoB,CAAA,EAAC,aAAW,KACjD,EAAA,EAAA,IAAA,CAAC,SAAD,CAAA,SAAQ,4BAAkC,CAAA,EAAC,GAC7C,CAAA,CAAA,EAGN,IAAW,UAEP,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CAAE,8BAC4B,EAAA,EAAA,IAAA,CAAC,SAAD,CAAA,SAAQ,GAAS,CAAA,EAAC,cAAY,KACxD,EAAA,EAAA,IAAA,CAAC,SAAD,CAAA,SAAQ,cAAoB,CAAA,EAAC,IAC5B,GACG,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,CACK,IAAI,8BACqB,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,KAAM,EAAoB,SAAA,gBAAiB,CAAA,EAAC,GAC3E,CAAA,CAAA,EACF,IACN,CAAA,CAAA,EAGH,IACX"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Fragment as e, jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
2
|
+
//#region src/components/InstallButton/install-hints.tsx
|
|
3
|
+
function r({ method: r, openInChromeIntent: i }) {
|
|
4
|
+
return r === "ios" ? /* @__PURE__ */ n(e, { children: [
|
|
5
|
+
"Toque em ",
|
|
6
|
+
/* @__PURE__ */ t("strong", { children: "Compartilhar" }),
|
|
7
|
+
" e escolha",
|
|
8
|
+
" ",
|
|
9
|
+
/* @__PURE__ */ t("strong", { children: "Adicionar à Tela de Início" }),
|
|
10
|
+
"."
|
|
11
|
+
] }) : r === "manual" ? /* @__PURE__ */ n(e, { children: [
|
|
12
|
+
"Abra o menu do navegador (",
|
|
13
|
+
/* @__PURE__ */ t("strong", { children: "⋮" }),
|
|
14
|
+
") e escolha",
|
|
15
|
+
" ",
|
|
16
|
+
/* @__PURE__ */ t("strong", { children: "Instalar app" }),
|
|
17
|
+
".",
|
|
18
|
+
i ? /* @__PURE__ */ n(e, { children: [
|
|
19
|
+
" ",
|
|
20
|
+
"Se não houver essa opção, ",
|
|
21
|
+
/* @__PURE__ */ t("a", {
|
|
22
|
+
href: i,
|
|
23
|
+
children: "abra no Chrome"
|
|
24
|
+
}),
|
|
25
|
+
"."
|
|
26
|
+
] }) : null
|
|
27
|
+
] }) : null;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
export { r as defaultInstallHint };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=install-hints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-hints.js","names":[],"sources":["../../../src/components/InstallButton/install-hints.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\n\nimport type { InstallMethod } from \"@/hooks/use-install-prompt\";\n\n/** What an install affordance needs to render the instruction for a method. */\nexport interface InstallHintInput {\n /** The strategy `useInstallPrompt` resolved. */\n method: InstallMethod;\n /** An `intent://` URL that re-opens the page in Chrome on Android, if any. */\n openInChromeIntent: string | null;\n}\n\n/** Renders the instruction shown when the browser has no install prompt. */\nexport type RenderInstallHint = (input: InstallHintInput) => ReactNode;\n\n/**\n * The default instruction for a browser that cannot be prompted.\n *\n * Written out rather than left to the app because every app wrote the same two\n * sentences, and getting them wrong is invisible: an iOS user who is told to\n * look for an \"Install\" menu never finds one, because Safari puts it behind the\n * Share sheet.\n *\n * On an Android fork the copy also offers the `intent://` link the SDK already\n * builds — reopening the page in Chrome is the only path that reaches a real\n * install on a browser whose menu has no install entry at all.\n *\n * @param input - The resolved method and the optional Chrome intent.\n * @returns The instruction, or `null` when the method needs none.\n */\nexport function defaultInstallHint({ method, openInChromeIntent }: InstallHintInput): ReactNode {\n if (method === \"ios\") {\n return (\n <>\n Toque em <strong>Compartilhar</strong> e escolha{\" \"}\n <strong>Adicionar à Tela de Início</strong>.\n </>\n );\n }\n if (method === \"manual\") {\n return (\n <>\n Abra o menu do navegador (<strong>⋮</strong>) e escolha{\" \"}\n <strong>Instalar app</strong>.\n {openInChromeIntent ? (\n <>\n {\" \"}\n Se não houver essa opção, <a href={openInChromeIntent}>abra no Chrome</a>.\n </>\n ) : null}\n </>\n );\n }\n return null;\n}\n"],"mappings":";;AA8BA,SAAgB,EAAmB,EAAE,WAAQ,yBAAmD;CAuB5F,OAtBI,MAAW,QAEP,kBAAA,GAAA,EAAA,UAAA;EAAE;EACW,kBAAC,UAAD,EAAA,UAAQ,eAAoB,CAAA;EAAC;EAAW;EACjD,kBAAC,UAAD,EAAA,UAAQ,6BAAkC,CAAA;EAAC;CAC7C,EAAA,CAAA,IAGN,MAAW,WAEP,kBAAA,GAAA,EAAA,UAAA;EAAE;EAC4B,kBAAC,UAAD,EAAA,UAAQ,IAAS,CAAA;EAAC;EAAY;EACxD,kBAAC,UAAD,EAAA,UAAQ,eAAoB,CAAA;EAAC;EAC5B,IACG,kBAAA,GAAA,EAAA,UAAA;GACK;GAAI;GACqB,kBAAC,KAAD;IAAG,MAAM;IAAoB,UAAA;GAAiB,CAAA;GAAC;EAC3E,EAAA,CAAA,IACF;CACN,EAAA,CAAA,IAGH;AACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControl.module.cjs","names":[],"sources":["../../../src/components/SegmentedControl/SegmentedControl.module.css"],"sourcesContent":[".group {\n display: inline-flex;\n background: var(--tempest-surface-2);\n border-radius: var(--tempest-radius-md);\n padding: 2px;\n font-family: var(--tempest-font-sans);\n gap: 0;\n}\n\n.group.fullWidth {\n display: flex;\n width: 100%;\n}\n\n.segment {\n flex: 1 1 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-1);\n padding: 6px 12px;\n border: 0;\n background: transparent;\n color: var(--tempest-text-muted);\n cursor: pointer;\n border-radius: calc(var(--tempest-radius-md) - 2px);\n font-weight: var(--tempest-weight-medium);\n transition:\n background var(--tempest-duration-fast, 120ms),\n color var(--tempest-duration-fast, 120ms);\n white-space: nowrap;\n}\n\n.group.sm .segment {\n padding: 4px 10px;\n font-size: var(--tempest-text-xs);\n min-height: 24px;\n}\n\n.group.md .segment {\n padding: 6px 12px;\n font-size: var(--tempest-text-sm);\n min-height: 32px;\n}\n\n.group.lg .segment {\n padding: 8px 14px;\n font-size: var(--tempest-text-md);\n min-height: 40px;\n}\n\n.segment:hover {\n color: var(--tempest-text);\n}\n\n.segment.active {\n background: var(--tempest-
|
|
1
|
+
{"version":3,"file":"SegmentedControl.module.cjs","names":[],"sources":["../../../src/components/SegmentedControl/SegmentedControl.module.css"],"sourcesContent":[".group {\n display: inline-flex;\n background: var(--tempest-surface-2);\n border-radius: var(--tempest-radius-md);\n padding: 2px;\n font-family: var(--tempest-font-sans);\n gap: 0;\n}\n\n.group.fullWidth {\n display: flex;\n width: 100%;\n}\n\n.segment {\n flex: 1 1 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-1);\n padding: 6px 12px;\n border: 0;\n background: transparent;\n color: var(--tempest-text-muted);\n cursor: pointer;\n border-radius: calc(var(--tempest-radius-md) - 2px);\n font-weight: var(--tempest-weight-medium);\n transition:\n background var(--tempest-duration-fast, 120ms),\n color var(--tempest-duration-fast, 120ms);\n white-space: nowrap;\n}\n\n.group.sm .segment {\n padding: 4px 10px;\n font-size: var(--tempest-text-xs);\n min-height: 24px;\n}\n\n.group.md .segment {\n padding: 6px 12px;\n font-size: var(--tempest-text-sm);\n min-height: 32px;\n}\n\n.group.lg .segment {\n padding: 8px 14px;\n font-size: var(--tempest-text-md);\n min-height: 40px;\n}\n\n.segment:hover {\n color: var(--tempest-text);\n}\n\n.segment.active {\n background: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n box-shadow:\n inset 0 0 0 2px var(--tempest-selected-indicator),\n var(--tempest-shadow-xs, 0 1px 2px rgb(0 0 0 / 0.08));\n}\n\n.segment:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n}\n\n.label {\n line-height: 1;\n}\n"],"mappings":";"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SegmentedControl.module.js","names":[],"sources":["../../../src/components/SegmentedControl/SegmentedControl.module.css"],"sourcesContent":[".group {\n display: inline-flex;\n background: var(--tempest-surface-2);\n border-radius: var(--tempest-radius-md);\n padding: 2px;\n font-family: var(--tempest-font-sans);\n gap: 0;\n}\n\n.group.fullWidth {\n display: flex;\n width: 100%;\n}\n\n.segment {\n flex: 1 1 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-1);\n padding: 6px 12px;\n border: 0;\n background: transparent;\n color: var(--tempest-text-muted);\n cursor: pointer;\n border-radius: calc(var(--tempest-radius-md) - 2px);\n font-weight: var(--tempest-weight-medium);\n transition:\n background var(--tempest-duration-fast, 120ms),\n color var(--tempest-duration-fast, 120ms);\n white-space: nowrap;\n}\n\n.group.sm .segment {\n padding: 4px 10px;\n font-size: var(--tempest-text-xs);\n min-height: 24px;\n}\n\n.group.md .segment {\n padding: 6px 12px;\n font-size: var(--tempest-text-sm);\n min-height: 32px;\n}\n\n.group.lg .segment {\n padding: 8px 14px;\n font-size: var(--tempest-text-md);\n min-height: 40px;\n}\n\n.segment:hover {\n color: var(--tempest-text);\n}\n\n.segment.active {\n background: var(--tempest-
|
|
1
|
+
{"version":3,"file":"SegmentedControl.module.js","names":[],"sources":["../../../src/components/SegmentedControl/SegmentedControl.module.css"],"sourcesContent":[".group {\n display: inline-flex;\n background: var(--tempest-surface-2);\n border-radius: var(--tempest-radius-md);\n padding: 2px;\n font-family: var(--tempest-font-sans);\n gap: 0;\n}\n\n.group.fullWidth {\n display: flex;\n width: 100%;\n}\n\n.segment {\n flex: 1 1 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--tempest-space-1);\n padding: 6px 12px;\n border: 0;\n background: transparent;\n color: var(--tempest-text-muted);\n cursor: pointer;\n border-radius: calc(var(--tempest-radius-md) - 2px);\n font-weight: var(--tempest-weight-medium);\n transition:\n background var(--tempest-duration-fast, 120ms),\n color var(--tempest-duration-fast, 120ms);\n white-space: nowrap;\n}\n\n.group.sm .segment {\n padding: 4px 10px;\n font-size: var(--tempest-text-xs);\n min-height: 24px;\n}\n\n.group.md .segment {\n padding: 6px 12px;\n font-size: var(--tempest-text-sm);\n min-height: 32px;\n}\n\n.group.lg .segment {\n padding: 8px 14px;\n font-size: var(--tempest-text-md);\n min-height: 40px;\n}\n\n.segment:hover {\n color: var(--tempest-text);\n}\n\n.segment.active {\n background: var(--tempest-primary-soft);\n color: var(--tempest-primary-on-soft);\n box-shadow:\n inset 0 0 0 2px var(--tempest-selected-indicator),\n var(--tempest-shadow-xs, 0 1px 2px rgb(0 0 0 / 0.08));\n}\n\n.segment:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.icon {\n display: inline-flex;\n align-items: center;\n}\n\n.label {\n line-height: 1;\n}\n"],"mappings":";"}
|