@janbox/storefront-ui 4.0.1 → 4.0.3

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 (31) hide show
  1. package/README.md +1 -1
  2. package/dist/lib/accordion/accordion-content/accordion-content.js +1 -1
  3. package/dist/lib/badge/badge.js +3 -1
  4. package/dist/lib/box/README.md +14 -1
  5. package/dist/lib/box/box.js +8 -4
  6. package/dist/lib/box/types.d.ts +7 -1
  7. package/dist/lib/container/README.md +14 -1
  8. package/dist/lib/container/container.js +19 -2
  9. package/dist/lib/container/types.d.ts +7 -1
  10. package/dist/lib/flexbox/flex-item/README.md +53 -0
  11. package/dist/lib/flexbox/flex-item/flex-item.js +9 -2
  12. package/dist/lib/flexbox/flex-item/types.d.ts +7 -1
  13. package/dist/lib/flexbox/{README.md → flexbox/README.md} +18 -4
  14. package/dist/lib/flexbox/flexbox/flexbox.js +10 -6
  15. package/dist/lib/flexbox/flexbox/types.d.ts +7 -1
  16. package/dist/lib/floating/floating-content/floating-content.js +5 -1
  17. package/dist/lib/grid/cell/README.md +17 -4
  18. package/dist/lib/grid/cell/cell.js +4 -3
  19. package/dist/lib/grid/cell/types.d.ts +1 -0
  20. package/dist/lib/grid/grid/README.md +16 -4
  21. package/dist/lib/grid/grid/grid.js +10 -9
  22. package/dist/lib/grid/grid/types.d.ts +1 -0
  23. package/dist/lib/notifications/notifications.js +3 -1
  24. package/dist/lib/swiper/swiper-dots-paging/swiper-dots-paging.js +3 -1
  25. package/dist/lib/swiper/swiper-side-arrows-paging/swiper-side-arrows-paging.js +3 -1
  26. package/dist/lib/tabs/tab-indicator/tab-indicator.js +1 -1
  27. package/dist/lib/tooltip/tooltip.js +3 -1
  28. package/dist/theme/index.js +2 -1
  29. package/dist/theme/provider.d.ts +7 -2
  30. package/dist/theme/provider.js +9 -4
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -478,7 +478,7 @@ Mọi component đều có README riêng với API reference, examples, và usag
478
478
  - [Drawer](./dist/lib/drawer/README.md)
479
479
  - [FilterPanel](./dist/lib/filter-panel/README.md)
480
480
  - [Flag](./dist/lib/flag/README.md)
481
- - [Flexbox](./dist/lib/flexbox/README.md)
481
+ - [Flexbox](./dist/lib/flexbox/flexbox/README.md)
482
482
  - [Floating](./dist/lib/floating/README.md)
483
483
  - [FormHelperText](./dist/lib/form-helper-text/README.md)
484
484
  - [FormLabel](./dist/lib/form-label/README.md)
@@ -3,8 +3,8 @@ import { useRef } from "react";
3
3
  import { motion } from "motion/react";
4
4
  import { getAccordionContentProps } from "./helpers.js";
5
5
  import { useAccordionContext } from "../accordion/helpers.js";
6
- import { Stylesheet } from "../../stylesheet/stylesheet.js";
7
6
  import { useAccordionGroupContext } from "../accordion-group/helpers.js";
7
+ import { Stylesheet } from "../../stylesheet/stylesheet.js";
8
8
  import { Primitive } from "../../primitive/primitive.js";
9
9
  const AccordionContent = ({ children, ref, ..._props }) => {
10
10
  const { accordionContentProps: accordionContentPropsByGroup = {} } = useAccordionGroupContext();
@@ -4,11 +4,13 @@ import { useFloating, autoUpdate, offset, hide, useMergeRefs, FloatingPortal } f
4
4
  import { getColorShadesByVariant } from "../../utils/index.js";
5
5
  import { cloneElement } from "react";
6
6
  import { FLOATING_Z_INDEX } from "../../theme/index.js";
7
+ import { usePortalContainer } from "../../theme/provider.js";
7
8
  import { Primitive } from "../primitive/primitive.js";
8
9
  import { getTypographyVar } from "../../theme/typography.js";
9
10
  const Badge = ({ color = "red", content, offset: offset$1 = 0, children }) => {
10
11
  var _a;
11
12
  const { main, contrast } = getColorShadesByVariant(color);
13
+ const portalRoot = usePortalContainer();
12
14
  const {
13
15
  floatingStyles,
14
16
  refs: { setFloating, setReference },
@@ -35,7 +37,7 @@ const Badge = ({ color = "red", content, offset: offset$1 = 0, children }) => {
35
37
  ...children.props,
36
38
  ref: mergedRefs
37
39
  }),
38
- context.open && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(
40
+ context.open && /* @__PURE__ */ jsx(FloatingPortal, { ...portalRoot && { root: portalRoot }, children: /* @__PURE__ */ jsx(
39
41
  "div",
40
42
  {
41
43
  style: {
@@ -11,7 +11,11 @@ import { Box } from '@janbox/storefront-ui';
11
11
  ## Props
12
12
 
13
13
  | Prop | Type | Default | Description |
14
- |------|------|---------|-------------|
14
+ | ------ | ------ | ------- | ----------- |
15
+ | `hidden` | `boolean` | - | Ẩn Box (`display: none`) hoặc hiển thị (`display: block`). Hỗ trợ responsive qua `sm`, `md`, `lg`. |
16
+ | `sm` | `BoxResponsiveProps` | - | Override responsive props tại breakpoint `sm` (768px). |
17
+ | `md` | `BoxResponsiveProps` | - | Override responsive props tại breakpoint `md` (1280px). |
18
+ | `lg` | `BoxResponsiveProps` | - | Override responsive props tại breakpoint `lg` (1680px). |
15
19
  | `sx` | `ToResponsiveProps<StyledCSS>` | - | Responsive Emotion CSS overrides. Accepts a base StyledCSS object plus optional `sm`, `md`, and `lg` breakpoint keys for responsive styles. |
16
20
  | `as` | `HTMLElementType` | "div" | Override the rendered HTML element (polymorphic). Defaults to `div`. |
17
21
  | `children` | `React.ReactNode` | - | Content rendered inside the Box. |
@@ -44,6 +48,15 @@ import { Box } from '@janbox/storefront-ui';
44
48
  </Box>
45
49
  ```
46
50
 
51
+ ### Responsive hidden
52
+
53
+ ```tsx
54
+ {/* Ẩn trên mobile, hiện từ md trở lên */}
55
+ <Box hidden md={{ hidden: false }}>
56
+ Desktop only content
57
+ </Box>
58
+ ```
59
+
47
60
  ## Related Components
48
61
 
49
62
  - [Primitive](../primitive/README.md)
@@ -2,22 +2,26 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { getBoxProps } from "./helpers.js";
3
3
  import { Primitive } from "../primitive/primitive.js";
4
4
  const Box = ({ children, ref, ..._props }) => {
5
- const { sx = {}, ...rest } = getBoxProps(_props);
5
+ const { hidden, sm, md, lg, sx = {}, ...rest } = getBoxProps(_props);
6
6
  return /* @__PURE__ */ jsx(
7
7
  Primitive,
8
8
  {
9
9
  ...rest,
10
10
  sx: {
11
+ ...createStyle({ hidden }),
11
12
  ...sx,
12
- sm: { ...sx.sm },
13
- md: { ...sx.md },
14
- lg: { ...sx.lg }
13
+ sm: { ...createStyle(sm), ...sx.sm },
14
+ md: { ...createStyle(md), ...sx.md },
15
+ lg: { ...createStyle(lg), ...sx.lg }
15
16
  },
16
17
  ref,
17
18
  children
18
19
  }
19
20
  );
20
21
  };
22
+ const createStyle = ({ hidden } = {}) => ({
23
+ display: hidden ? "none" : "block"
24
+ });
21
25
  export {
22
26
  Box
23
27
  };
@@ -1,3 +1,9 @@
1
1
  import { JSX } from 'react';
2
2
  import { PrimitiveProps } from '../primitive';
3
- export type BoxProps = PrimitiveProps<JSX.IntrinsicElements['div']>;
3
+ import { ShallowMerge, ToResponsiveProps } from '../../types';
4
+ export interface BoxResponsiveProps {
5
+ hidden?: boolean;
6
+ }
7
+ type BoxExtendedProps = ToResponsiveProps<BoxResponsiveProps>;
8
+ export type BoxProps = PrimitiveProps<ShallowMerge<JSX.IntrinsicElements['div'], BoxExtendedProps>, 'div'>;
9
+ export {};
@@ -11,7 +11,11 @@ import { Container } from '@janbox/storefront-ui';
11
11
  ## Props
12
12
 
13
13
  | Prop | Type | Default | Description |
14
- |------|------|---------|-------------|
14
+ | ------ | ------ | ------- | ----------- |
15
+ | `hidden` | `boolean` | - | Ẩn Container (`display: none`) hoặc hiển thị (`display: block`). Hỗ trợ responsive qua `sm`, `md`, `lg`. |
16
+ | `sm` | `ContainerResponsiveProps` | - | Override responsive props tại breakpoint `sm` (768px). |
17
+ | `md` | `ContainerResponsiveProps` | - | Override responsive props tại breakpoint `md` (1280px). |
18
+ | `lg` | `ContainerResponsiveProps` | - | Override responsive props tại breakpoint `lg` (1680px). |
15
19
  | `sx` | `ToResponsiveProps<StyledCSS>` | - | Custom Emotion CSS. Hỗ trợ responsive keys (`sm`, `md`, `lg`). **Không nên override** `width`, `max-width`, `margin-left`, `margin-right`, `padding-left`, `padding-right` — các thuộc tính này do Container quản lý nội bộ. |
16
20
  | `children` | `React.ReactNode` | - | Nội dung render bên trong container. |
17
21
  | `...rest` | `JSX.IntrinsicElements['div']` | - | Tất cả HTML div attributes tiêu chuẩn (`className`, `style`, `onClick`, v.v.). |
@@ -34,6 +38,15 @@ import { Container } from '@janbox/storefront-ui';
34
38
  </Container>
35
39
  ```
36
40
 
41
+ ### Responsive hidden
42
+
43
+ ```tsx
44
+ {/* Ẩn trên mobile, hiện từ md trở lên */}
45
+ <Container hidden md={{ hidden: false }}>
46
+ Desktop only section
47
+ </Container>
48
+ ```
49
+
37
50
  > Không dùng `sx` để override layout core của Container như `width`, `max-width`, `margin-left`, `margin-right`, `padding-left`, `padding-right`. Nếu cần layout khác, dùng wrapper component riêng thay vì thay đổi contract của Container.
38
51
 
39
52
  ## Dependencies
@@ -2,9 +2,26 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { getContainerProps } from "./helpers.js";
3
3
  import { Primitive } from "../primitive/primitive.js";
4
4
  const Container = ({ children, ref, ..._props }) => {
5
- const props = getContainerProps(_props);
6
- return /* @__PURE__ */ jsx(Primitive, { ...props, ref, children });
5
+ const { hidden, sm, md, lg, sx = {}, ...rest } = getContainerProps(_props);
6
+ return /* @__PURE__ */ jsx(
7
+ Primitive,
8
+ {
9
+ ...rest,
10
+ sx: {
11
+ ...createStyle({ hidden }),
12
+ ...sx,
13
+ sm: { ...createStyle(sm), ...sx.sm },
14
+ md: { ...createStyle(md), ...sx.md },
15
+ lg: { ...createStyle(lg), ...sx.lg }
16
+ },
17
+ ref,
18
+ children
19
+ }
20
+ );
7
21
  };
22
+ const createStyle = ({ hidden } = {}) => ({
23
+ display: hidden ? "none" : "block"
24
+ });
8
25
  export {
9
26
  Container
10
27
  };
@@ -1,3 +1,9 @@
1
1
  import { JSX } from 'react';
2
2
  import { PrimitiveProps } from '../primitive';
3
- export type ContainerProps = PrimitiveProps<JSX.IntrinsicElements['div'], 'div'>;
3
+ import { ShallowMerge, ToResponsiveProps } from '../../types';
4
+ export interface ContainerResponsiveProps {
5
+ hidden?: boolean;
6
+ }
7
+ type ContainerExtendedProps = ToResponsiveProps<ContainerResponsiveProps>;
8
+ export type ContainerProps = PrimitiveProps<ShallowMerge<JSX.IntrinsicElements['div'], ContainerExtendedProps>, 'div'>;
9
+ export {};
@@ -0,0 +1,53 @@
1
+ # FlexItem
2
+
3
+ A flex child component intended to be used inside Flexbox. Constrains content to `max-width: 100%` by default and supports `hidden` for responsive visibility control.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { FlexItem } from '@janbox/storefront-ui';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ | ------ | ------ | ------- | ----------- |
15
+ | `hidden` | `boolean` | - | Ẩn FlexItem (`display: none`) hoặc hiển thị (`display: block`). Hỗ trợ responsive qua `sm`, `md`, `lg`. |
16
+ | `sm` | `FlexItemResponsiveProps` | - | Responsive overrides applied at the sm breakpoint (≥768px). Accepts `{ hidden?: boolean }`. |
17
+ | `md` | `FlexItemResponsiveProps` | - | Responsive overrides applied at the md breakpoint (≥1280px). Accepts `{ hidden?: boolean }`. |
18
+ | `lg` | `FlexItemResponsiveProps` | - | Responsive overrides applied at the lg breakpoint (≥1680px). Accepts `{ hidden?: boolean }`. |
19
+ | `sx` | `ToResponsiveProps<StyledCSS>` | - | Custom Emotion CSS. Hỗ trợ responsive keys (`sm`, `md`, `lg`). |
20
+ | `children` | `React.ReactNode` | - | Nội dung render bên trong flex item. |
21
+ | `ref` | `React.Ref<HTMLDivElement>` | - | Forwarded ref to the underlying div element. |
22
+ | `...rest` | `JSX.IntrinsicElements['div']` | - | Tất cả HTML div attributes tiêu chuẩn. |
23
+
24
+ ## Examples
25
+
26
+ ### Basic FlexItem
27
+
28
+ ```tsx
29
+ <Flexbox sx={{ gap: 8 }}>
30
+ <FlexItem>Item 1</FlexItem>
31
+ <FlexItem>Item 2</FlexItem>
32
+ </Flexbox>
33
+ ```
34
+
35
+ ### Responsive hidden
36
+
37
+ ```tsx
38
+ {/* Ẩn trên mobile, hiện từ md trở lên */}
39
+ <Flexbox sx={{ gap: 8 }}>
40
+ <FlexItem>Always visible</FlexItem>
41
+ <FlexItem hidden md={{ hidden: false }}>Desktop only</FlexItem>
42
+ </Flexbox>
43
+ ```
44
+
45
+ ## Related Components
46
+
47
+ - [Flexbox](../flexbox/README.md)
48
+ - [Primitive](../../primitive/README.md)
49
+
50
+ ## See Also
51
+
52
+ - [Component Source](./flex-item.tsx)
53
+ - [Main README](../../../README.md)
@@ -2,13 +2,17 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { getFlexItemProps } from "./helpers.js";
3
3
  import { Primitive } from "../../primitive/primitive.js";
4
4
  const FlexItem = ({ children, ref, ...rest }) => {
5
- const { sx, ...props } = getFlexItemProps(rest);
5
+ const { hidden, sm, md, lg, sx, ...props } = getFlexItemProps(rest);
6
6
  return /* @__PURE__ */ jsx(
7
7
  Primitive,
8
8
  {
9
9
  sx: {
10
10
  maxWidth: "100%",
11
- ...sx
11
+ ...createStyle({ hidden }),
12
+ ...sx,
13
+ sm: { ...createStyle(sm), ...sx == null ? void 0 : sx.sm },
14
+ md: { ...createStyle(md), ...sx == null ? void 0 : sx.md },
15
+ lg: { ...createStyle(lg), ...sx == null ? void 0 : sx.lg }
12
16
  },
13
17
  ...props,
14
18
  ref,
@@ -16,6 +20,9 @@ const FlexItem = ({ children, ref, ...rest }) => {
16
20
  }
17
21
  );
18
22
  };
23
+ const createStyle = ({ hidden } = {}) => ({
24
+ display: hidden ? "none" : "block"
25
+ });
19
26
  export {
20
27
  FlexItem
21
28
  };
@@ -1,3 +1,9 @@
1
1
  import { JSX } from 'react';
2
2
  import { PrimitiveProps } from '../../primitive';
3
- export type FlexItemProps = PrimitiveProps<JSX.IntrinsicElements['div'], 'div'>;
3
+ import { ShallowMerge, ToResponsiveProps } from '../../../types';
4
+ export interface FlexItemResponsiveProps {
5
+ hidden?: boolean;
6
+ }
7
+ type FlexItemExtendedProps = ToResponsiveProps<FlexItemResponsiveProps>;
8
+ export type FlexItemProps = PrimitiveProps<ShallowMerge<JSX.IntrinsicElements['div'], FlexItemExtendedProps>, 'div'>;
9
+ export {};
@@ -11,7 +11,11 @@ import { Flexbox } from '@janbox/storefront-ui';
11
11
  ## Props
12
12
 
13
13
  | Prop | Type | Default | Description |
14
- |------|------|---------|-------------|
14
+ | ------ | ------ | ------- | ----------- |
15
+ | `hidden` | `boolean` | - | Ẩn Flexbox (`display: none`) hoặc hiển thị (`display: flex`). Hỗ trợ responsive qua `sm`, `md`, `lg`. |
16
+ | `sm` | `FlexboxResponsiveProps` | - | Responsive overrides applied at the sm breakpoint (≥768px). Accepts `{ hidden?: boolean }`. |
17
+ | `md` | `FlexboxResponsiveProps` | - | Responsive overrides applied at the md breakpoint (≥1280px). Accepts `{ hidden?: boolean }`. |
18
+ | `lg` | `FlexboxResponsiveProps` | - | Responsive overrides applied at the lg breakpoint (≥1680px). Accepts `{ hidden?: boolean }`. |
15
19
  | `sx` | `ToResponsiveProps<StyledCSS>` | { flexDirection: 'row', alignItems: 'flex-start', justifyContent: 'flex-start' } | Responsive style overrides via the Emotion-powered sx prop. Supports base styles plus `sm`, `md`, and `lg` breakpoint keys. Merged on top of the component defaults (`flexDirection: 'row'`, `alignItems: 'flex-start'`, `justifyContent: 'flex-start'`). |
16
20
  | `children` | `React.ReactNode` | - | Child elements rendered inside the flex container — typically `FlexItem` components. |
17
21
  | `ref` | `React.Ref<HTMLDivElement>` | - | Forwarded ref to the underlying `div` element. |
@@ -47,12 +51,22 @@ import { Flexbox } from '@janbox/storefront-ui';
47
51
  </Flexbox>
48
52
  ```
49
53
 
54
+ ### Responsive hidden
55
+
56
+ ```tsx
57
+ {/* Ẩn trên mobile, hiện từ md trở lên */}
58
+ <Flexbox hidden md={{ hidden: false }} sx={{ gap: 16 }}>
59
+ <FlexItem>Desktop only</FlexItem>
60
+ </Flexbox>
61
+ ```
62
+
50
63
  ## Related Components
51
64
 
52
- - [Primitive](../primitive/README.md)
65
+ - [FlexItem](../flex-item/README.md)
66
+ - [Primitive](../../primitive/README.md)
53
67
 
54
68
  ## See Also
55
69
 
56
70
  - [Component Source](./index.ts)
57
- - [Storybook Stories](./flexbox.stories.tsx)
58
- - [Main README](../../README.md)
71
+ - [Storybook Stories](../flexbox.stories.tsx)
72
+ - [Main README](../../../README.md)
@@ -2,23 +2,27 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { getFlexboxProps } from "./helpers.js";
3
3
  import { Primitive } from "../../primitive/primitive.js";
4
4
  const Flexbox = ({ children, ref, ..._props }) => {
5
- const { sx = {}, ...rest } = getFlexboxProps(_props);
5
+ var _a, _b, _c;
6
+ const { hidden, sm, md, lg, ...rest } = getFlexboxProps(_props);
6
7
  return /* @__PURE__ */ jsx(
7
8
  Primitive,
8
9
  {
9
10
  ...rest,
10
11
  sx: {
11
- display: "flex",
12
- ...sx,
13
- sm: { ...sx.sm },
14
- md: { ...sx.md },
15
- lg: { ...sx.lg }
12
+ ...createStyle({ hidden }),
13
+ ...rest.sx,
14
+ sm: { ...createStyle(sm), ...(_a = rest.sx) == null ? void 0 : _a.sm },
15
+ md: { ...createStyle(md), ...(_b = rest.sx) == null ? void 0 : _b.md },
16
+ lg: { ...createStyle(lg), ...(_c = rest.sx) == null ? void 0 : _c.lg }
16
17
  },
17
18
  ref,
18
19
  children
19
20
  }
20
21
  );
21
22
  };
23
+ const createStyle = ({ hidden } = {}) => ({
24
+ display: hidden ? "none" : "flex"
25
+ });
22
26
  export {
23
27
  Flexbox
24
28
  };
@@ -1,3 +1,9 @@
1
1
  import { JSX } from 'react';
2
2
  import { PrimitiveProps } from '../../primitive';
3
- export type FlexboxProps = PrimitiveProps<JSX.IntrinsicElements['div'], 'div'>;
3
+ import { ShallowMerge, ToResponsiveProps } from '../../../types';
4
+ export interface FlexboxResponsiveProps {
5
+ hidden?: boolean;
6
+ }
7
+ type FlexboxExtendedProps = ToResponsiveProps<FlexboxResponsiveProps>;
8
+ export type FlexboxProps = PrimitiveProps<ShallowMerge<JSX.IntrinsicElements['div'], FlexboxExtendedProps>, 'div'>;
9
+ export {};
@@ -5,6 +5,7 @@ import { useFloatingContext } from "../floating/helpers.js";
5
5
  import { FLOATING_Z_INDEX } from "../../../theme/index.js";
6
6
  import { useIsHydrated } from "../../../hooks/use-is-hydrated/index.js";
7
7
  import { createSxInterpolation } from "../../../utils/stylesheet.js";
8
+ import { usePortalContainer } from "../../../theme/provider.js";
8
9
  const FloatingContent = ({
9
10
  style,
10
11
  floatingPortalProps,
@@ -17,7 +18,10 @@ const FloatingContent = ({
17
18
  const { getFloatingProps, context } = useFloatingContext();
18
19
  const mergedRefs = useMergeRefs([context.refs.setFloating, ref]);
19
20
  const isHydrated = useIsHydrated();
20
- return /* @__PURE__ */ jsx(AnimatePresence, { children: context.open && isHydrated && /* @__PURE__ */ jsx(FloatingPortal, { root: document.body, ...floatingPortalProps, children: /* @__PURE__ */ jsx(
21
+ const portalRoot = usePortalContainer();
22
+ const { root = portalRoot, ...portalProps } = floatingPortalProps ?? {};
23
+ const resolvedRoot = root || void 0;
24
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: context.open && isHydrated && /* @__PURE__ */ jsx(FloatingPortal, { root: resolvedRoot, ...portalProps, children: /* @__PURE__ */ jsx(
21
25
  FloatingOverlay,
22
26
  {
23
27
  ...floatingOverlayProps,
@@ -11,11 +11,12 @@ import { Cell } from '@janbox/storefront-ui';
11
11
  ## Props
12
12
 
13
13
  | Prop | Type | Default | Description |
14
- |------|------|---------|-------------|
14
+ | ------ | ------ | ------- | ----------- |
15
15
  | `colSpan` | `number` | - | Number of grid columns the cell should span. Generates `grid-column: span colSpan`. Supports responsive variants via sm/md/lg. |
16
- | `sm` | `CellResponsiveProps` | - | Responsive overrides applied at the sm breakpoint (≥768px). Accepts `{ colSpan?: number }`. |
17
- | `md` | `CellResponsiveProps` | - | Responsive overrides applied at the md breakpoint (≥1280px). Accepts `{ colSpan?: number }`. |
18
- | `lg` | `CellResponsiveProps` | - | Responsive overrides applied at the lg breakpoint (≥1680px). Accepts `{ colSpan?: number }`. |
16
+ | `hidden` | `boolean` | - | Ẩn Cell (`display: none`) hoặc hiển thị (`display: block`). Hỗ trợ responsive qua `sm`, `md`, `lg`. |
17
+ | `sm` | `CellResponsiveProps` | - | Responsive overrides applied at the sm breakpoint (≥768px). Accepts `{ colSpan?: number, hidden?: boolean }`. |
18
+ | `md` | `CellResponsiveProps` | - | Responsive overrides applied at the md breakpoint (≥1280px). Accepts `{ colSpan?: number, hidden?: boolean }`. |
19
+ | `lg` | `CellResponsiveProps` | - | Responsive overrides applied at the lg breakpoint (≥1680px). Accepts `{ colSpan?: number, hidden?: boolean }`. |
19
20
  | `sx` | `ToResponsiveProps<StyledCSS>` | - | Escape-hatch for inline Emotion styles. Supports responsive keys (sm, md, lg) alongside base styles. |
20
21
  | `children` | `React.ReactNode` | - | Cell content. |
21
22
  | `ref` | `React.Ref<HTMLDivElement>` | - | Forwarded ref to the underlying div element. |
@@ -50,6 +51,18 @@ import { Cell } from '@janbox/storefront-ui';
50
51
  </Cell>
51
52
  ```
52
53
 
54
+ ### Responsive hidden
55
+
56
+ ```tsx
57
+ {/* Ẩn trên mobile, hiện từ sm trở lên */}
58
+ <Grid cols={3}>
59
+ <Cell hidden sm={{ hidden: false }}>
60
+ Desktop only cell
61
+ </Cell>
62
+ <Cell>Always visible</Cell>
63
+ </Grid>
64
+ ```
65
+
53
66
  ## Related Components
54
67
 
55
68
  - [Grid](../grid/README.md)
@@ -4,13 +4,13 @@ import { getCellProps } from "./helpers.js";
4
4
  import { Primitive } from "../../primitive/primitive.js";
5
5
  const Cell = ({ children, ref, ..._props }) => {
6
6
  var _a, _b, _c;
7
- const { colSpan, sm, md, lg, ...rest } = getCellProps(_props);
7
+ const { colSpan, hidden, sm, md, lg, ...rest } = getCellProps(_props);
8
8
  return /* @__PURE__ */ jsx(
9
9
  Primitive,
10
10
  {
11
11
  ...rest,
12
12
  sx: {
13
- ...createStyle({ colSpan }),
13
+ ...createStyle({ colSpan, hidden }),
14
14
  ...rest.sx,
15
15
  sm: {
16
16
  ...createStyle(sm),
@@ -30,8 +30,9 @@ const Cell = ({ children, ref, ..._props }) => {
30
30
  }
31
31
  );
32
32
  };
33
- const createStyle = ({ colSpan } = {}) => {
33
+ const createStyle = ({ colSpan, hidden } = {}) => {
34
34
  return {
35
+ display: hidden ? "none" : "block",
35
36
  gridColumn: isNumber(colSpan) ? `span ${colSpan}` : colSpan
36
37
  };
37
38
  };
@@ -3,5 +3,6 @@ import { ToResponsiveProps } from '../../../types';
3
3
  import { PrimitiveProps } from '../../primitive';
4
4
  export interface CellResponsiveProps {
5
5
  colSpan?: number;
6
+ hidden?: boolean;
6
7
  }
7
8
  export type CellProps = PrimitiveProps<JSX.IntrinsicElements['div'] & ToResponsiveProps<CellResponsiveProps>>;
@@ -11,11 +11,12 @@ import { Grid } from '@janbox/storefront-ui';
11
11
  ## Props
12
12
 
13
13
  | Prop | Type | Default | Description |
14
- |------|------|---------|-------------|
14
+ | ------ | ------ | ------- | ----------- |
15
15
  | `cols` | `number` | 1 | Number of equal-width columns in the grid. Generates `grid-template-columns: repeat(cols, minmax(0, 1fr))`. Supports responsive variants via sm/md/lg. |
16
- | `sm` | `GridResponsiveProps` | - | Responsive overrides applied at the sm breakpoint (≥768px). Accepts `{ cols?: number }`. |
17
- | `md` | `GridResponsiveProps` | - | Responsive overrides applied at the md breakpoint (≥1280px). Accepts `{ cols?: number }`. |
18
- | `lg` | `GridResponsiveProps` | - | Responsive overrides applied at the lg breakpoint (≥1680px). Accepts `{ cols?: number }`. |
16
+ | `hidden` | `boolean` | - | Ẩn Grid (`display: none`) hoặc hiển thị (`display: grid`). Hỗ trợ responsive qua `sm`, `md`, `lg`. |
17
+ | `sm` | `GridResponsiveProps` | - | Responsive overrides applied at the sm breakpoint (≥768px). Accepts `{ cols?: number, hidden?: boolean }`. |
18
+ | `md` | `GridResponsiveProps` | - | Responsive overrides applied at the md breakpoint (≥1280px). Accepts `{ cols?: number, hidden?: boolean }`. |
19
+ | `lg` | `GridResponsiveProps` | - | Responsive overrides applied at the lg breakpoint (≥1680px). Accepts `{ cols?: number, hidden?: boolean }`. |
19
20
  | `sx` | `ToResponsiveProps<StyledCSS>` | - | Escape-hatch for inline Emotion styles. Supports responsive keys (sm, md, lg) alongside base styles. |
20
21
  | `children` | `React.ReactNode` | - | Grid children, typically Cell components. |
21
22
  | `ref` | `React.Ref<HTMLDivElement>` | - | Forwarded ref to the underlying div element. |
@@ -55,6 +56,17 @@ import { Grid } from '@janbox/storefront-ui';
55
56
  </Grid>
56
57
  ```
57
58
 
59
+ ### Responsive hidden
60
+
61
+ ```tsx
62
+ {/* Ẩn trên mobile, hiện từ md trở lên */}
63
+ <Grid cols={3} hidden md={{ hidden: false }}>
64
+ <Cell>Item 1</Cell>
65
+ <Cell>Item 2</Cell>
66
+ <Cell>Item 3</Cell>
67
+ </Grid>
68
+ ```
69
+
58
70
  ## Related Components
59
71
 
60
72
  - [Cell](../cell/README.md)
@@ -5,25 +5,26 @@ import { Primitive } from "../../primitive/primitive.js";
5
5
  const GRID_MINIMUM_COLS = 1;
6
6
  const GRID_MAXIMUM_COLS = 24;
7
7
  const Grid = ({ children, ref, ..._props }) => {
8
- const { cols, sm, md, lg, sx = {}, ...rest } = getGridProps(_props);
8
+ var _a, _b, _c;
9
+ const { cols, hidden, sm, md, lg, ...rest } = getGridProps(_props);
9
10
  return /* @__PURE__ */ jsx(
10
11
  Primitive,
11
12
  {
13
+ ...rest,
12
14
  sx: {
13
- display: "grid",
14
- ...createStyle({ cols }),
15
- ...sx,
16
- sm: { ...createStyle(sm), ...sx.sm },
17
- md: { ...createStyle(md), ...sx.md },
18
- lg: { ...createStyle(lg), ...sx.lg }
15
+ ...createStyle({ cols, hidden }),
16
+ ...rest.sx,
17
+ sm: { ...createStyle(sm), ...(_a = rest.sx) == null ? void 0 : _a.sm },
18
+ md: { ...createStyle(md), ...(_b = rest.sx) == null ? void 0 : _b.md },
19
+ lg: { ...createStyle(lg), ...(_c = rest.sx) == null ? void 0 : _c.lg }
19
20
  },
20
- ...rest,
21
21
  ref,
22
22
  children
23
23
  }
24
24
  );
25
25
  };
26
- const createStyle = ({ cols } = {}) => ({
26
+ const createStyle = ({ cols, hidden } = {}) => ({
27
+ display: hidden ? "none" : "grid",
27
28
  gridTemplateColumns: isNil(cols) ? void 0 : `repeat(${cols}, minmax(0, 1fr))`
28
29
  });
29
30
  export {
@@ -3,6 +3,7 @@ import { ShallowMerge, ToResponsiveProps } from '../../../types';
3
3
  import { PrimitiveProps } from '../../primitive';
4
4
  export interface GridResponsiveProps {
5
5
  cols?: number;
6
+ hidden?: boolean;
6
7
  }
7
8
  type GridExtendedProps = ToResponsiveProps<GridResponsiveProps>;
8
9
  export type GridProps = PrimitiveProps<ShallowMerge<JSX.IntrinsicElements['div'], GridExtendedProps>>;
@@ -7,6 +7,7 @@ import { notificationStore } from "./helpers.js";
7
7
  import { getColorShadesByVariant } from "../../utils/index.js";
8
8
  import { FLOATING_Z_INDEX } from "../../theme/index.js";
9
9
  import CloseIcon from "../../assets/svg/times-close-delete.svg.js";
10
+ import { usePortalContainer } from "../../theme/provider.js";
10
11
  import { Primitive } from "../primitive/primitive.js";
11
12
  import { getTypographyVar } from "../../theme/typography.js";
12
13
  import { IconButton } from "../icon-button/icon-button.js";
@@ -17,7 +18,8 @@ const Notifications = () => {
17
18
  notificationStore.getSnapshot,
18
19
  notificationStore.getServerSnapshot
19
20
  );
20
- return /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(
21
+ const portalRoot = usePortalContainer();
22
+ return /* @__PURE__ */ jsx(FloatingPortal, { ...portalRoot && { root: portalRoot }, children: /* @__PURE__ */ jsx(
21
23
  Primitive,
22
24
  {
23
25
  sx: {
@@ -4,6 +4,7 @@ import { useFloating, autoUpdate, shift, flip, size, FloatingPortal } from "@flo
4
4
  import { times, isNumber } from "lodash-es";
5
5
  import { motion } from "motion/react";
6
6
  import { getColorVar } from "../../../theme/palette.js";
7
+ import { usePortalContainer } from "../../../theme/provider.js";
7
8
  import { useIsHydrated } from "../../../hooks/use-is-hydrated/index.js";
8
9
  import { Primitive } from "../../primitive/primitive.js";
9
10
  const SWIPER_DOTS_SIZE_VAR = "--swiper-dots-size";
@@ -22,6 +23,7 @@ const SwiperDotsPaging = ({
22
23
  const [inViewIndexes, setInViewIndexes] = useState((api == null ? void 0 : api.slidesInView()) ?? []);
23
24
  const [totalSlides, setTotalSlides] = useState((api == null ? void 0 : api.slideNodes().length) ?? 0);
24
25
  const isHydrated = useIsHydrated();
26
+ const portalRoot = usePortalContainer();
25
27
  const {
26
28
  floatingStyles,
27
29
  refs: { setFloating, setReference }
@@ -81,7 +83,7 @@ const SwiperDotsPaging = ({
81
83
  }
82
84
  }
83
85
  ),
84
- /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(
86
+ /* @__PURE__ */ jsx(FloatingPortal, { ...portalRoot && { root: portalRoot }, children: /* @__PURE__ */ jsx(
85
87
  Primitive,
86
88
  {
87
89
  style: {
@@ -3,6 +3,7 @@ import { useState, useRef, useCallback, useEffect } from "react";
3
3
  import { useFloating, autoUpdate, offset, size, FloatingPortal } from "@floating-ui/react";
4
4
  import { isEmpty, isNumber, isNil } from "lodash-es";
5
5
  import { getColorVar } from "../../../theme/palette.js";
6
+ import { usePortalContainer } from "../../../theme/provider.js";
6
7
  import ArrowLeftIcon from "../../../assets/svg/arrow-left.svg.js";
7
8
  import { SvgLoader } from "../../../components/svg-loader.js";
8
9
  import { Primitive } from "../../primitive/primitive.js";
@@ -23,6 +24,7 @@ const SwiperSideArrowsPaging = ({
23
24
  const [canScrollNext, setCanScrollNext] = useState(api.canScrollNext());
24
25
  const [isVisible, setIsVisible] = useState(visibility === "always");
25
26
  const [referenceStyle, setReferenceStyle] = useState(anchorSelector ? {} : { top: 0, bottom: 0 });
27
+ const portalRoot = usePortalContainer();
26
28
  const hoverStateRef = useRef({ root: false, floating: false });
27
29
  const updateVisibility = useCallback(() => {
28
30
  const { root, floating } = hoverStateRef.current;
@@ -136,7 +138,7 @@ const SwiperSideArrowsPaging = ({
136
138
  }
137
139
  }
138
140
  ),
139
- context.open && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsx(
141
+ context.open && /* @__PURE__ */ jsx(FloatingPortal, { ...portalRoot && { root: portalRoot }, children: /* @__PURE__ */ jsx(
140
142
  Primitive,
141
143
  {
142
144
  style: {
@@ -1,8 +1,8 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { useRef, useState, useEffect } from "react";
3
3
  import { motion } from "motion/react";
4
- import { Stylesheet } from "../../stylesheet/stylesheet.js";
5
4
  import { useTabsContext } from "../tabs/helpers.js";
5
+ import { Stylesheet } from "../../stylesheet/stylesheet.js";
6
6
  const TabIndicator = () => {
7
7
  const { activeIndex, tabIndicatorProps } = useTabsContext();
8
8
  const indicatorEl = useRef(null);
@@ -5,6 +5,7 @@ import { isUndefined, get } from "lodash-es";
5
5
  import { useControllableState } from "../../hooks/use-controllable-state.js";
6
6
  import { getColorVar } from "../../theme/palette.js";
7
7
  import { getTypographyVar } from "../../theme/typography.js";
8
+ import { usePortalContainer } from "../../theme/provider.js";
8
9
  import { Primitive } from "../primitive/primitive.js";
9
10
  function Tooltip({
10
11
  children,
@@ -22,6 +23,7 @@ function Tooltip({
22
23
  defaultValue: defaultOpen,
23
24
  value: open
24
25
  });
26
+ const portalRoot = usePortalContainer();
25
27
  const triggerRef = useRef(null);
26
28
  const arrowRef = useRef(null);
27
29
  const handleOpenChange = (val, event, reason) => {
@@ -78,7 +80,7 @@ function Tooltip({
78
80
  ref: mergedRefs
79
81
  })
80
82
  ),
81
- context.open && /* @__PURE__ */ jsx(FloatingPortal, { children: /* @__PURE__ */ jsxs(
83
+ context.open && /* @__PURE__ */ jsx(FloatingPortal, { ...portalRoot && { root: portalRoot }, children: /* @__PURE__ */ jsxs(
82
84
  Primitive,
83
85
  {
84
86
  style: {
@@ -1,7 +1,7 @@
1
1
  import { breakpoint, containerQuery, getSmallerScreens, mediaQuery, screens } from "./screens.js";
2
2
  import { getColorVar, groupedColors, paletteVars, parseColor } from "./palette.js";
3
3
  import { getSmallerTypographyVariants, getTypographyVar, typographyVars } from "./typography.js";
4
- import { ThemeProvider, useTheme } from "./provider.js";
4
+ import { ThemeProvider, usePortalContainer, useTheme } from "./provider.js";
5
5
  import { createTheme, defaultTheme } from "./create-theme.js";
6
6
  const FLOATING_Z_INDEX = 9999;
7
7
  export {
@@ -21,5 +21,6 @@ export {
21
21
  parseColor,
22
22
  screens,
23
23
  typographyVars,
24
+ usePortalContainer,
24
25
  useTheme
25
26
  };
@@ -1,8 +1,13 @@
1
- import { PropsWithChildren } from 'react';
1
+ import { FloatingPortal } from '@floating-ui/react';
2
+ import { ComponentProps, PropsWithChildren } from 'react';
2
3
  import { Theme } from '.';
4
+ type PortalContainer = ComponentProps<typeof FloatingPortal>['root'];
3
5
  export type ThemeProviderProps = PropsWithChildren<{
4
6
  theme: Theme;
5
7
  locale?: string;
8
+ root?: PortalContainer;
6
9
  }>;
10
+ export declare const usePortalContainer: () => HTMLElement | ShadowRoot | import('react').MutableRefObject<HTMLElement | ShadowRoot | null> | null | undefined;
7
11
  export declare const useTheme: () => Theme;
8
- export declare const ThemeProvider: ({ theme, children, locale }: ThemeProviderProps) => import("react/jsx-runtime").JSX.Element;
12
+ export declare const ThemeProvider: ({ theme, children, locale, root }: ThemeProviderProps) => import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Global } from "@emotion/react";
3
3
  import { useContext, createContext } from "react";
4
4
  import { getColorVar } from "./palette.js";
@@ -6,6 +6,9 @@ import { flatten } from "flat";
6
6
  import { flatMap } from "lodash-es";
7
7
  import { getTypographyVar } from "./typography.js";
8
8
  import { LocaleContext, FALLBACK_LOCALE } from "../i18n/index.js";
9
+ const usePortalContainer = () => {
10
+ return useContext(PortalContainerContext);
11
+ };
9
12
  const useTheme = () => {
10
13
  const context = useContext(ThemeContext);
11
14
  if (!context) {
@@ -14,11 +17,12 @@ const useTheme = () => {
14
17
  return context;
15
18
  };
16
19
  const ThemeContext = createContext(null);
17
- const ThemeProvider = ({ theme, children, locale = FALLBACK_LOCALE }) => {
18
- return /* @__PURE__ */ jsxs(ThemeContext.Provider, { value: theme, children: [
20
+ const PortalContainerContext = createContext(null);
21
+ const ThemeProvider = ({ theme, children, locale = FALLBACK_LOCALE, root }) => {
22
+ return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: theme, children: /* @__PURE__ */ jsxs(PortalContainerContext.Provider, { value: root ?? null, children: [
19
23
  /* @__PURE__ */ jsx(Global, { styles: createGlobalStyle(theme) }),
20
24
  /* @__PURE__ */ jsx(LocaleContext.Provider, { value: locale, children })
21
- ] });
25
+ ] }) });
22
26
  };
23
27
  const createGlobalStyle = ({ palette, typography }) => {
24
28
  const flattenPalette = flatten(palette);
@@ -304,5 +308,6 @@ const createGlobalStyle = ({ palette, typography }) => {
304
308
  };
305
309
  export {
306
310
  ThemeProvider,
311
+ usePortalContainer,
307
312
  useTheme
308
313
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@janbox/storefront-ui",
3
- "version": "4.0.1",
3
+ "version": "4.0.3",
4
4
  "description": "Storefront UI component library for Janbox",
5
5
  "author": "Janbox",
6
6
  "keywords": [