@blockbite/ui 1.0.4 → 1.0.5

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 (48) hide show
  1. package/package.json +2 -2
  2. package/src/AutocompleteDropdown.tsx +109 -0
  3. package/src/Badge.tsx +21 -0
  4. package/src/BitePreview.tsx +88 -0
  5. package/src/Button.tsx +60 -0
  6. package/src/ButtonToggle.tsx +165 -0
  7. package/src/Chapter.tsx +22 -0
  8. package/src/ChapterDivider.tsx +25 -0
  9. package/src/Checkbox.tsx +30 -0
  10. package/src/DisappearingMessage.tsx +41 -0
  11. package/src/DropdownPicker.tsx +72 -0
  12. package/src/EmptyState.tsx +32 -0
  13. package/src/FloatingPanel.tsx +59 -0
  14. package/src/FocalPointControl.tsx +58 -0
  15. package/src/Icon.tsx +17 -0
  16. package/src/LinkPicker.tsx +94 -0
  17. package/src/MediaPicker.tsx +161 -0
  18. package/src/MetricsControl.tsx +179 -0
  19. package/src/Modal.tsx +171 -0
  20. package/src/NewWindowPortal.tsx +68 -0
  21. package/src/Notice.tsx +32 -0
  22. package/src/PasswordInput.tsx +53 -0
  23. package/src/Popover.tsx +68 -0
  24. package/src/RangeSlider.tsx +68 -0
  25. package/src/ResponsiveImage.tsx +42 -0
  26. package/src/ResponsiveVideo.tsx +20 -0
  27. package/src/ScrollList.tsx +24 -0
  28. package/src/SectionList.tsx +166 -0
  29. package/src/SelectControlWrapper.tsx +47 -0
  30. package/src/SingleBlockTypeAppender.tsx +37 -0
  31. package/src/SlideIn.tsx +34 -0
  32. package/src/Spinner.tsx +24 -0
  33. package/src/Tabs.tsx +102 -0
  34. package/src/Tag.tsx +44 -0
  35. package/src/TextControl.tsx +74 -0
  36. package/src/TextControlLabel.tsx +27 -0
  37. package/src/ToggleGroup.tsx +72 -0
  38. package/src/ToggleSwitch.tsx +37 -0
  39. package/src/Wrap.tsx +23 -0
  40. package/src/_dev/App.css +42 -0
  41. package/src/_dev/App.tsx +183 -0
  42. package/src/_dev/assets/react.svg +1 -0
  43. package/src/_dev/index.css +68 -0
  44. package/src/_dev/main.tsx +10 -0
  45. package/src/_dev/vite-env.d.ts +1 -0
  46. package/src/types/ui-fallbacks.d.ts +7 -0
  47. package/src/types.ts +4 -0
  48. package/src/ui.css +66 -0
@@ -0,0 +1,166 @@
1
+ import { Wrap } from "./Wrap";
2
+ import { Button } from "@wordpress/components";
3
+ import { useRef, useState } from "@wordpress/element";
4
+
5
+ import { TextControl } from "./TextControl";
6
+ import classNames from "classnames";
7
+
8
+ import PlusIcon from "@blockbite/icons/dist/Plus";
9
+ import TrashIcon from "@blockbite/icons/dist/Trash";
10
+
11
+ type SectionTypeProps = {
12
+ id: string;
13
+ name: string;
14
+ ref?: any;
15
+ code?: string;
16
+ content?: any;
17
+ };
18
+
19
+ type SectionListProps = {
20
+ sections: SectionTypeProps[];
21
+ setSections: React.Dispatch<React.SetStateAction<any[]>>;
22
+ activeSection: any;
23
+ setActiveSection: any;
24
+ Update: any;
25
+ newSection: any;
26
+ setCode: (code: string) => void;
27
+ addons?: JSX.Element;
28
+ };
29
+
30
+ const SectionList = ({
31
+ sections,
32
+ setSections,
33
+ activeSection,
34
+ setActiveSection,
35
+ Update,
36
+ newSection,
37
+ setCode,
38
+ addons,
39
+ }: SectionListProps) => {
40
+ const [renameSection, setRenameSection] = useState({
41
+ id: "",
42
+ name: "",
43
+ });
44
+ const textFieldRef = useRef<HTMLInputElement | null>(null);
45
+
46
+ const addSection = () => {
47
+ const createNewSection = { ...newSection() };
48
+ setSections([...sections, createNewSection]);
49
+ };
50
+
51
+ const removeSection = () => {
52
+ if (!activeSection || sections.length === 0) return;
53
+
54
+ const newSections = sections.filter(
55
+ (section) => section.id !== activeSection
56
+ );
57
+
58
+ let newActiveSection = null;
59
+
60
+ if (newSections.length > 0) {
61
+ // If we're not deleting the last section
62
+ const currentIndex = sections.findIndex((s) => s.id === activeSection);
63
+ const nextIndex = Math.min(currentIndex, newSections.length - 1);
64
+ newActiveSection = newSections[nextIndex]?.id || newSections[0]?.id;
65
+ } else {
66
+ // If we're deleting the last section, create a new one
67
+ const createNewSection = { ...newSection() };
68
+ newSections.push(createNewSection);
69
+ newActiveSection = createNewSection.id;
70
+ }
71
+
72
+ setSections(newSections);
73
+ setActiveSection(newActiveSection);
74
+ setCode(newSections.find((s) => s.id === newActiveSection)?.code || "");
75
+ };
76
+
77
+ const onBlurHandler = (value) => {
78
+ setSections((prevSections: SectionTypeProps[]) =>
79
+ prevSections.map(
80
+ (section: SectionTypeProps): SectionTypeProps =>
81
+ section.id === renameSection.id
82
+ ? { ...section, name: value }
83
+ : section
84
+ )
85
+ );
86
+
87
+ setRenameSection({
88
+ id: "",
89
+ name: "",
90
+ });
91
+ };
92
+
93
+ return (
94
+ <Wrap important className="w-[200px] border-r">
95
+ <ul className="h-[calc(100%-80px)] divide-y overflow-auto">
96
+ {addons}
97
+ <li>
98
+ <ul className="divide-y border-b">
99
+ {sections.map((section, index) => (
100
+ <li
101
+ key={`section__${index}__${section?.id}`}
102
+ className="relative m-0"
103
+ >
104
+ <button
105
+ className={classNames(
106
+ "text-gray-medium w-full truncate bg-opacity-50 px-3 py-2 text-left text-sm/6 font-semibold hover:bg-easy hover:text-wordpress",
107
+ {
108
+ "bg-easy": section.id === activeSection,
109
+ }
110
+ )}
111
+ onClick={() => {
112
+ setActiveSection(section.id);
113
+ Update(activeSection, section.id);
114
+ }}
115
+ onDoubleClick={() => {
116
+ setRenameSection({
117
+ id: section.id,
118
+ name: section.name,
119
+ });
120
+ }}
121
+ >
122
+ {section.name}
123
+ </button>
124
+
125
+ {renameSection.id === section.id && (
126
+ <TextControl
127
+ className="absolute left-0 top-0 h-full w-full"
128
+ defaultValue={renameSection.name}
129
+ onChange={(value) =>
130
+ setRenameSection({
131
+ ...renameSection,
132
+ name: value,
133
+ })
134
+ }
135
+ ref={textFieldRef}
136
+ onBlur={(value) => onBlurHandler(value)}
137
+ />
138
+ )}
139
+ </li>
140
+ ))}
141
+ </ul>
142
+ </li>
143
+ </ul>
144
+ <Wrap className="flex justify-center gap-2 p-2">
145
+ <Button
146
+ onClick={addSection}
147
+ icon={<PlusIcon />}
148
+ variant="tertiary"
149
+ size="compact"
150
+ >
151
+ Add
152
+ </Button>
153
+ <Button
154
+ onClick={removeSection}
155
+ icon={<TrashIcon />}
156
+ variant="tertiary"
157
+ size="compact"
158
+ >
159
+ Remove
160
+ </Button>
161
+ </Wrap>
162
+ </Wrap>
163
+ );
164
+ };
165
+
166
+ export default SectionList;
@@ -0,0 +1,47 @@
1
+ import { SelectControl as WordpressSelect } from '@wordpress/components';
2
+ import { useEffect, useState } from '@wordpress/element';
3
+
4
+ type SelectControlWrapperProps = {
5
+ className?: string;
6
+ defaultValue: string;
7
+ children?: React.ReactNode;
8
+ options: any[];
9
+ label?: string;
10
+ onChange?: (value: string) => void;
11
+ };
12
+
13
+ export const SelectControlWrapper = ({
14
+ onChange,
15
+ className,
16
+ defaultValue,
17
+ options,
18
+ label = '',
19
+ }: SelectControlWrapperProps) => {
20
+ const [value, setValue] = useState('');
21
+
22
+ useEffect(() => {
23
+ setValue(defaultValue);
24
+ // eslint-disable-next-line react-hooks/exhaustive-deps
25
+ }, []);
26
+
27
+ const handleChange = (val: string) => {
28
+ setValue(val);
29
+ if (onChange) {
30
+ onChange(val);
31
+ }
32
+ };
33
+
34
+ return (
35
+ <div className={className}>
36
+ {options && options.length > 0 && (
37
+ <WordpressSelect
38
+ label={label || ''}
39
+ value={value || ''}
40
+ onChange={handleChange}
41
+ options={[{ value: '', label: 'Select an option' }, ...options]}
42
+ />
43
+ )}
44
+ {!options && <p>No options available</p>}
45
+ </div>
46
+ );
47
+ };
@@ -0,0 +1,37 @@
1
+ import { createBlock } from '@wordpress/blocks';
2
+ import { Button } from '@wordpress/components';
3
+
4
+ const { select, dispatch } = wp.data;
5
+
6
+ interface SingleBlockTypeAppenderProps {
7
+ buttonText?: string;
8
+ }
9
+
10
+ const SingleBlockTypeAppender = ({
11
+ buttonText = 'Add block',
12
+ }: SingleBlockTypeAppenderProps) => {
13
+ const addLastDuplicate = () => {
14
+ // Get the current block
15
+ const currentBlock = select('core/block-editor').getSelectedBlock();
16
+ const { clientId, innerBlocks } = currentBlock;
17
+
18
+ // get the last block
19
+ const lastBlock = innerBlocks[innerBlocks.length - 1];
20
+ // duplicate the last block
21
+ const newBlock = createBlock(
22
+ lastBlock.name,
23
+ lastBlock.attributes,
24
+ lastBlock.innerBlocks
25
+ );
26
+
27
+ dispatch('core/block-editor').insertBlocks(newBlock, 0, clientId);
28
+ };
29
+
30
+ return (
31
+ <Button variant="primary" onClick={() => addLastDuplicate()}>
32
+ {buttonText}
33
+ </Button>
34
+ );
35
+ };
36
+
37
+ export default SingleBlockTypeAppender;
@@ -0,0 +1,34 @@
1
+ import { Wrap } from "./Wrap";
2
+ import { useEffect, useState } from "@wordpress/element";
3
+ import classNames from "classnames";
4
+
5
+ type SlideInProps = {
6
+ children: React.ReactNode;
7
+ className?: string;
8
+ watch?: any;
9
+ };
10
+
11
+ export const SlideIn = ({ children, watch, className }: SlideInProps) => {
12
+ const [slide, setSlide] = useState(0);
13
+
14
+ useEffect(() => {
15
+ setTimeout(() => {
16
+ setSlide(1);
17
+ }, 250);
18
+ }, [watch]);
19
+
20
+ return (
21
+ <Wrap
22
+ className={classNames(
23
+ className,
24
+ "duration-50 transform transition-all ease-in-out",
25
+ {
26
+ "translate-x-0": slide === 1,
27
+ "-translate-x-full": slide === 0,
28
+ }
29
+ )}
30
+ >
31
+ {children}
32
+ </Wrap>
33
+ );
34
+ };
@@ -0,0 +1,24 @@
1
+ import { Spinner as WordpressSpinner } from '@wordpress/components';
2
+ import { useEffect, useState } from '@wordpress/element';
3
+ import classNames from 'classnames';
4
+
5
+ type SpinnerProps = {
6
+ label?: string;
7
+ className?: string;
8
+ defaultValue: boolean;
9
+ };
10
+
11
+ export const Spinner = ({ className, defaultValue }: SpinnerProps) => {
12
+ const [isLoading, setIsLoading] = useState(false);
13
+
14
+ useEffect(() => {
15
+ setTimeout(() => {
16
+ setIsLoading(false);
17
+ }, 500);
18
+ if (defaultValue) {
19
+ setIsLoading(true);
20
+ }
21
+ }, [isLoading, defaultValue]);
22
+
23
+ return isLoading && <WordpressSpinner className={classNames(className)} />;
24
+ };
package/src/Tabs.tsx ADDED
@@ -0,0 +1,102 @@
1
+ import { Wrap } from "./Wrap";
2
+ import { TabPanel } from "@wordpress/components";
3
+ import { createContext, useContext, useState } from "@wordpress/element";
4
+ import classNames from "classnames";
5
+
6
+ const TabsContext = createContext<{
7
+ activeTab: string | undefined;
8
+ setActiveTab: React.Dispatch<React.SetStateAction<string | undefined>>;
9
+ } | null>(null);
10
+
11
+ type TabsProps = {
12
+ className?: string;
13
+ value?: string;
14
+ defaultValue?: string;
15
+ onValueChange?: (value: string) => void;
16
+ children?: React.ReactNode;
17
+ };
18
+
19
+ export const TabsWrapper = ({
20
+ children,
21
+ defaultValue,
22
+ value,
23
+ onValueChange,
24
+ ...rest
25
+ }: TabsProps) => {
26
+ const isControlled = value !== undefined;
27
+
28
+ const [internalValue, setInternalValue] = useState(defaultValue || "");
29
+
30
+ const activeTab = isControlled ? value : internalValue;
31
+
32
+ const handleTabChange = (newValue: string) => {
33
+ if (!isControlled) {
34
+ setInternalValue(newValue);
35
+ }
36
+
37
+ if (onValueChange) {
38
+ onValueChange(newValue);
39
+ }
40
+ };
41
+
42
+ const providerValue = {
43
+ activeTab,
44
+ setActiveTab: handleTabChange,
45
+ };
46
+
47
+ return (
48
+ <TabsContext.Provider value={providerValue}>
49
+ <Wrap important className={rest.className}>
50
+ {children}
51
+ </Wrap>
52
+ </TabsContext.Provider>
53
+ );
54
+ };
55
+
56
+ type TabsListProps = {
57
+ options: { name: string; title: string; icon?: any }[];
58
+ children?: React.ReactNode;
59
+ onValueChange?: (value: string) => void;
60
+ className?: string;
61
+ };
62
+
63
+ export const TabsList = ({
64
+ options,
65
+ children,
66
+ className,
67
+ onValueChange,
68
+ }: TabsListProps) => {
69
+ const context = useContext(TabsContext);
70
+ const setActiveTab = onValueChange ? onValueChange : context?.setActiveTab;
71
+
72
+ return (
73
+ <TabPanel
74
+ className={classNames("tabs-list", className)}
75
+ tabs={options}
76
+ onSelect={(tabName) => {
77
+ setActiveTab(tabName);
78
+ }}
79
+ >
80
+ {() => children}
81
+ </TabPanel>
82
+ );
83
+ };
84
+
85
+ type TabsContentProps = {
86
+ value: string;
87
+ children: React.ReactNode;
88
+ className?: string;
89
+ };
90
+
91
+ export const TabsContent = ({
92
+ value,
93
+ children,
94
+ className,
95
+ }: TabsContentProps) => {
96
+ const context = useContext(TabsContext);
97
+ const activeTab = context?.activeTab;
98
+
99
+ return activeTab === value ? (
100
+ <Wrap className={className}>{children}</Wrap>
101
+ ) : null;
102
+ };
package/src/Tag.tsx ADDED
@@ -0,0 +1,44 @@
1
+ import classNames from 'classnames';
2
+
3
+ type TagProps = {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ color:
7
+ | 'blue'
8
+ | 'ruby'
9
+ | 'tomato'
10
+ | 'red'
11
+ | 'crimson'
12
+ | 'pink'
13
+ | 'plum'
14
+ | 'purple'
15
+ | 'violet'
16
+ | 'iris'
17
+ | 'indigo'
18
+ | 'cyan'
19
+ | 'teal'
20
+ | 'jade'
21
+ | 'green'
22
+ | 'grass'
23
+ | 'brown'
24
+ | 'orange'
25
+ | 'sky'
26
+ | 'gray';
27
+ asButton?: boolean;
28
+ onClick?: () => void;
29
+ };
30
+
31
+ export const Tag = ({
32
+ children,
33
+ onClick,
34
+ color = 'blue',
35
+ className,
36
+ }: TagProps) => {
37
+ return (
38
+ <button onClick={onClick}>
39
+ <div color={color} className={classNames(className)}>
40
+ {children}
41
+ </div>
42
+ </button>
43
+ );
44
+ };
@@ -0,0 +1,74 @@
1
+ import { Wrap } from "./Wrap";
2
+ import { forwardRef } from "@wordpress/element";
3
+ import classNames from "classnames";
4
+
5
+ type TextControlProps = {
6
+ className?: string;
7
+ inputClassName?: string;
8
+ defaultValue: any;
9
+ children?: React.ReactNode;
10
+ onChange?: (value: string) => void;
11
+ onClick?: () => void;
12
+ readOnly?: boolean;
13
+ placeholder?: string;
14
+ onBlur?: (value) => void;
15
+ type?: string;
16
+ label?: string;
17
+ helper?: string;
18
+ };
19
+
20
+ export const TextControl = forwardRef<HTMLInputElement, TextControlProps>(
21
+ (
22
+ {
23
+ onClick,
24
+ onChange,
25
+ className,
26
+ defaultValue,
27
+ children,
28
+ inputClassName,
29
+ readOnly,
30
+ placeholder,
31
+ onBlur,
32
+ type = "text",
33
+ label,
34
+ helper = "",
35
+ },
36
+ ref
37
+ ) => {
38
+ const fieldId = `text-control-${Math.random()
39
+ .toString(36)
40
+ .substring(2, 15)}`;
41
+
42
+ return (
43
+ <Wrap className={classNames("flex items-center p-0", className)}>
44
+ {label ? (
45
+ <label htmlFor={fieldId} className="text-primary !m-0 !mb-0 !mr-2">
46
+ {label}
47
+ </label>
48
+ ) : null}
49
+ <input
50
+ id={fieldId}
51
+ className={classNames(
52
+ "border-primary !m-0 !mb-0 h-[32px] !rounded-none border border-opacity-70 focus:outline-none focus:ring-0",
53
+ inputClassName
54
+ )}
55
+ type={type}
56
+ value={defaultValue}
57
+ placeholder={placeholder}
58
+ onFocus={() => onClick && onClick()}
59
+ onBlur={(e) => {
60
+ if (onClick) onClick();
61
+ if (onBlur) onBlur(e.target.value);
62
+ }}
63
+ onChange={(e) => onChange && onChange(e.target.value)}
64
+ readOnly={readOnly}
65
+ ref={ref}
66
+ />
67
+ <span>{children}</span>
68
+ {helper && <p className="text-primary !m-0 !mb-0 text-xs">{helper}</p>}
69
+ </Wrap>
70
+ );
71
+ }
72
+ );
73
+
74
+ TextControl.displayName = "TextControl"; // Recommended for debugging
@@ -0,0 +1,27 @@
1
+ import Pencil1Icon from "@blockbite/icons/dist/Pencil1";
2
+ import { Wrap } from "./Wrap";
3
+ import { TextControl } from "@wordpress/components";
4
+
5
+ type TextControlLabelProps = {
6
+ className?: string;
7
+ defaultValue: any;
8
+ children?: React.ReactNode;
9
+ onChange?: (value: string) => void;
10
+ onClick?: () => void;
11
+ };
12
+
13
+ export const TextControlLabel = ({
14
+ onChange,
15
+ defaultValue,
16
+ children,
17
+ }: TextControlLabelProps) => {
18
+ return (
19
+ <Wrap className="blockbite-ui__text-control-label flex items-center">
20
+ <span className="outline-b-2 relative block -outline-offset-2 outline-black">
21
+ <TextControl type="text" value={defaultValue} onChange={onChange} />
22
+ <Pencil1Icon className="absolute right-1 top-2" />
23
+ </span>{" "}
24
+ {children}
25
+ </Wrap>
26
+ );
27
+ };
@@ -0,0 +1,72 @@
1
+ import { Wrap } from "./Wrap";
2
+ import { memo, useEffect, useState } from "@wordpress/element";
3
+
4
+ import {
5
+ __experimentalToggleGroupControlOptionIcon as ToggleGroupControlIcon,
6
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
7
+ __experimentalToggleGroupControl as WordpressToggleGroupControl,
8
+ } from "@wordpress/components";
9
+ import classNames from "classnames";
10
+
11
+ type ToggleProps = {
12
+ className?: string;
13
+ options: { value: string; label: string; icon?: React.ReactElement }[];
14
+ defaultPressed: string;
15
+ label?: string | boolean;
16
+ variant?: "primary" | "secondary";
17
+ display?: "icon" | "label";
18
+ size?: "small" | "default" | "compact";
19
+ onPressedChange: (value: string) => void;
20
+ };
21
+
22
+ const ToggleGroup: React.FC<ToggleProps> = memo(
23
+ ({
24
+ className,
25
+ options,
26
+ defaultPressed,
27
+ display = "label",
28
+ label = false,
29
+ onPressedChange,
30
+ }) => {
31
+ const [isPressed, setIsPressed] = useState<string>("");
32
+
33
+ const handlePressChange = (value: string) => {
34
+ onPressedChange(value);
35
+ };
36
+
37
+ useEffect(() => {
38
+ setIsPressed(defaultPressed);
39
+ }, [defaultPressed]);
40
+
41
+ return (
42
+ <Wrap className={classNames(className)}>
43
+ <WordpressToggleGroupControl
44
+ isBlock
45
+ value={isPressed}
46
+ label={typeof label === "string" ? label : ""}
47
+ >
48
+ {options.map((option) =>
49
+ display === "icon" ? (
50
+ <ToggleGroupControlIcon
51
+ icon={option.icon}
52
+ label={option.label}
53
+ key={`ToggleGroupControlIcon-${option.value}`}
54
+ value={option.value}
55
+ onClick={() => handlePressChange(option.value)}
56
+ />
57
+ ) : (
58
+ <ToggleGroupControlOption
59
+ key={`ToggleGroupControlOption-${option.value}`}
60
+ value={option.value}
61
+ label={option.label}
62
+ onClick={() => handlePressChange(option.value)}
63
+ />
64
+ )
65
+ )}
66
+ </WordpressToggleGroupControl>
67
+ </Wrap>
68
+ );
69
+ }
70
+ );
71
+
72
+ export { ToggleGroup };
@@ -0,0 +1,37 @@
1
+ import { Wrap } from "./Wrap";
2
+ import { ToggleControl as Switch } from "@wordpress/components";
3
+ import { useEffect, useState } from "@wordpress/element";
4
+ import classNames from "classnames";
5
+
6
+ type ToggleSwitchProps = {
7
+ label?: string;
8
+ className?: string;
9
+ onChange?: (checked: boolean) => void;
10
+ checked?: boolean;
11
+ };
12
+
13
+ export const ToggleSwitch = ({
14
+ label,
15
+ className,
16
+ onChange,
17
+ checked = false, // Default to false if undefined, so it's always controlled
18
+ }: ToggleSwitchProps) => {
19
+ const [isChecked, setIsChecked] = useState(checked);
20
+
21
+ useEffect(() => {
22
+ setIsChecked(checked);
23
+ }, [checked]);
24
+
25
+ return (
26
+ <Wrap className={classNames(className, "flex items-center gap-2")}>
27
+ <Switch
28
+ checked={isChecked}
29
+ label={label}
30
+ onChange={(e) => {
31
+ setIsChecked(e);
32
+ onChange && onChange(e);
33
+ }}
34
+ ></Switch>
35
+ </Wrap>
36
+ );
37
+ };
package/src/Wrap.tsx ADDED
@@ -0,0 +1,23 @@
1
+ type WrapProps = {
2
+ children: React.ReactNode;
3
+ className?: string;
4
+ important?: boolean;
5
+ onClick?: (e: React.MouseEvent) => void;
6
+ };
7
+
8
+ export const Wrap = ({
9
+ children,
10
+ className,
11
+ important = false,
12
+ onClick,
13
+ }: WrapProps) => {
14
+ if (important) {
15
+ return (
16
+ <div className="bb_" onClick={onClick}>
17
+ <div className={className}>{children}</div>
18
+ </div>
19
+ );
20
+ }
21
+
22
+ return <div className={className}>{children}</div>;
23
+ };