@bitrise/bitkit-v2 0.3.502-beta.3242 → 0.3.502-beta.3245

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.
Files changed (26) hide show
  1. package/AGENTS.md +2 -2
  2. package/dist/components/BitkitContentWrapper/BitkitContentWrapper.d.ts +1 -1
  3. package/dist/components/BitkitContentWrapper/BitkitContentWrapper.js +2 -1
  4. package/dist/components/BitkitContentWrapper/BitkitContentWrapper.js.map +1 -1
  5. package/dist/components/BitkitFilter/BitkitFilter.js +2 -3
  6. package/dist/components/BitkitFilter/BitkitFilter.js.map +1 -1
  7. package/dist/components/BitkitPageHeader/BitkitPageHeader.d.ts +1 -1
  8. package/dist/components/BitkitPageHeader/BitkitPageHeader.js +2 -4
  9. package/dist/components/BitkitPageHeader/BitkitPageHeader.js.map +1 -1
  10. package/dist/components/BitkitPageSubheader/BitkitPageSubheader.js +3 -2
  11. package/dist/components/BitkitPageSubheader/BitkitPageSubheader.js.map +1 -1
  12. package/dist/theme/common/ContentWidth.common.d.ts +3 -20
  13. package/dist/theme/recipes/ContentWrapper.recipe.d.ts +1 -17
  14. package/dist/theme/recipes/ContentWrapper.recipe.js +1 -3
  15. package/dist/theme/recipes/ContentWrapper.recipe.js.map +1 -1
  16. package/dist/theme/slot-recipes/PageSubheader.recipe.d.ts +9 -8
  17. package/dist/theme/slot-recipes/PageSubheader.recipe.js +7 -8
  18. package/dist/theme/slot-recipes/PageSubheader.recipe.js.map +1 -1
  19. package/dist/theme/slot-recipes/index.d.ts +9 -21
  20. package/dist/theme/tokens/index.d.ts +3 -0
  21. package/dist/theme/tokens/sizes.d.ts +3 -0
  22. package/dist/theme/tokens/sizes.js +2 -1
  23. package/dist/theme/tokens/sizes.js.map +1 -1
  24. package/package.json +1 -1
  25. package/dist/theme/common/ContentWidth.common.js +0 -15
  26. package/dist/theme/common/ContentWidth.common.js.map +0 -1
package/AGENTS.md CHANGED
@@ -13,7 +13,7 @@ The mistakes that break things silently or come up in almost every review. Each
13
13
  3. **Don't override a Bitkit component's styling**, and don't pass an empty or placeholder value to satisfy a required prop. Layout props (`width`, `flex`, `margin*`, …) are fine. → _Building UI_
14
14
  4. **Never wrap a Bitkit component in `<Box>` for styling.** Put the style props on the component. → _Coding rules_
15
15
  5. **Only semantic colors.** No palette colors, `sys/*` colors, hex codes or color names. → _Coding rules_
16
- 6. **Sizes are tokens.** A page's content width is the `contentWidth` of `BitkitContentWrapper` and the components above it; anything else above 128 uses `rem()`. A bare-number string like `"420"` is dropped. → _Coding rules_
16
+ 6. **Sizes are tokens.** A page's content width is a `layout/maxWidth` token (`contentWidth` on `BitkitContentWrapper` and the components above it); anything else above 128 uses `rem()`. A bare-number string like `"420"` is dropped. → _Coding rules_
17
17
  7. **Omit default prop values.** → _Coding rules_
18
18
 
19
19
  ## Before you start (instructions for the AI assistant)
@@ -234,7 +234,7 @@ Bitkit components use consistent prop names across the library:
234
234
  - **A surface that stays dark in both color modes** (logs, terminal output) — `background/fixed-contrast` with `text/on-color` (and `color/white/*` for dimmer text). `background/contrast` turns light in dark mode, so the white text on it disappears. Don't reach for a palette color instead.
235
235
  - **Always use `textStyle`** — never set `fontFamily` or `fontSize` on `<Text>`. Use `textStyle="body/md/regular"`, `textStyle="code/md"`, etc.
236
236
  - **Sizes as strings** — `width="32"`, not `width={32}`. Tokens are strings.
237
- - **Use existing size tokens** — closest existing token (1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128) representing pixel values (1=1px, 32=32px). Avoid arbitrary px (`width="100px"`). Above 128: a page's content width is `contentWidth` (`640`, `960`, `1280`, `1616`, `full`) on `BitkitContentWrapper` and on the page header, subheader and filter bar above it — see `Docs/Page Layout`; for anything else use Bitkit's `rem()` helper (`import { rem } from '@bitrise/bitkit-v2'`, `maxWidth={rem(800)}`), never a bare-number string — `maxWidth="800"` is not a token, reaches the CSS without a unit, and the browser silently drops it.
237
+ - **Use existing size tokens** — closest existing token (1, 2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 64, 96, 128) representing pixel values (1=1px, 32=32px). Avoid arbitrary px (`width="100px"`). Above 128: a page's content width is `contentWidth` (`layout/maxWidth/640`, `…/960`, `…/1280`, `…/1616`, `…/full`) on `BitkitContentWrapper` and on the page header, subheader and filter bar above it — see `Docs/Page Layout`; for anything else use Bitkit's `rem()` helper (`import { rem } from '@bitrise/bitkit-v2'`, `maxWidth={rem(800)}`), never a bare-number string — `maxWidth="800"` is not a token, reaches the CSS without a unit, and the browser silently drops it.
238
238
  - **Omit default prop values** — `<IconCross />`, not `<IconCross size="24" />` when 24 is the default.
239
239
  - **Never pass `color` to a colored icon** — multi/fixed-color icons (`Icon*Color`, `IconWarningYellow`, `Icon*White`, etc.) have their colors baked in. They omit `color` from their props, so passing it is a type error. Only monochrome icons follow `color` (via `currentColor`).
240
240
  - **No shorthand props** — use full names: `marginInline`, `paddingBlockEnd`, `width`, `background`, `padding`. Never `mx`, `pb`, `w`, `bg`, `p`, `mt`.
@@ -1,6 +1,6 @@
1
1
  import { BoxProps } from '@chakra-ui/react/box';
2
2
  import { ContentWidth } from '../../theme/common/ContentWidth.common';
3
- /** A page content width: one of the `layout/maxWidth` tokens, or `full` for no cap. */
3
+ /** A page content width: one of the `layout/maxWidth` tokens, `layout/maxWidth/full` for no cap. */
4
4
  export type BitkitContentWidth = ContentWidth;
5
5
  export interface BitkitContentWrapperProps extends BoxProps {
6
6
  /**
@@ -8,8 +8,9 @@ var BitkitContentWrapper = forwardRef((props, ref) => {
8
8
  const { columnTemplate, contentWidth, css, ...rest } = props;
9
9
  const recipe = useRecipe({ key: "contentWrapper" });
10
10
  return /* @__PURE__ */ jsx(Box, {
11
- css: [recipe({ contentWidth }), css],
11
+ css: [recipe(), css],
12
12
  gridTemplateColumns: columnTemplate ? { tablet: columnTemplate } : void 0,
13
+ maxWidth: contentWidth,
13
14
  ref,
14
15
  ...rest
15
16
  });
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitContentWrapper.js","names":[],"sources":["../../../lib/components/BitkitContentWrapper/BitkitContentWrapper.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport { type ContentWidth } from '../../theme/common/ContentWidth.common';\n\n/** A page content width: one of the `layout/maxWidth` tokens, or `full` for no cap. */\nexport type BitkitContentWidth = ContentWidth;\n\nexport interface BitkitContentWrapperProps extends BoxProps {\n /**\n * Side-by-side columns from `tablet` up, as a CSS grid template — `'1fr 1fr'` for two equal columns,\n * `'1fr 25rem'` for one that fills next to a fixed one. Below `tablet` the columns stack.\n */\n columnTemplate?: string;\n /** The cap on the page's content column, padding included. Pick the same value for the page header above it. */\n contentWidth: BitkitContentWidth;\n}\n\n/** The page's content grid: capped at a `layout/maxWidth` token, centred, one column of sections 32 apart unless `columnTemplate` says otherwise. */\nconst BitkitContentWrapper = forwardRef<HTMLDivElement, BitkitContentWrapperProps>((props, ref) => {\n const { columnTemplate, contentWidth, css, ...rest } = props;\n\n const recipe = useRecipe({ key: 'contentWrapper' });\n\n return (\n <Box\n css={[recipe({ contentWidth }), css]}\n gridTemplateColumns={columnTemplate ? { tablet: columnTemplate } : undefined}\n ref={ref}\n {...rest}\n />\n );\n});\n\nBitkitContentWrapper.displayName = 'BitkitContentWrapper';\n\nexport default BitkitContentWrapper;\n"],"mappings":";;;;;;AAoBA,IAAM,uBAAuB,YAAuD,OAAO,QAAQ;CACjG,MAAM,EAAE,gBAAgB,cAAc,KAAK,GAAG,SAAS;CAEvD,MAAM,SAAS,UAAU,EAAE,KAAK,iBAAiB,CAAC;CAElD,OACE,oBAAC,KAAD;EACE,KAAK,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,GAAG;EACnC,qBAAqB,iBAAiB,EAAE,QAAQ,eAAe,IAAI,KAAA;EAC9D;EACL,GAAI;CACL,CAAA;AAEL,CAAC;AAED,qBAAqB,cAAc"}
1
+ {"version":3,"file":"BitkitContentWrapper.js","names":[],"sources":["../../../lib/components/BitkitContentWrapper/BitkitContentWrapper.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef } from 'react';\n\nimport { type ContentWidth } from '../../theme/common/ContentWidth.common';\n\n/** A page content width: one of the `layout/maxWidth` tokens, `layout/maxWidth/full` for no cap. */\nexport type BitkitContentWidth = ContentWidth;\n\nexport interface BitkitContentWrapperProps extends BoxProps {\n /**\n * Side-by-side columns from `tablet` up, as a CSS grid template — `'1fr 1fr'` for two equal columns,\n * `'1fr 25rem'` for one that fills next to a fixed one. Below `tablet` the columns stack.\n */\n columnTemplate?: string;\n /** The cap on the page's content column, padding included. Pick the same value for the page header above it. */\n contentWidth: BitkitContentWidth;\n}\n\n/** The page's content grid: capped at a `layout/maxWidth` token, centred, one column of sections 32 apart unless `columnTemplate` says otherwise. */\nconst BitkitContentWrapper = forwardRef<HTMLDivElement, BitkitContentWrapperProps>((props, ref) => {\n const { columnTemplate, contentWidth, css, ...rest } = props;\n\n const recipe = useRecipe({ key: 'contentWrapper' });\n\n return (\n <Box\n css={[recipe(), css]}\n gridTemplateColumns={columnTemplate ? { tablet: columnTemplate } : undefined}\n maxWidth={contentWidth}\n ref={ref}\n {...rest}\n />\n );\n});\n\nBitkitContentWrapper.displayName = 'BitkitContentWrapper';\n\nexport default BitkitContentWrapper;\n"],"mappings":";;;;;;AAoBA,IAAM,uBAAuB,YAAuD,OAAO,QAAQ;CACjG,MAAM,EAAE,gBAAgB,cAAc,KAAK,GAAG,SAAS;CAEvD,MAAM,SAAS,UAAU,EAAE,KAAK,iBAAiB,CAAC;CAElD,OACE,oBAAC,KAAD;EACE,KAAK,CAAC,OAAO,GAAG,GAAG;EACnB,qBAAqB,iBAAiB,EAAE,QAAQ,eAAe,IAAI,KAAA;EACnE,UAAU;EACL;EACL,GAAI;CACL,CAAA;AAEL,CAAC;AAED,qBAAqB,cAAc"}
@@ -7,7 +7,6 @@ import BitkitBadge from "../BitkitBadge/BitkitBadge.js";
7
7
  import { flattenChildren } from "../../utilities/flattenChildren.js";
8
8
  import BitkitButton from "../BitkitButton/BitkitButton.js";
9
9
  import useResponsive from "../../hooks/useResponsive.js";
10
- import { contentMaxWidth } from "../../theme/common/ContentWidth.common.js";
11
10
  import { FilterContextProvider } from "./BitkitFilter.context.js";
12
11
  import BitkitFilterAdd from "./BitkitFilterAdd.js";
13
12
  import BitkitFilterDateRange from "./BitkitFilterDateRange.js";
@@ -44,7 +43,7 @@ var BitkitFilter = forwardRef((props, ref) => {
44
43
  const [isDialogOpen, setIsDialogOpen] = useState(false);
45
44
  const recipe = useSlotRecipe({ key: "filter" });
46
45
  let pageLayout;
47
- if (contentWidth) pageLayout = contentWidth === "full" ? "full" : "capped";
46
+ if (contentWidth) pageLayout = contentWidth === "layout/maxWidth/full" ? "full" : "capped";
48
47
  const styles = recipe({ pageLayout });
49
48
  const resets = useRef(/* @__PURE__ */ new Map());
50
49
  const [dirtyIds, setDirtyIds] = useState(/* @__PURE__ */ new Set());
@@ -201,7 +200,7 @@ var BitkitFilter = forwardRef((props, ref) => {
201
200
  ...rest,
202
201
  children: /* @__PURE__ */ jsxs(Box, {
203
202
  css: styles.content,
204
- maxWidth: contentMaxWidth(contentWidth),
203
+ maxWidth: contentWidth,
205
204
  ref: focusTarget,
206
205
  children: [/* @__PURE__ */ jsxs(Box, {
207
206
  css: styles.filters,
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitFilter.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilter.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Drawer } from '@chakra-ui/react/drawer';\nimport { Portal } from '@chakra-ui/react/portal';\n// `chakra.button`: the mobile \"Filters\" button carries a count badge, which BitkitButton's string-only label can't.\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport useResponsive from '../../hooks/useResponsive';\nimport { IconCross, IconFilter } from '../../icons';\nimport { contentMaxWidth } from '../../theme/common/ContentWidth.common';\nimport { usePortalContext } from '../../utilities/BitkitPortalContext';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitBadge from '../BitkitBadge/BitkitBadge';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport { FilterContextProvider, type FilterStatus } from './BitkitFilter.context';\nimport BitkitFilterAdd from './BitkitFilterAdd';\nimport BitkitFilterDateRange from './BitkitFilterDateRange';\nimport BitkitFilterSearch from './BitkitFilterSearch';\nimport BitkitFilterSelect from './BitkitFilterSelect';\nimport BitkitFilterSwitch from './BitkitFilterSwitch';\n\nexport interface BitkitFilterProps extends Omit<BoxProps, 'children'> {\n /**\n * The filters: `BitkitFilter.Switch`, `.Select`, `.DateRange`, `.Add` and `.Search`, in the\n * order they appear. `.Search` always sits at the end of the bar.\n */\n children: ReactNode;\n /**\n * Lines the bar up with the page content below it, for a bar under a page header: it gains a\n * bottom border and the page's inline padding (32, 16 below tablet). Pass the `BitkitContentWrapper`'s value,\n * as for `BitkitPageHeader`; the bar is capped at that width and centred.\n *\n * Without it the bar has no padding of its own: place it with style props, like any block. Desktop only.\n */\n contentWidth?: BitkitContentWidth;\n /**\n * Forces the desktop bar or the mobile \"Filters\" dialog. Defaults to the viewport: mobile below the\n * tablet breakpoint. The mobile dialog is made for narrow screens; from tablet up it is a 320px drawer.\n */\n device?: 'desktop' | 'mobile';\n /**\n * Called once on \"Clear filters\" in place of handing each changed filter its `resetValue` — for a\n * page that resets them all in one write, e.g. one URL update. The `resetValue`s still decide when\n * the button shows.\n */\n onClear?: () => void;\n /**\n * Shows \"Clear filters\" while any filter differs from its `resetValue`. It hands each of those\n * filters its `resetValue` back through its own `onValueChange`, or calls `onClear`.\n */\n showClear?: boolean;\n /** Shows the filter icon at the start of the bar. Desktop only. */\n showIcon?: boolean;\n /** `loading`: while the page's data loads, the filters can't be opened, removed or cleared. The search stays usable. */\n state?: 'loading';\n}\n\nconst toggle = (set: ReadonlySet<string>, id: string, isIn: boolean) => {\n if (set.has(id) === isIn) return set;\n const next = new Set(set);\n if (isIn) next.add(id);\n else next.delete(id);\n return next;\n};\n\n/**\n * A bar of filters above a list. Each filter is a controlled sub-component with its own `value` and\n * `onValueChange`; the bar lays them out and offers \"Clear filters\" across all of them.\n *\n * Below the tablet breakpoint the bar collapses into a \"Filters\" button opening a full-screen dialog,\n * where every filter is a field opening its own view. A filter still applies on its view's \"Apply\".\n */\nconst BitkitFilter = forwardRef<HTMLDivElement, BitkitFilterProps>((props, ref) => {\n const { children, contentWidth, css, device, onClear, showClear = true, showIcon = true, state, ...rest } = props;\n const { isMobile: isMobileViewport } = useResponsive();\n const isMobile = device ? device === 'mobile' : isMobileViewport;\n const isDisabled = state === 'loading';\n const { disablePortal } = usePortalContext();\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n\n const recipe = useSlotRecipe({ key: 'filter' });\n let pageLayout: 'capped' | 'full' | undefined;\n if (contentWidth) pageLayout = contentWidth === 'full' ? 'full' : 'capped';\n const styles = recipe({ pageLayout });\n\n const resets = useRef(new Map<string, () => void>());\n const [dirtyIds, setDirtyIds] = useState<ReadonlySet<string>>(new Set());\n const [activeIds, setActiveIds] = useState<ReadonlySet<string>>(new Set());\n\n const register = useCallback((id: string, reset: () => void) => {\n resets.current.set(id, reset);\n }, []);\n\n const setStatus = useCallback((id: string, { isActive, isDirty }: FilterStatus) => {\n setDirtyIds((prev) => toggle(prev, id, isDirty));\n setActiveIds((prev) => toggle(prev, id, isActive));\n }, []);\n\n const unregister = useCallback(\n (id: string) => {\n resets.current.delete(id);\n setStatus(id, { isActive: false, isDirty: false });\n },\n [setStatus],\n );\n\n const context = useMemo(\n () => ({ isDisabled, isMobile, register, setStatus, styles, unregister }),\n [isDisabled, isMobile, register, setStatus, styles, unregister],\n );\n\n const filters: ReactNode[] = [];\n const searches: ReactNode[] = [];\n // Flattened, so a filter grouped in a fragment is still sorted to the right place.\n flattenChildren(children).forEach((child) => {\n if (child.type === BitkitFilterSearch) searches.push(child);\n else filters.push(child);\n });\n\n const isDirty = dirtyIds.size > 0;\n const activeCount = activeIds.size;\n // The \"Clear filters\" button goes away (or is disabled) once nothing differs, so focus then moves\n // to the first control of the cleared filters rather than dropping to the page. Only if it hasn't\n // gone anywhere else meanwhile, e.g. while a slow reset was under way.\n const focusTarget = useRef<HTMLDivElement>(null);\n const isFocusPending = useRef(false);\n const clearAll = () => {\n if (isDisabled) return;\n isFocusPending.current = true;\n // Called off once focus lands anywhere, so a clear that never fully lands can't pull it in later.\n document.addEventListener(\n 'focusin',\n () => {\n isFocusPending.current = false;\n },\n { once: true },\n );\n if (onClear) onClear();\n else dirtyIds.forEach((id) => resets.current.get(id)?.());\n };\n useEffect(() => {\n if (!isFocusPending.current || isDirty) return;\n isFocusPending.current = false;\n const active = document.activeElement;\n if (active && active !== document.body && !active.matches('[data-filter-clear]')) return;\n focusTarget.current?.querySelector<HTMLElement>('button:not(:disabled), input:not(:disabled)')?.focus();\n }, [isDirty]);\n\n // While the page loads, the controls are only `aria-disabled`, so they keep a focus handed to them.\n // `data-disabled` takes them out of the hover and active styles too.\n const disabledProps = { 'aria-disabled': isDisabled || undefined, 'data-disabled': isDisabled ? '' : undefined };\n const clearProps = { ...disabledProps, 'data-filter-clear': '', onClick: clearAll };\n\n if (isMobile) {\n return (\n <FilterContextProvider value={context}>\n <Box css={css} ref={ref} {...rest}>\n <Box css={styles.mobileBar} ref={isDialogOpen ? undefined : focusTarget}>\n <chakra.button\n aria-expanded={isDialogOpen}\n aria-haspopup=\"dialog\"\n aria-label={`Filters, ${activeCount} applied`}\n {...disabledProps}\n css={styles.mobileTrigger}\n onClick={() => !isDisabled && setIsDialogOpen(true)}\n type=\"button\"\n >\n <IconFilter size=\"16\" />\n Filters\n <BitkitBadge colorVariant={activeCount ? 'purple' : 'neutral'}>{activeCount}</BitkitBadge>\n </chakra.button>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches}\n {/* Kept mounted while closed: the filters inside register their values with the bar. */}\n <Drawer.Root\n lazyMount={false}\n onOpenChange={({ open }) => setIsDialogOpen(open)}\n open={isDialogOpen}\n unmountOnExit={false}\n >\n <Portal disabled={disablePortal}>\n <Drawer.Positioner>\n <Drawer.Content>\n <Box css={styles.mobileHeader}>\n <Drawer.Title css={styles.mobileTitle} unstyled>\n Filters\n </Drawer.Title>\n <Drawer.CloseTrigger asChild unstyled>\n <BitkitCloseButton size=\"sm\" />\n </Drawer.CloseTrigger>\n </Box>\n <Drawer.Body css={styles.mobileBody} ref={isDialogOpen ? focusTarget : undefined}>\n {filters}\n </Drawer.Body>\n <Drawer.Footer>\n <Box css={styles.mobileFooter}>\n {showClear && (\n <BitkitButton\n {...clearProps}\n size=\"md\"\n state={isDirty ? undefined : 'disabled'}\n variant=\"secondary\"\n >\n Clear filters\n </BitkitButton>\n )}\n <BitkitButton onClick={() => setIsDialogOpen(false)} size=\"md\">\n Show results\n </BitkitButton>\n </Box>\n </Drawer.Footer>\n </Drawer.Content>\n </Drawer.Positioner>\n </Portal>\n </Drawer.Root>\n </Box>\n </FilterContextProvider>\n );\n }\n\n return (\n <FilterContextProvider value={context}>\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.content} maxWidth={contentMaxWidth(contentWidth)} ref={focusTarget}>\n <Box css={styles.filters}>\n {showIcon && (\n <Box css={styles.icon}>\n <IconFilter />\n </Box>\n )}\n <Box css={styles.items}>{filters}</Box>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} flexShrink=\"0\" icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches.length > 0 && <Box css={styles.searchBlock}>{searches}</Box>}\n </Box>\n </Box>\n </FilterContextProvider>\n );\n});\n\nBitkitFilter.displayName = 'BitkitFilter';\n\nexport default withSubComponents(BitkitFilter, {\n Add: BitkitFilterAdd,\n DateRange: BitkitFilterDateRange,\n Search: BitkitFilterSearch,\n Select: BitkitFilterSelect,\n Switch: BitkitFilterSwitch,\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitFilter.js","names":[],"sources":["../../../lib/components/BitkitFilter/BitkitFilter.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { Drawer } from '@chakra-ui/react/drawer';\nimport { Portal } from '@chakra-ui/react/portal';\n// `chakra.button`: the mobile \"Filters\" button carries a count badge, which BitkitButton's string-only label can't.\nimport { chakra, useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { forwardRef, type ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport useResponsive from '../../hooks/useResponsive';\nimport { IconCross, IconFilter } from '../../icons';\nimport { usePortalContext } from '../../utilities/BitkitPortalContext';\nimport { flattenChildren } from '../../utilities/flattenChildren';\nimport { withSubComponents } from '../../utilities/withSubComponents';\nimport BitkitBadge from '../BitkitBadge/BitkitBadge';\nimport BitkitButton from '../BitkitButton/BitkitButton';\nimport BitkitCloseButton from '../BitkitCloseButton/BitkitCloseButton';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport { FilterContextProvider, type FilterStatus } from './BitkitFilter.context';\nimport BitkitFilterAdd from './BitkitFilterAdd';\nimport BitkitFilterDateRange from './BitkitFilterDateRange';\nimport BitkitFilterSearch from './BitkitFilterSearch';\nimport BitkitFilterSelect from './BitkitFilterSelect';\nimport BitkitFilterSwitch from './BitkitFilterSwitch';\n\nexport interface BitkitFilterProps extends Omit<BoxProps, 'children'> {\n /**\n * The filters: `BitkitFilter.Switch`, `.Select`, `.DateRange`, `.Add` and `.Search`, in the\n * order they appear. `.Search` always sits at the end of the bar.\n */\n children: ReactNode;\n /**\n * Lines the bar up with the page content below it, for a bar under a page header: it gains a\n * bottom border and the page's inline padding (32, 16 below tablet). Pass the `BitkitContentWrapper`'s value,\n * as for `BitkitPageHeader`; the bar is capped at that width and centred.\n *\n * Without it the bar has no padding of its own: place it with style props, like any block. Desktop only.\n */\n contentWidth?: BitkitContentWidth;\n /**\n * Forces the desktop bar or the mobile \"Filters\" dialog. Defaults to the viewport: mobile below the\n * tablet breakpoint. The mobile dialog is made for narrow screens; from tablet up it is a 320px drawer.\n */\n device?: 'desktop' | 'mobile';\n /**\n * Called once on \"Clear filters\" in place of handing each changed filter its `resetValue` — for a\n * page that resets them all in one write, e.g. one URL update. The `resetValue`s still decide when\n * the button shows.\n */\n onClear?: () => void;\n /**\n * Shows \"Clear filters\" while any filter differs from its `resetValue`. It hands each of those\n * filters its `resetValue` back through its own `onValueChange`, or calls `onClear`.\n */\n showClear?: boolean;\n /** Shows the filter icon at the start of the bar. Desktop only. */\n showIcon?: boolean;\n /** `loading`: while the page's data loads, the filters can't be opened, removed or cleared. The search stays usable. */\n state?: 'loading';\n}\n\nconst toggle = (set: ReadonlySet<string>, id: string, isIn: boolean) => {\n if (set.has(id) === isIn) return set;\n const next = new Set(set);\n if (isIn) next.add(id);\n else next.delete(id);\n return next;\n};\n\n/**\n * A bar of filters above a list. Each filter is a controlled sub-component with its own `value` and\n * `onValueChange`; the bar lays them out and offers \"Clear filters\" across all of them.\n *\n * Below the tablet breakpoint the bar collapses into a \"Filters\" button opening a full-screen dialog,\n * where every filter is a field opening its own view. A filter still applies on its view's \"Apply\".\n */\nconst BitkitFilter = forwardRef<HTMLDivElement, BitkitFilterProps>((props, ref) => {\n const { children, contentWidth, css, device, onClear, showClear = true, showIcon = true, state, ...rest } = props;\n const { isMobile: isMobileViewport } = useResponsive();\n const isMobile = device ? device === 'mobile' : isMobileViewport;\n const isDisabled = state === 'loading';\n const { disablePortal } = usePortalContext();\n const [isDialogOpen, setIsDialogOpen] = useState(false);\n\n const recipe = useSlotRecipe({ key: 'filter' });\n let pageLayout: 'capped' | 'full' | undefined;\n if (contentWidth) pageLayout = contentWidth === 'layout/maxWidth/full' ? 'full' : 'capped';\n const styles = recipe({ pageLayout });\n\n const resets = useRef(new Map<string, () => void>());\n const [dirtyIds, setDirtyIds] = useState<ReadonlySet<string>>(new Set());\n const [activeIds, setActiveIds] = useState<ReadonlySet<string>>(new Set());\n\n const register = useCallback((id: string, reset: () => void) => {\n resets.current.set(id, reset);\n }, []);\n\n const setStatus = useCallback((id: string, { isActive, isDirty }: FilterStatus) => {\n setDirtyIds((prev) => toggle(prev, id, isDirty));\n setActiveIds((prev) => toggle(prev, id, isActive));\n }, []);\n\n const unregister = useCallback(\n (id: string) => {\n resets.current.delete(id);\n setStatus(id, { isActive: false, isDirty: false });\n },\n [setStatus],\n );\n\n const context = useMemo(\n () => ({ isDisabled, isMobile, register, setStatus, styles, unregister }),\n [isDisabled, isMobile, register, setStatus, styles, unregister],\n );\n\n const filters: ReactNode[] = [];\n const searches: ReactNode[] = [];\n // Flattened, so a filter grouped in a fragment is still sorted to the right place.\n flattenChildren(children).forEach((child) => {\n if (child.type === BitkitFilterSearch) searches.push(child);\n else filters.push(child);\n });\n\n const isDirty = dirtyIds.size > 0;\n const activeCount = activeIds.size;\n // The \"Clear filters\" button goes away (or is disabled) once nothing differs, so focus then moves\n // to the first control of the cleared filters rather than dropping to the page. Only if it hasn't\n // gone anywhere else meanwhile, e.g. while a slow reset was under way.\n const focusTarget = useRef<HTMLDivElement>(null);\n const isFocusPending = useRef(false);\n const clearAll = () => {\n if (isDisabled) return;\n isFocusPending.current = true;\n // Called off once focus lands anywhere, so a clear that never fully lands can't pull it in later.\n document.addEventListener(\n 'focusin',\n () => {\n isFocusPending.current = false;\n },\n { once: true },\n );\n if (onClear) onClear();\n else dirtyIds.forEach((id) => resets.current.get(id)?.());\n };\n useEffect(() => {\n if (!isFocusPending.current || isDirty) return;\n isFocusPending.current = false;\n const active = document.activeElement;\n if (active && active !== document.body && !active.matches('[data-filter-clear]')) return;\n focusTarget.current?.querySelector<HTMLElement>('button:not(:disabled), input:not(:disabled)')?.focus();\n }, [isDirty]);\n\n // While the page loads, the controls are only `aria-disabled`, so they keep a focus handed to them.\n // `data-disabled` takes them out of the hover and active styles too.\n const disabledProps = { 'aria-disabled': isDisabled || undefined, 'data-disabled': isDisabled ? '' : undefined };\n const clearProps = { ...disabledProps, 'data-filter-clear': '', onClick: clearAll };\n\n if (isMobile) {\n return (\n <FilterContextProvider value={context}>\n <Box css={css} ref={ref} {...rest}>\n <Box css={styles.mobileBar} ref={isDialogOpen ? undefined : focusTarget}>\n <chakra.button\n aria-expanded={isDialogOpen}\n aria-haspopup=\"dialog\"\n aria-label={`Filters, ${activeCount} applied`}\n {...disabledProps}\n css={styles.mobileTrigger}\n onClick={() => !isDisabled && setIsDialogOpen(true)}\n type=\"button\"\n >\n <IconFilter size=\"16\" />\n Filters\n <BitkitBadge colorVariant={activeCount ? 'purple' : 'neutral'}>{activeCount}</BitkitBadge>\n </chakra.button>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches}\n {/* Kept mounted while closed: the filters inside register their values with the bar. */}\n <Drawer.Root\n lazyMount={false}\n onOpenChange={({ open }) => setIsDialogOpen(open)}\n open={isDialogOpen}\n unmountOnExit={false}\n >\n <Portal disabled={disablePortal}>\n <Drawer.Positioner>\n <Drawer.Content>\n <Box css={styles.mobileHeader}>\n <Drawer.Title css={styles.mobileTitle} unstyled>\n Filters\n </Drawer.Title>\n <Drawer.CloseTrigger asChild unstyled>\n <BitkitCloseButton size=\"sm\" />\n </Drawer.CloseTrigger>\n </Box>\n <Drawer.Body css={styles.mobileBody} ref={isDialogOpen ? focusTarget : undefined}>\n {filters}\n </Drawer.Body>\n <Drawer.Footer>\n <Box css={styles.mobileFooter}>\n {showClear && (\n <BitkitButton\n {...clearProps}\n size=\"md\"\n state={isDirty ? undefined : 'disabled'}\n variant=\"secondary\"\n >\n Clear filters\n </BitkitButton>\n )}\n <BitkitButton onClick={() => setIsDialogOpen(false)} size=\"md\">\n Show results\n </BitkitButton>\n </Box>\n </Drawer.Footer>\n </Drawer.Content>\n </Drawer.Positioner>\n </Portal>\n </Drawer.Root>\n </Box>\n </FilterContextProvider>\n );\n }\n\n return (\n <FilterContextProvider value={context}>\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.content} maxWidth={contentWidth} ref={focusTarget}>\n <Box css={styles.filters}>\n {showIcon && (\n <Box css={styles.icon}>\n <IconFilter />\n </Box>\n )}\n <Box css={styles.items}>{filters}</Box>\n {showClear && isDirty && (\n <BitkitButton {...clearProps} flexShrink=\"0\" icon={IconCross} size=\"sm\" variant=\"tertiary\">\n Clear filters\n </BitkitButton>\n )}\n </Box>\n {searches.length > 0 && <Box css={styles.searchBlock}>{searches}</Box>}\n </Box>\n </Box>\n </FilterContextProvider>\n );\n});\n\nBitkitFilter.displayName = 'BitkitFilter';\n\nexport default withSubComponents(BitkitFilter, {\n Add: BitkitFilterAdd,\n DateRange: BitkitFilterDateRange,\n Search: BitkitFilterSearch,\n Select: BitkitFilterSelect,\n Switch: BitkitFilterSwitch,\n});\n"],"mappings":"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"}
@@ -49,7 +49,7 @@ export interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {
49
49
  /**
50
50
  * Lines the header's content up with the page content below it: pass the `BitkitContentWrapper`'s value. The title,
51
51
  * the actions and the tabs are capped at that width and centred, while the background and the bottom border still run
52
- * edge to edge. `'full'` keeps the same 32px inset without a cap. Without it the header keeps the default inset, which
52
+ * edge to edge. `layout/maxWidth/full` keeps the same 32px inset without a cap. Without it the header keeps the default inset, which
53
53
  * lines up with a sidebar below it.
54
54
  */
55
55
  contentWidth?: BitkitContentWidth;
@@ -1,6 +1,5 @@
1
1
  import IconMenuHamburger from "../../icons/IconMenuHamburger.js";
2
2
  import { isEmptyNode } from "../../utilities/isEmptyNode.js";
3
- import { contentMaxWidth } from "../../theme/common/ContentWidth.common.js";
4
3
  import BitkitIconButton from "../BitkitIconButton/BitkitIconButton.js";
5
4
  import BitkitOverflowTooltip from "../BitkitOverflowTooltip/BitkitOverflowTooltip.js";
6
5
  import useIsTruncated from "../../utilities/useIsTruncated.js";
@@ -25,7 +24,6 @@ var BitkitPageHeader = forwardRef((props, ref) => {
25
24
  const isCollapsed = variant === "minimal" || Boolean(isSticky);
26
25
  const showControls = !isEmptyNode(controls) && isCollapsed;
27
26
  const canShowTitleTooltip = isTitleTruncated && !(isCollapsed && hasBreadcrumb);
28
- const capWidth = contentMaxWidth(contentWidth);
29
27
  const styles = useSlotRecipe({ key: "pageHeader" })({
30
28
  contentVariant,
31
29
  hasBreadcrumb,
@@ -84,7 +82,7 @@ var BitkitPageHeader = forwardRef((props, ref) => {
84
82
  ...rest,
85
83
  children: [/* @__PURE__ */ jsxs(Box, {
86
84
  css: styles.headerBlock,
87
- maxWidth: capWidth,
85
+ maxWidth: contentWidth,
88
86
  children: [
89
87
  hasBreadcrumb && /* @__PURE__ */ jsx(Box, {
90
88
  css: styles.breadcrumb,
@@ -125,7 +123,7 @@ var BitkitPageHeader = forwardRef((props, ref) => {
125
123
  ]
126
124
  }), hasTabs && /* @__PURE__ */ jsx(Box, {
127
125
  css: styles.tabs,
128
- maxWidth: capWidth,
126
+ maxWidth: contentWidth,
129
127
  children: tabs
130
128
  })]
131
129
  });
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitPageHeader.js","names":[],"sources":["../../../lib/components/BitkitPageHeader/BitkitPageHeader.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport IconMenuHamburger from '../../icons/IconMenuHamburger';\nimport { contentMaxWidth } from '../../theme/common/ContentWidth.common';\nimport { isEmptyNode } from '../../utilities/isEmptyNode';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport BitkitIconButton from '../BitkitIconButton/BitkitIconButton';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\nexport interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {\n /**\n * Avatar, app icon or thumbnail rendered before the title. Hidden in the collapsed layout.\n *\n * Belongs to the sub-page layouts: the design's main-page header is a title and its subtitle with\n * no avatar, so pass this alongside a `breadcrumb`.\n *\n * Size it to match the design's two sub-page cards, since the node is yours to build: `size=\"40\"`\n * (8px radius) against a bare title, and `size=\"48\"` (10px radius) once `secondaryText` is\n * present — the same avatar the `app` and `user` content shapes use.\n */\n avatar?: ReactNode;\n /** Badge rendered next to the title. Hidden in the collapsed layout. */\n badge?: ReactNode;\n /**\n * Breadcrumb trail above the title — pass a `BitkitBreadcrumb`. Its presence is what switches the\n * header from the design's main-page layout to a sub-page one, so there is no separate page-type\n * prop: the recipe's own `hasBreadcrumb` variant is derived from it.\n *\n * The two layouts carry more than the trail. A main-page header is a title and its subtitle —\n * top-level pages and product overviews — with no avatar and no `app`/`user` content shape. Those\n * belong to sub-pages, so pass them together with a trail.\n */\n breadcrumb?: ReactNode;\n /**\n * Trailing action buttons. Stacked full-width below the title on mobile, primary action on top.\n *\n * The collapsed row is built for `size=\"sm\"` buttons and the expanded header for `size=\"md\"`.\n * Actions arrive as children, so the component cannot set that for you — pass a flat `size=\"sm\"`\n * whenever `minimal` or `isSticky` is in play.\n *\n * Flat, not responsive: Bitkit icons render a different SVG asset per size, so `BitkitButton`\n * resolves `size` in JS and a `{ base, tablet }` object matches no key, falling through to a 24px\n * icon in a 32px button. Since the header collapses in CSS from `tablet` up, a `sm` action is a\n * little small in the expanded mobile layout — but the design draws no mobile frame for the\n * collapsed variants, so `sm` is the closer of the two.\n */\n children?: ReactNode;\n /**\n * Shape of the title block, mirroring the design's `content` property. `title` is a page title\n * with an optional subtitle; `app` and `user` are the tighter entity layouts — a larger avatar\n * centred against a smaller title and 14px secondary text. They differ only in the gap between\n * the avatar and the text, 16 and 12 respectively.\n */\n contentVariant?: 'title' | 'app' | 'user';\n /**\n * Lines the header's content up with the page content below it: pass the `BitkitContentWrapper`'s value. The title,\n * the actions and the tabs are capped at that width and centred, while the background and the bottom border still run\n * edge to edge. `'full'` keeps the same 32px inset without a cap. Without it the header keeps the default inset, which\n * lines up with a sidebar below it.\n */\n contentWidth?: BitkitContentWidth;\n /**\n * Inline controls between the trail and the actions — a view switcher, filters, a settings menu.\n * Their presence spreads the row into three groups instead of the usual trail-and-actions pair,\n * which is the shape the design file calls `PageHeader-WFE`.\n *\n * Belongs to the collapsed row, so it is honoured by `variant=\"minimal\"` *and* by `isSticky` —\n * the two produce the same row — and ignored in the expanded layouts, which the design draws no\n * controls in.\n */\n controls?: ReactNode;\n /**\n * Renders the compact single-row layout used while the header is stuck to the top of the viewport.\n * Appearance only — the consumer owns scroll detection and any portalling, since the sticky offset\n * and header container are application concerns.\n *\n * Ignored below the `tablet` breakpoint, where the header always stays expanded.\n *\n * Pair it with `size=\"sm\"` actions — see `children`.\n */\n isSticky?: boolean;\n /**\n * Opens the page's sidebar on mobile. Its presence switches the header to the design's\n * `hasSidebar` layout below `tablet`: the title shrinks beside a menu button, and the\n * `sectionTitle` takes over as the large heading under a rule, with the actions below it. Wire it\n * to a `BitkitPageSidebar`'s `open` / `onOpenChange`. From `tablet` the button is hidden and the\n * header is the ordinary one — the sidebar is beside the page there.\n */\n onMenuClick?: () => void;\n /**\n * Descriptive text below the title. Hidden in the collapsed layout.\n *\n * A string, like `title`, so it can be truncated to one line and carried in full by the tooltip\n * that appears when it does not fit.\n */\n secondaryText?: string;\n /**\n * Draws the line along the header's bottom edge. Set it to `false` for a header that flows\n * straight into the page, and the bottom padding goes with the line: the header's background is\n * the page's, so without the line that padding would only be a margin the page cannot shrink. The\n * page spaces its content from the header itself.\n *\n * The collapsed row keeps its padding, which centres the row rather than spacing the page, and from\n * `tablet` a header with `isSticky` keeps its line as well, since content scrolls under it. Below\n * `tablet` `isSticky` is ignored, so there the line goes too.\n *\n * Ignored with `tabs`, whose list sits on that edge. That includes `variant=\"minimal\"`, which hides\n * the tab list from `tablet`: the line stays although no tabs show above it.\n */\n showDivider?: boolean;\n /**\n * The sidebar section the page is showing, for the `hasSidebar` layout — see `onMenuClick`. It\n * is the page's visible `h2` on mobile, where the sidebar that would name it is behind the menu\n * button. Not rendered from `tablet`, and never without `onMenuClick`.\n */\n sectionTitle?: string;\n /**\n * Tab bar along the bottom edge — pass a `BitkitTabs.List`. Hidden in the collapsed layout.\n *\n * The node is rendered inside the header, so tab panels cannot be siblings of the row. To drive\n * page content from it, put `BitkitTabs.Root` around the header *and* the content, and pass only\n * the list here — `BitkitTabs.List` reads its Root from context and throws without an ancestor:\n *\n * ```tsx\n * <BitkitTabs.Root defaultValue=\"overview\">\n * <BitkitPageHeader tabs={<BitkitTabs.List>…</BitkitTabs.List>} title=\"Build Hub\" />\n * <BitkitTabs.ContentGroup>\n * <BitkitTabs.Content padding=\"32\" value=\"overview\">…</BitkitTabs.Content>\n * </BitkitTabs.ContentGroup>\n * </BitkitTabs.Root>\n * ```\n *\n * Only the row is hidden when the header collapses — the panels live outside it and keep\n * rendering, so the page never blanks.\n */\n tabs?: ReactNode;\n /**\n * The page title, as a string. Everything passed here lands inside the `h1`, so it is the\n * heading's accessible name and nothing else belongs in it — the avatar, badge and secondary\n * text each have their own slot. Required: it is what gives the header a page heading at every\n * breakpoint.\n *\n * Truncated to a single line with an ellipsis. When it does not fit, hovering or focusing it\n * shows the full string in a tooltip — so a long title never grows the header.\n *\n * In the collapsed layout (`minimal` or `isSticky`) *with* a `breadcrumb`, the design shows the\n * title in the trail rather than as a heading, so end the breadcrumb with a\n * `BitkitBreadcrumb.CurrentItem` carrying it — the title is then rendered as a visually hidden\n * `h1`, since `Breadcrumb.CurrentLink` is a link and not a heading. Below `tablet` the header\n * never collapses and the visible heading is used instead (`BitkitBreadcrumb` drops its\n * `CurrentItem` on mobile).\n */\n title: string;\n /**\n * `minimal` collapses the header to the single breadcrumb row used by sub-pages that don't repeat\n * their title as a heading. Like `isSticky`, it only collapses from `tablet` up, and expects\n * `size=\"sm\"` actions — see `children`.\n */\n variant?: 'default' | 'minimal';\n}\n\nconst BitkitPageHeader = forwardRef<HTMLElement, BitkitPageHeaderProps>((props, ref) => {\n const {\n avatar,\n badge,\n breadcrumb,\n children,\n contentVariant,\n contentWidth,\n controls,\n css,\n isSticky,\n onMenuClick,\n secondaryText,\n sectionTitle,\n showDivider = true,\n tabs,\n title,\n variant,\n ...rest\n } = props;\n\n // Which slots the collapsed row drops, and whether it collapses at all, is decided entirely by\n // the recipe's `tablet`-scoped styles. Nothing here branches on the viewport: a breakpoint hook\n // would render the wrong layout on first paint, since Chakra's `useMediaQuery` returns its\n // fallback until after mount.\n\n // A slot's presence is its own toggle, so all of them share one emptiness test rather than plain\n // truthiness: an array whose entries all render nothing — `items.map()` where every branch is\n // `null` — is still truthy, and would otherwise render a wrapper that only contributes gap. For\n // `controls` it would also spread the row into three groups around an empty middle one.\n const hasAvatar = !isEmptyNode(avatar);\n const hasBadge = !isEmptyNode(badge);\n const hasBreadcrumb = !isEmptyNode(breadcrumb);\n const hasActions = !isEmptyNode(children);\n const hasSecondaryText = !isEmptyNode(secondaryText);\n const hasSidebar = onMenuClick !== undefined;\n const hasSectionTitle = hasSidebar && !isEmptyNode(sectionTitle);\n const hasTabs = !isEmptyNode(tabs);\n\n // `BitkitOverflowTooltip` does no measuring of its own — it shows the tooltip unless told not to,\n // so each truncating text reports whether it is actually clipped and suppresses its own tooltip\n // when it is not. Keyed on the text so a change re-measures without waiting for a resize.\n const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated<HTMLHeadingElement>(title);\n const { isTruncated: isSecondaryTruncated, ref: secondaryRef } = useIsTruncated<HTMLParagraphElement>(secondaryText);\n\n // The design only draws inline controls in the collapsed row, so they are ignored in the\n // expanded layouts rather than left to land somewhere undesigned. `minimal` and `isSticky`\n // produce that row through the same `collapsedSlots`, so both have to honour them — gating on\n // `minimal` alone left `isSticky` rendering the identical row with the middle group missing.\n const isCollapsed = variant === 'minimal' || Boolean(isSticky);\n const showControls = !isEmptyNode(controls) && isCollapsed;\n\n // The collapsed row with a trail makes the title block `srOnly`, which is a 1px box — so the\n // measurement reports \"truncated\" for a heading nobody can see. Left alone that puts a tab stop\n // on an invisible element and opens a tooltip against it. The trail already carries the title as\n // its current item in exactly that layout, so there is nothing to surface anyway.\n const canShowTitleTooltip = isTitleTruncated && !(isCollapsed && hasBreadcrumb);\n\n const capWidth = contentMaxWidth(contentWidth);\n\n const recipe = useSlotRecipe({ key: 'pageHeader' });\n const styles = recipe({\n contentVariant,\n hasBreadcrumb,\n hasControls: showControls,\n hasDivider: showDivider || hasTabs,\n hasSidebar,\n alignsToContent: Boolean(contentWidth),\n isSticky,\n variant,\n });\n\n // The controls are the middle of three groups, so they follow whichever element holds the left\n // slot: the trail when there is one, otherwise the title — which lives inside `contentBlock`, so\n // they have to go in there with it rather than beside it.\n const controlsNode = showControls ? <Box css={styles.controls}>{controls}</Box> : null;\n\n // With a sidebar the menu button shares the title's row, so the block gets a wrapper the content\n // row treats as the title block. Other headers keep their DOM.\n const titleBlock = (\n <Box css={styles.titleBlock}>\n {hasAvatar && <Box css={styles.avatar}>{avatar}</Box>}\n\n <Box css={styles.textBlock}>\n <Box css={styles.titleRow}>\n {/*\n `bottom-start` rather than the tooltip's default `right`: these texts are\n full-width rows, so a right-hand tooltip lands over the actions. Opening below and\n aligned to the text's start keeps it clear of them and lines it up with the text\n it repeats.\n */}\n <BitkitOverflowTooltip disabled={!canShowTitleTooltip} placement=\"bottom-start\" text={title}>\n {/*\n `tabIndex` only while truncated: the tooltip trigger is rendered `asChild`, so an\n `h1` stays unfocusable and the full title would be mouse-only. Adding the tab\n stop unconditionally would put one in every header for no benefit.\n */}\n <Text as=\"h1\" css={styles.title} ref={titleRef} tabIndex={canShowTitleTooltip ? 0 : undefined}>\n {title}\n </Text>\n </BitkitOverflowTooltip>\n {hasBadge && <Box css={styles.badge}>{badge}</Box>}\n </Box>\n\n {hasSecondaryText && secondaryText !== undefined && (\n <BitkitOverflowTooltip disabled={!isSecondaryTruncated} placement=\"bottom-start\" text={secondaryText}>\n <Text css={styles.secondaryText} ref={secondaryRef} tabIndex={isSecondaryTruncated ? 0 : undefined}>\n {secondaryText}\n </Text>\n </BitkitOverflowTooltip>\n )}\n </Box>\n </Box>\n );\n\n return (\n <Box as=\"header\" css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.headerBlock} maxWidth={capWidth}>\n {hasBreadcrumb && <Box css={styles.breadcrumb}>{breadcrumb}</Box>}\n\n {hasBreadcrumb && controlsNode}\n\n <Box css={styles.contentBlock}>\n {hasSidebar ? (\n <Box css={styles.titleBar}>\n {titleBlock}\n <Box css={styles.menuButton}>\n <BitkitIconButton\n icon={IconMenuHamburger}\n label=\"Open navigation\"\n onClick={onMenuClick}\n size=\"sm\"\n variant=\"secondary\"\n />\n </Box>\n </Box>\n ) : (\n titleBlock\n )}\n\n {hasSectionTitle && (\n <Box css={styles.sectionBlock}>\n <Text as=\"h2\" css={styles.sectionTitle}>\n {sectionTitle}\n </Text>\n </Box>\n )}\n\n {!hasBreadcrumb && controlsNode}\n\n {hasActions && <Box css={styles.actionBlock}>{children}</Box>}\n </Box>\n </Box>\n\n {hasTabs && (\n <Box css={styles.tabs} maxWidth={capWidth}>\n {tabs}\n </Box>\n )}\n </Box>\n );\n});\n\nBitkitPageHeader.displayName = 'BitkitPageHeader';\n\nexport default BitkitPageHeader;\n"],"mappings":"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"}
1
+ {"version":3,"file":"BitkitPageHeader.js","names":[],"sources":["../../../lib/components/BitkitPageHeader/BitkitPageHeader.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport IconMenuHamburger from '../../icons/IconMenuHamburger';\nimport { isEmptyNode } from '../../utilities/isEmptyNode';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport BitkitIconButton from '../BitkitIconButton/BitkitIconButton';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\nexport interface BitkitPageHeaderProps extends Omit<BoxProps, 'title'> {\n /**\n * Avatar, app icon or thumbnail rendered before the title. Hidden in the collapsed layout.\n *\n * Belongs to the sub-page layouts: the design's main-page header is a title and its subtitle with\n * no avatar, so pass this alongside a `breadcrumb`.\n *\n * Size it to match the design's two sub-page cards, since the node is yours to build: `size=\"40\"`\n * (8px radius) against a bare title, and `size=\"48\"` (10px radius) once `secondaryText` is\n * present — the same avatar the `app` and `user` content shapes use.\n */\n avatar?: ReactNode;\n /** Badge rendered next to the title. Hidden in the collapsed layout. */\n badge?: ReactNode;\n /**\n * Breadcrumb trail above the title — pass a `BitkitBreadcrumb`. Its presence is what switches the\n * header from the design's main-page layout to a sub-page one, so there is no separate page-type\n * prop: the recipe's own `hasBreadcrumb` variant is derived from it.\n *\n * The two layouts carry more than the trail. A main-page header is a title and its subtitle —\n * top-level pages and product overviews — with no avatar and no `app`/`user` content shape. Those\n * belong to sub-pages, so pass them together with a trail.\n */\n breadcrumb?: ReactNode;\n /**\n * Trailing action buttons. Stacked full-width below the title on mobile, primary action on top.\n *\n * The collapsed row is built for `size=\"sm\"` buttons and the expanded header for `size=\"md\"`.\n * Actions arrive as children, so the component cannot set that for you — pass a flat `size=\"sm\"`\n * whenever `minimal` or `isSticky` is in play.\n *\n * Flat, not responsive: Bitkit icons render a different SVG asset per size, so `BitkitButton`\n * resolves `size` in JS and a `{ base, tablet }` object matches no key, falling through to a 24px\n * icon in a 32px button. Since the header collapses in CSS from `tablet` up, a `sm` action is a\n * little small in the expanded mobile layout — but the design draws no mobile frame for the\n * collapsed variants, so `sm` is the closer of the two.\n */\n children?: ReactNode;\n /**\n * Shape of the title block, mirroring the design's `content` property. `title` is a page title\n * with an optional subtitle; `app` and `user` are the tighter entity layouts — a larger avatar\n * centred against a smaller title and 14px secondary text. They differ only in the gap between\n * the avatar and the text, 16 and 12 respectively.\n */\n contentVariant?: 'title' | 'app' | 'user';\n /**\n * Lines the header's content up with the page content below it: pass the `BitkitContentWrapper`'s value. The title,\n * the actions and the tabs are capped at that width and centred, while the background and the bottom border still run\n * edge to edge. `layout/maxWidth/full` keeps the same 32px inset without a cap. Without it the header keeps the default inset, which\n * lines up with a sidebar below it.\n */\n contentWidth?: BitkitContentWidth;\n /**\n * Inline controls between the trail and the actions — a view switcher, filters, a settings menu.\n * Their presence spreads the row into three groups instead of the usual trail-and-actions pair,\n * which is the shape the design file calls `PageHeader-WFE`.\n *\n * Belongs to the collapsed row, so it is honoured by `variant=\"minimal\"` *and* by `isSticky` —\n * the two produce the same row — and ignored in the expanded layouts, which the design draws no\n * controls in.\n */\n controls?: ReactNode;\n /**\n * Renders the compact single-row layout used while the header is stuck to the top of the viewport.\n * Appearance only — the consumer owns scroll detection and any portalling, since the sticky offset\n * and header container are application concerns.\n *\n * Ignored below the `tablet` breakpoint, where the header always stays expanded.\n *\n * Pair it with `size=\"sm\"` actions — see `children`.\n */\n isSticky?: boolean;\n /**\n * Opens the page's sidebar on mobile. Its presence switches the header to the design's\n * `hasSidebar` layout below `tablet`: the title shrinks beside a menu button, and the\n * `sectionTitle` takes over as the large heading under a rule, with the actions below it. Wire it\n * to a `BitkitPageSidebar`'s `open` / `onOpenChange`. From `tablet` the button is hidden and the\n * header is the ordinary one — the sidebar is beside the page there.\n */\n onMenuClick?: () => void;\n /**\n * Descriptive text below the title. Hidden in the collapsed layout.\n *\n * A string, like `title`, so it can be truncated to one line and carried in full by the tooltip\n * that appears when it does not fit.\n */\n secondaryText?: string;\n /**\n * Draws the line along the header's bottom edge. Set it to `false` for a header that flows\n * straight into the page, and the bottom padding goes with the line: the header's background is\n * the page's, so without the line that padding would only be a margin the page cannot shrink. The\n * page spaces its content from the header itself.\n *\n * The collapsed row keeps its padding, which centres the row rather than spacing the page, and from\n * `tablet` a header with `isSticky` keeps its line as well, since content scrolls under it. Below\n * `tablet` `isSticky` is ignored, so there the line goes too.\n *\n * Ignored with `tabs`, whose list sits on that edge. That includes `variant=\"minimal\"`, which hides\n * the tab list from `tablet`: the line stays although no tabs show above it.\n */\n showDivider?: boolean;\n /**\n * The sidebar section the page is showing, for the `hasSidebar` layout — see `onMenuClick`. It\n * is the page's visible `h2` on mobile, where the sidebar that would name it is behind the menu\n * button. Not rendered from `tablet`, and never without `onMenuClick`.\n */\n sectionTitle?: string;\n /**\n * Tab bar along the bottom edge — pass a `BitkitTabs.List`. Hidden in the collapsed layout.\n *\n * The node is rendered inside the header, so tab panels cannot be siblings of the row. To drive\n * page content from it, put `BitkitTabs.Root` around the header *and* the content, and pass only\n * the list here — `BitkitTabs.List` reads its Root from context and throws without an ancestor:\n *\n * ```tsx\n * <BitkitTabs.Root defaultValue=\"overview\">\n * <BitkitPageHeader tabs={<BitkitTabs.List>…</BitkitTabs.List>} title=\"Build Hub\" />\n * <BitkitTabs.ContentGroup>\n * <BitkitTabs.Content padding=\"32\" value=\"overview\">…</BitkitTabs.Content>\n * </BitkitTabs.ContentGroup>\n * </BitkitTabs.Root>\n * ```\n *\n * Only the row is hidden when the header collapses — the panels live outside it and keep\n * rendering, so the page never blanks.\n */\n tabs?: ReactNode;\n /**\n * The page title, as a string. Everything passed here lands inside the `h1`, so it is the\n * heading's accessible name and nothing else belongs in it — the avatar, badge and secondary\n * text each have their own slot. Required: it is what gives the header a page heading at every\n * breakpoint.\n *\n * Truncated to a single line with an ellipsis. When it does not fit, hovering or focusing it\n * shows the full string in a tooltip — so a long title never grows the header.\n *\n * In the collapsed layout (`minimal` or `isSticky`) *with* a `breadcrumb`, the design shows the\n * title in the trail rather than as a heading, so end the breadcrumb with a\n * `BitkitBreadcrumb.CurrentItem` carrying it — the title is then rendered as a visually hidden\n * `h1`, since `Breadcrumb.CurrentLink` is a link and not a heading. Below `tablet` the header\n * never collapses and the visible heading is used instead (`BitkitBreadcrumb` drops its\n * `CurrentItem` on mobile).\n */\n title: string;\n /**\n * `minimal` collapses the header to the single breadcrumb row used by sub-pages that don't repeat\n * their title as a heading. Like `isSticky`, it only collapses from `tablet` up, and expects\n * `size=\"sm\"` actions — see `children`.\n */\n variant?: 'default' | 'minimal';\n}\n\nconst BitkitPageHeader = forwardRef<HTMLElement, BitkitPageHeaderProps>((props, ref) => {\n const {\n avatar,\n badge,\n breadcrumb,\n children,\n contentVariant,\n contentWidth,\n controls,\n css,\n isSticky,\n onMenuClick,\n secondaryText,\n sectionTitle,\n showDivider = true,\n tabs,\n title,\n variant,\n ...rest\n } = props;\n\n // Which slots the collapsed row drops, and whether it collapses at all, is decided entirely by\n // the recipe's `tablet`-scoped styles. Nothing here branches on the viewport: a breakpoint hook\n // would render the wrong layout on first paint, since Chakra's `useMediaQuery` returns its\n // fallback until after mount.\n\n // A slot's presence is its own toggle, so all of them share one emptiness test rather than plain\n // truthiness: an array whose entries all render nothing — `items.map()` where every branch is\n // `null` — is still truthy, and would otherwise render a wrapper that only contributes gap. For\n // `controls` it would also spread the row into three groups around an empty middle one.\n const hasAvatar = !isEmptyNode(avatar);\n const hasBadge = !isEmptyNode(badge);\n const hasBreadcrumb = !isEmptyNode(breadcrumb);\n const hasActions = !isEmptyNode(children);\n const hasSecondaryText = !isEmptyNode(secondaryText);\n const hasSidebar = onMenuClick !== undefined;\n const hasSectionTitle = hasSidebar && !isEmptyNode(sectionTitle);\n const hasTabs = !isEmptyNode(tabs);\n\n // `BitkitOverflowTooltip` does no measuring of its own — it shows the tooltip unless told not to,\n // so each truncating text reports whether it is actually clipped and suppresses its own tooltip\n // when it is not. Keyed on the text so a change re-measures without waiting for a resize.\n const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated<HTMLHeadingElement>(title);\n const { isTruncated: isSecondaryTruncated, ref: secondaryRef } = useIsTruncated<HTMLParagraphElement>(secondaryText);\n\n // The design only draws inline controls in the collapsed row, so they are ignored in the\n // expanded layouts rather than left to land somewhere undesigned. `minimal` and `isSticky`\n // produce that row through the same `collapsedSlots`, so both have to honour them — gating on\n // `minimal` alone left `isSticky` rendering the identical row with the middle group missing.\n const isCollapsed = variant === 'minimal' || Boolean(isSticky);\n const showControls = !isEmptyNode(controls) && isCollapsed;\n\n // The collapsed row with a trail makes the title block `srOnly`, which is a 1px box — so the\n // measurement reports \"truncated\" for a heading nobody can see. Left alone that puts a tab stop\n // on an invisible element and opens a tooltip against it. The trail already carries the title as\n // its current item in exactly that layout, so there is nothing to surface anyway.\n const canShowTitleTooltip = isTitleTruncated && !(isCollapsed && hasBreadcrumb);\n\n const recipe = useSlotRecipe({ key: 'pageHeader' });\n const styles = recipe({\n contentVariant,\n hasBreadcrumb,\n hasControls: showControls,\n hasDivider: showDivider || hasTabs,\n hasSidebar,\n alignsToContent: Boolean(contentWidth),\n isSticky,\n variant,\n });\n\n // The controls are the middle of three groups, so they follow whichever element holds the left\n // slot: the trail when there is one, otherwise the title — which lives inside `contentBlock`, so\n // they have to go in there with it rather than beside it.\n const controlsNode = showControls ? <Box css={styles.controls}>{controls}</Box> : null;\n\n // With a sidebar the menu button shares the title's row, so the block gets a wrapper the content\n // row treats as the title block. Other headers keep their DOM.\n const titleBlock = (\n <Box css={styles.titleBlock}>\n {hasAvatar && <Box css={styles.avatar}>{avatar}</Box>}\n\n <Box css={styles.textBlock}>\n <Box css={styles.titleRow}>\n {/*\n `bottom-start` rather than the tooltip's default `right`: these texts are\n full-width rows, so a right-hand tooltip lands over the actions. Opening below and\n aligned to the text's start keeps it clear of them and lines it up with the text\n it repeats.\n */}\n <BitkitOverflowTooltip disabled={!canShowTitleTooltip} placement=\"bottom-start\" text={title}>\n {/*\n `tabIndex` only while truncated: the tooltip trigger is rendered `asChild`, so an\n `h1` stays unfocusable and the full title would be mouse-only. Adding the tab\n stop unconditionally would put one in every header for no benefit.\n */}\n <Text as=\"h1\" css={styles.title} ref={titleRef} tabIndex={canShowTitleTooltip ? 0 : undefined}>\n {title}\n </Text>\n </BitkitOverflowTooltip>\n {hasBadge && <Box css={styles.badge}>{badge}</Box>}\n </Box>\n\n {hasSecondaryText && secondaryText !== undefined && (\n <BitkitOverflowTooltip disabled={!isSecondaryTruncated} placement=\"bottom-start\" text={secondaryText}>\n <Text css={styles.secondaryText} ref={secondaryRef} tabIndex={isSecondaryTruncated ? 0 : undefined}>\n {secondaryText}\n </Text>\n </BitkitOverflowTooltip>\n )}\n </Box>\n </Box>\n );\n\n return (\n <Box as=\"header\" css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.headerBlock} maxWidth={contentWidth}>\n {hasBreadcrumb && <Box css={styles.breadcrumb}>{breadcrumb}</Box>}\n\n {hasBreadcrumb && controlsNode}\n\n <Box css={styles.contentBlock}>\n {hasSidebar ? (\n <Box css={styles.titleBar}>\n {titleBlock}\n <Box css={styles.menuButton}>\n <BitkitIconButton\n icon={IconMenuHamburger}\n label=\"Open navigation\"\n onClick={onMenuClick}\n size=\"sm\"\n variant=\"secondary\"\n />\n </Box>\n </Box>\n ) : (\n titleBlock\n )}\n\n {hasSectionTitle && (\n <Box css={styles.sectionBlock}>\n <Text as=\"h2\" css={styles.sectionTitle}>\n {sectionTitle}\n </Text>\n </Box>\n )}\n\n {!hasBreadcrumb && controlsNode}\n\n {hasActions && <Box css={styles.actionBlock}>{children}</Box>}\n </Box>\n </Box>\n\n {hasTabs && (\n <Box css={styles.tabs} maxWidth={contentWidth}>\n {tabs}\n </Box>\n )}\n </Box>\n );\n});\n\nBitkitPageHeader.displayName = 'BitkitPageHeader';\n\nexport default BitkitPageHeader;\n"],"mappings":"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"}
@@ -15,8 +15,8 @@ var BitkitPageSubheader = forwardRef((props, ref) => {
15
15
  const hasTabs = !isEmptyNode(tabs);
16
16
  const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated(title);
17
17
  const styles = useSlotRecipe({ key: "pageSubheader" })({
18
- contentWidth,
19
- hasTabs
18
+ hasTabs,
19
+ isCapped: Boolean(contentWidth) && contentWidth !== "layout/maxWidth/full"
20
20
  });
21
21
  return /* @__PURE__ */ jsx(Box, {
22
22
  css: [styles.root, css],
@@ -24,6 +24,7 @@ var BitkitPageSubheader = forwardRef((props, ref) => {
24
24
  ...rest,
25
25
  children: /* @__PURE__ */ jsxs(Box, {
26
26
  css: styles.contentBlock,
27
+ maxWidth: contentWidth,
27
28
  children: [/* @__PURE__ */ jsxs(Box, {
28
29
  css: styles.headerBlock,
29
30
  children: [/* @__PURE__ */ jsxs(Box, {
@@ -1 +1 @@
1
- {"version":3,"file":"BitkitPageSubheader.js","names":[],"sources":["../../../lib/components/BitkitPageSubheader/BitkitPageSubheader.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport { isEmptyNode } from '../../utilities/isEmptyNode';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\nexport interface BitkitPageSubheaderProps extends Omit<BoxProps, 'title'> {\n /** The actions, secondary first and primary last — mobile shows them the other way round. */\n children?: ReactNode;\n /** Caps the content at the page content column's width, so the title lines up with it. Use the `BitkitContentWrapper`'s value. */\n contentWidth?: BitkitContentWidth;\n /** One line under the title, inline content such as a \"Learn more\" link included. */\n secondaryText?: ReactNode;\n /** A `BitkitTabs.List`, rendered under the title; wrap the page in `BitkitTabs.Root` so the panels can live outside. */\n tabs?: ReactNode;\n /** The sub-page title, rendered as an `h2` under the page header's `h1`. */\n title: string;\n}\n\n/** The title bar of a sub-page, under a page header whose sidebar sits beside the content. */\nconst BitkitPageSubheader = forwardRef<HTMLDivElement, BitkitPageSubheaderProps>((props, ref) => {\n const { children, contentWidth, css, secondaryText, tabs, title, ...rest } = props;\n\n const hasActions = !isEmptyNode(children);\n const hasSecondaryText = !isEmptyNode(secondaryText);\n const hasTabs = !isEmptyNode(tabs);\n\n const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated<HTMLHeadingElement>(title);\n\n const recipe = useSlotRecipe({ key: 'pageSubheader' });\n const styles = recipe({ contentWidth, hasTabs });\n\n return (\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.contentBlock}>\n <Box css={styles.headerBlock}>\n <Box css={styles.textBlock}>\n <BitkitOverflowTooltip disabled={!isTitleTruncated} placement=\"bottom-start\" text={title}>\n <Text as=\"h2\" css={styles.title} ref={titleRef} tabIndex={isTitleTruncated ? 0 : undefined}>\n {title}\n </Text>\n </BitkitOverflowTooltip>\n\n {hasSecondaryText && <Text css={styles.secondaryText}>{secondaryText}</Text>}\n </Box>\n\n {hasActions && <Box css={styles.actionBlock}>{children}</Box>}\n </Box>\n\n {hasTabs && <Box css={styles.tabs}>{tabs}</Box>}\n </Box>\n </Box>\n );\n});\n\nBitkitPageSubheader.displayName = 'BitkitPageSubheader';\n\nexport default BitkitPageSubheader;\n"],"mappings":";;;;;;;;;;AAwBA,IAAM,sBAAsB,YAAsD,OAAO,QAAQ;CAC/F,MAAM,EAAE,UAAU,cAAc,KAAK,eAAe,MAAM,OAAO,GAAG,SAAS;CAE7E,MAAM,aAAa,CAAC,YAAY,QAAQ;CACxC,MAAM,mBAAmB,CAAC,YAAY,aAAa;CACnD,MAAM,UAAU,CAAC,YAAY,IAAI;CAEjC,MAAM,EAAE,aAAa,kBAAkB,KAAK,aAAa,eAAmC,KAAK;CAGjG,MAAM,SADS,cAAc,EAAE,KAAK,gBAAgB,CACrC,CAAA,CAAO;EAAE;EAAc;CAAQ,CAAC;CAE/C,OACE,oBAAC,KAAD;EAAK,KAAK,CAAC,OAAO,MAAM,GAAG;EAAQ;EAAK,GAAI;EAC1C,UAAA,qBAAC,KAAD;GAAK,KAAK,OAAO;GAAjB,UAAA,CACE,qBAAC,KAAD;IAAK,KAAK,OAAO;IAAjB,UAAA,CACE,qBAAC,KAAD;KAAK,KAAK,OAAO;KAAjB,UAAA,CACE,oBAAC,uBAAD;MAAuB,UAAU,CAAC;MAAkB,WAAU;MAAe,MAAM;MACjF,UAAA,oBAAC,MAAD;OAAM,IAAG;OAAK,KAAK,OAAO;OAAO,KAAK;OAAU,UAAU,mBAAmB,IAAI,KAAA;OAC9E,UAAA;MACG,CAAA;KACe,CAAA,GAEtB,oBAAoB,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAgB,UAAA;KAAoB,CAAA,CACxE;IAEJ,CAAA,GAAA,cAAc,oBAAC,KAAD;KAAK,KAAK,OAAO;KAAc;IAAc,CAAA,CACzD;GAEJ,CAAA,GAAA,WAAW,oBAAC,KAAD;IAAK,KAAK,OAAO;IAAO,UAAA;GAAU,CAAA,CAC3C;;CACF,CAAA;AAET,CAAC;AAED,oBAAoB,cAAc"}
1
+ {"version":3,"file":"BitkitPageSubheader.js","names":[],"sources":["../../../lib/components/BitkitPageSubheader/BitkitPageSubheader.tsx"],"sourcesContent":["import { Box, type BoxProps } from '@chakra-ui/react/box';\nimport { useSlotRecipe } from '@chakra-ui/react/styled-system';\nimport { Text } from '@chakra-ui/react/text';\nimport { forwardRef, type ReactNode } from 'react';\n\nimport { isEmptyNode } from '../../utilities/isEmptyNode';\nimport useIsTruncated from '../../utilities/useIsTruncated';\nimport { type BitkitContentWidth } from '../BitkitContentWrapper/BitkitContentWrapper';\nimport BitkitOverflowTooltip from '../BitkitOverflowTooltip/BitkitOverflowTooltip';\n\nexport interface BitkitPageSubheaderProps extends Omit<BoxProps, 'title'> {\n /** The actions, secondary first and primary last — mobile shows them the other way round. */\n children?: ReactNode;\n /** Caps the content at the page content column's width, so the title lines up with it. Use the `BitkitContentWrapper`'s value. */\n contentWidth?: BitkitContentWidth;\n /** One line under the title, inline content such as a \"Learn more\" link included. */\n secondaryText?: ReactNode;\n /** A `BitkitTabs.List`, rendered under the title; wrap the page in `BitkitTabs.Root` so the panels can live outside. */\n tabs?: ReactNode;\n /** The sub-page title, rendered as an `h2` under the page header's `h1`. */\n title: string;\n}\n\n/** The title bar of a sub-page, under a page header whose sidebar sits beside the content. */\nconst BitkitPageSubheader = forwardRef<HTMLDivElement, BitkitPageSubheaderProps>((props, ref) => {\n const { children, contentWidth, css, secondaryText, tabs, title, ...rest } = props;\n\n const hasActions = !isEmptyNode(children);\n const hasSecondaryText = !isEmptyNode(secondaryText);\n const hasTabs = !isEmptyNode(tabs);\n\n const { isTruncated: isTitleTruncated, ref: titleRef } = useIsTruncated<HTMLHeadingElement>(title);\n\n const recipe = useSlotRecipe({ key: 'pageSubheader' });\n const styles = recipe({ hasTabs, isCapped: Boolean(contentWidth) && contentWidth !== 'layout/maxWidth/full' });\n\n return (\n <Box css={[styles.root, css]} ref={ref} {...rest}>\n <Box css={styles.contentBlock} maxWidth={contentWidth}>\n <Box css={styles.headerBlock}>\n <Box css={styles.textBlock}>\n <BitkitOverflowTooltip disabled={!isTitleTruncated} placement=\"bottom-start\" text={title}>\n <Text as=\"h2\" css={styles.title} ref={titleRef} tabIndex={isTitleTruncated ? 0 : undefined}>\n {title}\n </Text>\n </BitkitOverflowTooltip>\n\n {hasSecondaryText && <Text css={styles.secondaryText}>{secondaryText}</Text>}\n </Box>\n\n {hasActions && <Box css={styles.actionBlock}>{children}</Box>}\n </Box>\n\n {hasTabs && <Box css={styles.tabs}>{tabs}</Box>}\n </Box>\n </Box>\n );\n});\n\nBitkitPageSubheader.displayName = 'BitkitPageSubheader';\n\nexport default BitkitPageSubheader;\n"],"mappings":";;;;;;;;;;AAwBA,IAAM,sBAAsB,YAAsD,OAAO,QAAQ;CAC/F,MAAM,EAAE,UAAU,cAAc,KAAK,eAAe,MAAM,OAAO,GAAG,SAAS;CAE7E,MAAM,aAAa,CAAC,YAAY,QAAQ;CACxC,MAAM,mBAAmB,CAAC,YAAY,aAAa;CACnD,MAAM,UAAU,CAAC,YAAY,IAAI;CAEjC,MAAM,EAAE,aAAa,kBAAkB,KAAK,aAAa,eAAmC,KAAK;CAGjG,MAAM,SADS,cAAc,EAAE,KAAK,gBAAgB,CACrC,CAAA,CAAO;EAAE;EAAS,UAAU,QAAQ,YAAY,KAAK,iBAAiB;CAAuB,CAAC;CAE7G,OACE,oBAAC,KAAD;EAAK,KAAK,CAAC,OAAO,MAAM,GAAG;EAAQ;EAAK,GAAI;EAC1C,UAAA,qBAAC,KAAD;GAAK,KAAK,OAAO;GAAc,UAAU;GAAzC,UAAA,CACE,qBAAC,KAAD;IAAK,KAAK,OAAO;IAAjB,UAAA,CACE,qBAAC,KAAD;KAAK,KAAK,OAAO;KAAjB,UAAA,CACE,oBAAC,uBAAD;MAAuB,UAAU,CAAC;MAAkB,WAAU;MAAe,MAAM;MACjF,UAAA,oBAAC,MAAD;OAAM,IAAG;OAAK,KAAK,OAAO;OAAO,KAAK;OAAU,UAAU,mBAAmB,IAAI,KAAA;OAC9E,UAAA;MACG,CAAA;KACe,CAAA,GAEtB,oBAAoB,oBAAC,MAAD;MAAM,KAAK,OAAO;MAAgB,UAAA;KAAoB,CAAA,CACxE;IAEJ,CAAA,GAAA,cAAc,oBAAC,KAAD;KAAK,KAAK,OAAO;KAAc;IAAc,CAAA,CACzD;GAEJ,CAAA,GAAA,WAAW,oBAAC,KAAD;IAAK,KAAK,OAAO;IAAO,UAAA;GAAU,CAAA,CAC3C;;CACF,CAAA;AAET,CAAC;AAED,oBAAoB,cAAc"}
@@ -1,20 +1,3 @@
1
- /** The page content widths: a `layout/maxWidth` cap, padding included, or `full` for none. */
2
- declare const contentWidthStyles: {
3
- readonly '640': {
4
- readonly maxWidth: "layout/maxWidth/640";
5
- };
6
- readonly '960': {
7
- readonly maxWidth: "layout/maxWidth/960";
8
- };
9
- readonly '1280': {
10
- readonly maxWidth: "layout/maxWidth/1280";
11
- };
12
- readonly '1616': {
13
- readonly maxWidth: "layout/maxWidth/1616";
14
- };
15
- readonly full: {};
16
- };
17
- export type ContentWidth = keyof typeof contentWidthStyles;
18
- /** The `maxWidth` token for a content width, or nothing for `full` and no width at all. */
19
- export declare const contentMaxWidth: (contentWidth?: ContentWidth) => "layout/maxWidth/640" | "layout/maxWidth/960" | "layout/maxWidth/1280" | "layout/maxWidth/1616" | undefined;
20
- export default contentWidthStyles;
1
+ /** The page content widths, padding included: a `layout/maxWidth` token, `full` for no cap. */
2
+ export declare const contentWidths: readonly ["layout/maxWidth/640", "layout/maxWidth/960", "layout/maxWidth/1280", "layout/maxWidth/1616", "layout/maxWidth/full"];
3
+ export type ContentWidth = (typeof contentWidths)[number];
@@ -1,18 +1,2 @@
1
- declare const contentWrapperRecipe: import('@chakra-ui/react').RecipeDefinition<{
2
- contentWidth: {
3
- readonly '640': {
4
- readonly maxWidth: "layout/maxWidth/640";
5
- };
6
- readonly '960': {
7
- readonly maxWidth: "layout/maxWidth/960";
8
- };
9
- readonly '1280': {
10
- readonly maxWidth: "layout/maxWidth/1280";
11
- };
12
- readonly '1616': {
13
- readonly maxWidth: "layout/maxWidth/1616";
14
- };
15
- readonly full: {};
16
- };
17
- }>;
1
+ declare const contentWrapperRecipe: import('@chakra-ui/react').RecipeDefinition<import('@chakra-ui/react').RecipeVariantRecord>;
18
2
  export default contentWrapperRecipe;
@@ -1,4 +1,3 @@
1
- import contentWidthStyles from "../common/ContentWidth.common.js";
2
1
  import { defineRecipe } from "@chakra-ui/react/styled-system";
3
2
  //#region lib/theme/recipes/ContentWrapper.recipe.ts
4
3
  var contentWrapperRecipe = defineRecipe({
@@ -20,8 +19,7 @@ var contentWrapperRecipe = defineRecipe({
20
19
  },
21
20
  rowGap: "32",
22
21
  width: "100%"
23
- },
24
- variants: { contentWidth: contentWidthStyles }
22
+ }
25
23
  });
26
24
  //#endregion
27
25
  export { contentWrapperRecipe as default };
@@ -1 +1 @@
1
- {"version":3,"file":"ContentWrapper.recipe.js","names":[],"sources":["../../../lib/theme/recipes/ContentWrapper.recipe.ts"],"sourcesContent":["import { defineRecipe } from '@chakra-ui/react/styled-system';\n\nimport contentWidthStyles from '../common/ContentWidth.common';\n\nconst contentWrapperRecipe = defineRecipe({\n className: 'content-wrapper',\n base: {\n columnGap: { base: '32', desktop: '64' },\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr)',\n // Lets a `1fr` column shrink below its content's width instead of overflowing the page.\n '& > *': { minWidth: 0 },\n marginInline: 'auto',\n paddingBlockEnd: '48',\n paddingBlockStart: '32',\n paddingInline: { base: '16', tablet: '32' },\n rowGap: '32',\n width: '100%',\n },\n variants: {\n contentWidth: contentWidthStyles,\n },\n});\n\nexport default contentWrapperRecipe;\n"],"mappings":";;;AAIA,IAAM,uBAAuB,aAAa;CACxC,WAAW;CACX,MAAM;EACJ,WAAW;GAAE,MAAM;GAAM,SAAS;EAAK;EACvC,SAAS;EACT,qBAAqB;EAErB,SAAS,EAAE,UAAU,EAAE;EACvB,cAAc;EACd,iBAAiB;EACjB,mBAAmB;EACnB,eAAe;GAAE,MAAM;GAAM,QAAQ;EAAK;EAC1C,QAAQ;EACR,OAAO;CACT;CACA,UAAU,EACR,cAAc,mBAChB;AACF,CAAC"}
1
+ {"version":3,"file":"ContentWrapper.recipe.js","names":[],"sources":["../../../lib/theme/recipes/ContentWrapper.recipe.ts"],"sourcesContent":["import { defineRecipe } from '@chakra-ui/react/styled-system';\n\nconst contentWrapperRecipe = defineRecipe({\n className: 'content-wrapper',\n base: {\n columnGap: { base: '32', desktop: '64' },\n display: 'grid',\n gridTemplateColumns: 'minmax(0, 1fr)',\n // Lets a `1fr` column shrink below its content's width instead of overflowing the page.\n '& > *': { minWidth: 0 },\n marginInline: 'auto',\n paddingBlockEnd: '48',\n paddingBlockStart: '32',\n paddingInline: { base: '16', tablet: '32' },\n rowGap: '32',\n width: '100%',\n },\n});\n\nexport default contentWrapperRecipe;\n"],"mappings":";;AAEA,IAAM,uBAAuB,aAAa;CACxC,WAAW;CACX,MAAM;EACJ,WAAW;GAAE,MAAM;GAAM,SAAS;EAAK;EACvC,SAAS;EACT,qBAAqB;EAErB,SAAS,EAAE,UAAU,EAAE;EACvB,cAAc;EACd,iBAAiB;EACjB,mBAAmB;EACnB,eAAe;GAAE,MAAM;GAAM,QAAQ;EAAK;EAC1C,QAAQ;EACR,OAAO;CACT;AACF,CAAC"}
@@ -1,14 +1,15 @@
1
- import { default as contentWidthStyles } from '../common/ContentWidth.common';
2
1
  declare const pageSubheaderSlotRecipe: import('@chakra-ui/react').SlotRecipeDefinition<"title" | "tabs" | "root" | "secondaryText" | "headerBlock" | "contentBlock" | "textBlock" | "actionBlock", {
3
- contentWidth: { [K in keyof typeof contentWidthStyles]: {
4
- contentBlock: (typeof contentWidthStyles)[K];
5
- tabs?: {
6
- paddingInlineStart: {
7
- base: string;
8
- tablet: string;
2
+ isCapped: {
3
+ true: {
4
+ tabs: {
5
+ paddingInlineStart: {
6
+ base: "16";
7
+ tablet: "32";
8
+ };
9
9
  };
10
10
  };
11
- }; };
11
+ false: {};
12
+ };
12
13
  hasTabs: {
13
14
  true: {
14
15
  root: {
@@ -1,5 +1,4 @@
1
1
  import { rem } from "../themeUtils.js";
2
- import contentWidthStyles from "../common/ContentWidth.common.js";
3
2
  import { defineSlotRecipe } from "@chakra-ui/react/styled-system";
4
3
  //#region lib/theme/slot-recipes/PageSubheader.recipe.ts
5
4
  var pageSubheaderSlotRecipe = defineSlotRecipe({
@@ -107,13 +106,13 @@ var pageSubheaderSlotRecipe = defineSlotRecipe({
107
106
  }
108
107
  },
109
108
  variants: {
110
- contentWidth: Object.fromEntries(Object.entries(contentWidthStyles).map(([key, styles]) => [key, key === "full" ? { contentBlock: styles } : {
111
- contentBlock: styles,
112
- tabs: { paddingInlineStart: {
109
+ isCapped: {
110
+ true: { tabs: { paddingInlineStart: {
113
111
  base: "16",
114
112
  tablet: "32"
115
- } }
116
- }])),
113
+ } } },
114
+ false: {}
115
+ },
117
116
  hasTabs: {
118
117
  true: {
119
118
  root: {
@@ -129,8 +128,8 @@ var pageSubheaderSlotRecipe = defineSlotRecipe({
129
128
  }
130
129
  },
131
130
  defaultVariants: {
132
- contentWidth: "full",
133
- hasTabs: false
131
+ hasTabs: false,
132
+ isCapped: false
134
133
  }
135
134
  });
136
135
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"PageSubheader.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/PageSubheader.recipe.ts"],"sourcesContent":["import { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nimport contentWidthStyles from '../common/ContentWidth.common';\nimport { rem } from '../themeUtils';\n\nconst pageSubheaderSlotRecipe = defineSlotRecipe({\n className: 'page-subheader',\n slots: ['root', 'contentBlock', 'headerBlock', 'textBlock', 'title', 'secondaryText', 'actionBlock', 'tabs'] as const,\n base: {\n root: {\n background: 'background/primary',\n display: 'flex',\n flexDirection: 'column',\n width: '100%',\n },\n contentBlock: {\n display: 'flex',\n flexDirection: 'column',\n marginInline: 'auto',\n width: '100%',\n },\n headerBlock: {\n alignItems: { base: 'stretch', tablet: 'flex-end' },\n display: 'flex',\n flexDirection: { base: 'column', tablet: 'row' },\n gap: { base: '16', tablet: '32' },\n paddingBlockEnd: { base: '16', tablet: '0' },\n paddingBlockStart: { base: '16', tablet: '32' },\n paddingInline: { base: '16', tablet: '32' },\n },\n textBlock: {\n display: 'flex',\n flex: { base: 'none', tablet: '1 0 0' },\n flexDirection: 'column',\n gap: { base: '4', tablet: '0' },\n minWidth: 0,\n },\n title: {\n color: 'text/primary',\n // Desktop keeps the design's 4px under the title with or without the secondary text; mobile has a gap instead.\n paddingBlockEnd: { base: '0', tablet: '4' },\n textStyle: { base: 'heading/mobile/h1', tablet: 'heading/h2' },\n truncate: true,\n },\n secondaryText: {\n color: 'text/secondary',\n textStyle: 'body/md/regular',\n },\n actionBlock: {\n display: 'flex',\n flexShrink: 0,\n // Mobile puts the primary action first, so the row (and its lines, once they wrap) reverses the DOM order.\n flexDirection: { base: 'row-reverse', tablet: 'row' },\n flexWrap: { base: 'wrap-reverse', tablet: 'wrap' },\n gap: '12',\n '& > *': { flex: { base: '1 1 auto', tablet: 'none' } },\n },\n tabs: {\n // Lands the tab list's own bottom line on the root's border, so the subheader keeps one edge.\n marginBlockEnd: rem(-1),\n paddingInlineEnd: '32',\n paddingInlineStart: '16',\n },\n },\n variants: {\n contentWidth: Object.fromEntries(\n Object.entries(contentWidthStyles).map(([key, styles]) => [\n key,\n // The capped design insets the tabs by 32, the full-width one by 16 (pending a design answer on which is right).\n key === 'full'\n ? { contentBlock: styles }\n : { contentBlock: styles, tabs: { paddingInlineStart: { base: '16', tablet: '32' } } },\n ]),\n ) as {\n [K in keyof typeof contentWidthStyles]: {\n contentBlock: (typeof contentWidthStyles)[K];\n tabs?: { paddingInlineStart: { base: string; tablet: string } };\n };\n },\n hasTabs: {\n true: {\n root: {\n borderBlockEnd: '1px solid',\n borderColor: 'border/regular',\n },\n headerBlock: {\n paddingBlockEnd: { base: '16', tablet: '24' },\n },\n },\n false: {},\n },\n },\n defaultVariants: {\n contentWidth: 'full',\n hasTabs: false,\n },\n});\n\nexport default pageSubheaderSlotRecipe;\n"],"mappings":";;;;AAKA,IAAM,0BAA0B,iBAAiB;CAC/C,WAAW;CACX,OAAO;EAAC;EAAQ;EAAgB;EAAe;EAAa;EAAS;EAAiB;EAAe;CAAM;CAC3G,MAAM;EACJ,MAAM;GACJ,YAAY;GACZ,SAAS;GACT,eAAe;GACf,OAAO;EACT;EACA,cAAc;GACZ,SAAS;GACT,eAAe;GACf,cAAc;GACd,OAAO;EACT;EACA,aAAa;GACX,YAAY;IAAE,MAAM;IAAW,QAAQ;GAAW;GAClD,SAAS;GACT,eAAe;IAAE,MAAM;IAAU,QAAQ;GAAM;GAC/C,KAAK;IAAE,MAAM;IAAM,QAAQ;GAAK;GAChC,iBAAiB;IAAE,MAAM;IAAM,QAAQ;GAAI;GAC3C,mBAAmB;IAAE,MAAM;IAAM,QAAQ;GAAK;GAC9C,eAAe;IAAE,MAAM;IAAM,QAAQ;GAAK;EAC5C;EACA,WAAW;GACT,SAAS;GACT,MAAM;IAAE,MAAM;IAAQ,QAAQ;GAAQ;GACtC,eAAe;GACf,KAAK;IAAE,MAAM;IAAK,QAAQ;GAAI;GAC9B,UAAU;EACZ;EACA,OAAO;GACL,OAAO;GAEP,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAI;GAC1C,WAAW;IAAE,MAAM;IAAqB,QAAQ;GAAa;GAC7D,UAAU;EACZ;EACA,eAAe;GACb,OAAO;GACP,WAAW;EACb;EACA,aAAa;GACX,SAAS;GACT,YAAY;GAEZ,eAAe;IAAE,MAAM;IAAe,QAAQ;GAAM;GACpD,UAAU;IAAE,MAAM;IAAgB,QAAQ;GAAO;GACjD,KAAK;GACL,SAAS,EAAE,MAAM;IAAE,MAAM;IAAY,QAAQ;GAAO,EAAE;EACxD;EACA,MAAM;GAEJ,gBAAgB,IAAI,EAAE;GACtB,kBAAkB;GAClB,oBAAoB;EACtB;CACF;CACA,UAAU;EACR,cAAc,OAAO,YACnB,OAAO,QAAQ,kBAAkB,CAAC,CAAC,KAAK,CAAC,KAAK,YAAY,CACxD,KAEA,QAAQ,SACJ,EAAE,cAAc,OAAO,IACvB;GAAE,cAAc;GAAQ,MAAM,EAAE,oBAAoB;IAAE,MAAM;IAAM,QAAQ;GAAK,EAAE;EAAE,CACzF,CAAC,CACH;EAMA,SAAS;GACP,MAAM;IACJ,MAAM;KACJ,gBAAgB;KAChB,aAAa;IACf;IACA,aAAa,EACX,iBAAiB;KAAE,MAAM;KAAM,QAAQ;IAAK,EAC9C;GACF;GACA,OAAO,CAAC;EACV;CACF;CACA,iBAAiB;EACf,cAAc;EACd,SAAS;CACX;AACF,CAAC"}
1
+ {"version":3,"file":"PageSubheader.recipe.js","names":[],"sources":["../../../lib/theme/slot-recipes/PageSubheader.recipe.ts"],"sourcesContent":["import { defineSlotRecipe } from '@chakra-ui/react/styled-system';\n\nimport { rem } from '../themeUtils';\n\nconst pageSubheaderSlotRecipe = defineSlotRecipe({\n className: 'page-subheader',\n slots: ['root', 'contentBlock', 'headerBlock', 'textBlock', 'title', 'secondaryText', 'actionBlock', 'tabs'] as const,\n base: {\n root: {\n background: 'background/primary',\n display: 'flex',\n flexDirection: 'column',\n width: '100%',\n },\n contentBlock: {\n display: 'flex',\n flexDirection: 'column',\n marginInline: 'auto',\n width: '100%',\n },\n headerBlock: {\n alignItems: { base: 'stretch', tablet: 'flex-end' },\n display: 'flex',\n flexDirection: { base: 'column', tablet: 'row' },\n gap: { base: '16', tablet: '32' },\n paddingBlockEnd: { base: '16', tablet: '0' },\n paddingBlockStart: { base: '16', tablet: '32' },\n paddingInline: { base: '16', tablet: '32' },\n },\n textBlock: {\n display: 'flex',\n flex: { base: 'none', tablet: '1 0 0' },\n flexDirection: 'column',\n gap: { base: '4', tablet: '0' },\n minWidth: 0,\n },\n title: {\n color: 'text/primary',\n // Desktop keeps the design's 4px under the title with or without the secondary text; mobile has a gap instead.\n paddingBlockEnd: { base: '0', tablet: '4' },\n textStyle: { base: 'heading/mobile/h1', tablet: 'heading/h2' },\n truncate: true,\n },\n secondaryText: {\n color: 'text/secondary',\n textStyle: 'body/md/regular',\n },\n actionBlock: {\n display: 'flex',\n flexShrink: 0,\n // Mobile puts the primary action first, so the row (and its lines, once they wrap) reverses the DOM order.\n flexDirection: { base: 'row-reverse', tablet: 'row' },\n flexWrap: { base: 'wrap-reverse', tablet: 'wrap' },\n gap: '12',\n '& > *': { flex: { base: '1 1 auto', tablet: 'none' } },\n },\n tabs: {\n // Lands the tab list's own bottom line on the root's border, so the subheader keeps one edge.\n marginBlockEnd: rem(-1),\n paddingInlineEnd: '32',\n paddingInlineStart: '16',\n },\n },\n variants: {\n // The capped design insets the tabs by 32, the full-width one by 16 (pending a design answer on which is right).\n isCapped: {\n true: { tabs: { paddingInlineStart: { base: '16', tablet: '32' } } },\n false: {},\n },\n hasTabs: {\n true: {\n root: {\n borderBlockEnd: '1px solid',\n borderColor: 'border/regular',\n },\n headerBlock: {\n paddingBlockEnd: { base: '16', tablet: '24' },\n },\n },\n false: {},\n },\n },\n defaultVariants: {\n hasTabs: false,\n isCapped: false,\n },\n});\n\nexport default pageSubheaderSlotRecipe;\n"],"mappings":";;;AAIA,IAAM,0BAA0B,iBAAiB;CAC/C,WAAW;CACX,OAAO;EAAC;EAAQ;EAAgB;EAAe;EAAa;EAAS;EAAiB;EAAe;CAAM;CAC3G,MAAM;EACJ,MAAM;GACJ,YAAY;GACZ,SAAS;GACT,eAAe;GACf,OAAO;EACT;EACA,cAAc;GACZ,SAAS;GACT,eAAe;GACf,cAAc;GACd,OAAO;EACT;EACA,aAAa;GACX,YAAY;IAAE,MAAM;IAAW,QAAQ;GAAW;GAClD,SAAS;GACT,eAAe;IAAE,MAAM;IAAU,QAAQ;GAAM;GAC/C,KAAK;IAAE,MAAM;IAAM,QAAQ;GAAK;GAChC,iBAAiB;IAAE,MAAM;IAAM,QAAQ;GAAI;GAC3C,mBAAmB;IAAE,MAAM;IAAM,QAAQ;GAAK;GAC9C,eAAe;IAAE,MAAM;IAAM,QAAQ;GAAK;EAC5C;EACA,WAAW;GACT,SAAS;GACT,MAAM;IAAE,MAAM;IAAQ,QAAQ;GAAQ;GACtC,eAAe;GACf,KAAK;IAAE,MAAM;IAAK,QAAQ;GAAI;GAC9B,UAAU;EACZ;EACA,OAAO;GACL,OAAO;GAEP,iBAAiB;IAAE,MAAM;IAAK,QAAQ;GAAI;GAC1C,WAAW;IAAE,MAAM;IAAqB,QAAQ;GAAa;GAC7D,UAAU;EACZ;EACA,eAAe;GACb,OAAO;GACP,WAAW;EACb;EACA,aAAa;GACX,SAAS;GACT,YAAY;GAEZ,eAAe;IAAE,MAAM;IAAe,QAAQ;GAAM;GACpD,UAAU;IAAE,MAAM;IAAgB,QAAQ;GAAO;GACjD,KAAK;GACL,SAAS,EAAE,MAAM;IAAE,MAAM;IAAY,QAAQ;GAAO,EAAE;EACxD;EACA,MAAM;GAEJ,gBAAgB,IAAI,EAAE;GACtB,kBAAkB;GAClB,oBAAoB;EACtB;CACF;CACA,UAAU;EAER,UAAU;GACR,MAAM,EAAE,MAAM,EAAE,oBAAoB;IAAE,MAAM;IAAM,QAAQ;GAAK,EAAE,EAAE;GACnE,OAAO,CAAC;EACV;EACA,SAAS;GACP,MAAM;IACJ,MAAM;KACJ,gBAAgB;KAChB,aAAa;IACf;IACA,aAAa,EACX,iBAAiB;KAAE,MAAM;KAAM,QAAQ;IAAK,EAC9C;GACF;GACA,OAAO,CAAC;EACV;CACF;CACA,iBAAiB;EACf,SAAS;EACT,UAAU;CACZ;AACF,CAAC"}
@@ -2101,29 +2101,17 @@ declare const slotRecipes: {
2101
2101
  };
2102
2102
  }>;
2103
2103
  pageSubheader: import('@chakra-ui/react').SlotRecipeDefinition<"title" | "tabs" | "root" | "secondaryText" | "headerBlock" | "contentBlock" | "textBlock" | "actionBlock", {
2104
- contentWidth: { [K in "full" | "640" | "960" | "1280" | "1616"]: {
2105
- contentBlock: {
2106
- readonly '640': {
2107
- readonly maxWidth: "layout/maxWidth/640";
2108
- };
2109
- readonly '960': {
2110
- readonly maxWidth: "layout/maxWidth/960";
2111
- };
2112
- readonly '1280': {
2113
- readonly maxWidth: "layout/maxWidth/1280";
2114
- };
2115
- readonly '1616': {
2116
- readonly maxWidth: "layout/maxWidth/1616";
2117
- };
2118
- readonly full: {};
2119
- }[K];
2120
- tabs?: {
2121
- paddingInlineStart: {
2122
- base: string;
2123
- tablet: string;
2104
+ isCapped: {
2105
+ true: {
2106
+ tabs: {
2107
+ paddingInlineStart: {
2108
+ base: "16";
2109
+ tablet: "32";
2110
+ };
2124
2111
  };
2125
2112
  };
2126
- }; };
2113
+ false: {};
2114
+ };
2127
2115
  hasTabs: {
2128
2116
  true: {
2129
2117
  root: {
@@ -553,6 +553,9 @@ declare const tokens: {
553
553
  'layout/maxWidth/1616': {
554
554
  value: string;
555
555
  };
556
+ 'layout/maxWidth/full': {
557
+ value: string;
558
+ };
556
559
  "1": {
557
560
  value: string;
558
561
  };
@@ -11,6 +11,9 @@ declare const sizes: {
11
11
  'layout/maxWidth/1616': {
12
12
  value: string;
13
13
  };
14
+ 'layout/maxWidth/full': {
15
+ value: string;
16
+ };
14
17
  "1": {
15
18
  value: string;
16
19
  };
@@ -6,7 +6,8 @@ var sizes = defineTokens.sizes({
6
6
  "layout/maxWidth/640": { value: "40rem" },
7
7
  "layout/maxWidth/960": { value: "60rem" },
8
8
  "layout/maxWidth/1280": { value: "80rem" },
9
- "layout/maxWidth/1616": { value: "101rem" }
9
+ "layout/maxWidth/1616": { value: "101rem" },
10
+ "layout/maxWidth/full": { value: "100%" }
10
11
  });
11
12
  //#endregion
12
13
  export { sizes as default };
@@ -1 +1 @@
1
- {"version":3,"file":"sizes.js","names":["object"],"sources":["../../../lib/theme/tokens/sizes.ts"],"sourcesContent":["import { defineTokens } from '@chakra-ui/react/styled-system';\n\nimport object from './sizesAndSpacing.json';\n\nconst sizes = defineTokens.sizes({\n ...object,\n 'layout/maxWidth/640': { value: '40rem' },\n 'layout/maxWidth/960': { value: '60rem' },\n 'layout/maxWidth/1280': { value: '80rem' },\n 'layout/maxWidth/1616': { value: '101rem' },\n});\n\nexport default sizes;\n"],"mappings":";;;AAIA,IAAM,QAAQ,aAAa,MAAM;CAC/B,GAAGA;CACH,uBAAuB,EAAE,OAAO,QAAQ;CACxC,uBAAuB,EAAE,OAAO,QAAQ;CACxC,wBAAwB,EAAE,OAAO,QAAQ;CACzC,wBAAwB,EAAE,OAAO,SAAS;AAC5C,CAAC"}
1
+ {"version":3,"file":"sizes.js","names":["object"],"sources":["../../../lib/theme/tokens/sizes.ts"],"sourcesContent":["import { defineTokens } from '@chakra-ui/react/styled-system';\n\nimport object from './sizesAndSpacing.json';\n\nconst sizes = defineTokens.sizes({\n ...object,\n 'layout/maxWidth/640': { value: '40rem' },\n 'layout/maxWidth/960': { value: '60rem' },\n 'layout/maxWidth/1280': { value: '80rem' },\n 'layout/maxWidth/1616': { value: '101rem' },\n 'layout/maxWidth/full': { value: '100%' },\n});\n\nexport default sizes;\n"],"mappings":";;;AAIA,IAAM,QAAQ,aAAa,MAAM;CAC/B,GAAGA;CACH,uBAAuB,EAAE,OAAO,QAAQ;CACxC,uBAAuB,EAAE,OAAO,QAAQ;CACxC,wBAAwB,EAAE,OAAO,QAAQ;CACzC,wBAAwB,EAAE,OAAO,SAAS;CAC1C,wBAAwB,EAAE,OAAO,OAAO;AAC1C,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bitrise/bitkit-v2",
3
3
  "private": false,
4
- "version": "0.3.502-beta.3242",
4
+ "version": "0.3.502-beta.3245",
5
5
  "description": "Bitrise Design System Components built with Chakra UI V3",
6
6
  "keywords": [
7
7
  "react",
@@ -1,15 +0,0 @@
1
- //#region lib/theme/common/ContentWidth.common.ts
2
- /** The page content widths: a `layout/maxWidth` cap, padding included, or `full` for none. */
3
- var contentWidthStyles = {
4
- "640": { maxWidth: "layout/maxWidth/640" },
5
- "960": { maxWidth: "layout/maxWidth/960" },
6
- "1280": { maxWidth: "layout/maxWidth/1280" },
7
- "1616": { maxWidth: "layout/maxWidth/1616" },
8
- full: {}
9
- };
10
- /** The `maxWidth` token for a content width, or nothing for `full` and no width at all. */
11
- var contentMaxWidth = (contentWidth) => contentWidth && contentWidth !== "full" ? contentWidthStyles[contentWidth].maxWidth : void 0;
12
- //#endregion
13
- export { contentMaxWidth, contentWidthStyles as default };
14
-
15
- //# sourceMappingURL=ContentWidth.common.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ContentWidth.common.js","names":[],"sources":["../../../lib/theme/common/ContentWidth.common.ts"],"sourcesContent":["/** The page content widths: a `layout/maxWidth` cap, padding included, or `full` for none. */\nconst contentWidthStyles = {\n '640': { maxWidth: 'layout/maxWidth/640' },\n '960': { maxWidth: 'layout/maxWidth/960' },\n '1280': { maxWidth: 'layout/maxWidth/1280' },\n '1616': { maxWidth: 'layout/maxWidth/1616' },\n full: {},\n} as const;\n\nexport type ContentWidth = keyof typeof contentWidthStyles;\n\n/** The `maxWidth` token for a content width, or nothing for `full` and no width at all. */\nexport const contentMaxWidth = (contentWidth?: ContentWidth) =>\n contentWidth && contentWidth !== 'full' ? contentWidthStyles[contentWidth].maxWidth : undefined;\n\nexport default contentWidthStyles;\n"],"mappings":";;AACA,IAAM,qBAAqB;CACzB,OAAO,EAAE,UAAU,sBAAsB;CACzC,OAAO,EAAE,UAAU,sBAAsB;CACzC,QAAQ,EAAE,UAAU,uBAAuB;CAC3C,QAAQ,EAAE,UAAU,uBAAuB;CAC3C,MAAM,CAAC;AACT;;AAKA,IAAa,mBAAmB,iBAC9B,gBAAgB,iBAAiB,SAAS,mBAAmB,aAAa,CAAC,WAAW,KAAA"}