@intlayer/design-system 9.3.2 → 9.4.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/esm/api/hooks/analytics.mjs +6 -5
- package/dist/esm/api/hooks/analytics.mjs.map +1 -1
- package/dist/esm/api/hooks/auth.mjs +2 -0
- package/dist/esm/api/hooks/auth.mjs.map +1 -1
- package/dist/esm/api/index.mjs +2 -2
- package/dist/esm/api/useAuth/useOAuth2.mjs +1 -1
- package/dist/esm/api/useAuth/useSession.mjs +1 -1
- package/dist/esm/components/Accordion/Accordion.mjs +2 -2
- package/dist/esm/components/Accordion/Accordion.mjs.map +1 -1
- package/dist/esm/components/Browser/Browser.mjs +1 -1
- package/dist/esm/components/Browser/Browser.mjs.map +1 -1
- package/dist/esm/components/ContentEditor/ContentEditorTextArea.mjs +1 -1
- package/dist/esm/components/CopyButton/index.mjs +2 -1
- package/dist/esm/components/CopyButton/index.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs +4 -5
- package/dist/esm/components/DictionaryFieldEditor/ContentEditor.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs +6 -10
- package/dist/esm/components/DictionaryFieldEditor/ContentEditorView/TextEditor.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryCreationForm/DictionaryCreationForm.mjs +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs +6 -6
- package/dist/esm/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs +3 -7
- package/dist/esm/components/DictionaryFieldEditor/DictionaryFieldEditor.mjs.map +1 -1
- package/dist/esm/components/DictionaryFieldEditor/NavigationView/NavigationViewNode.mjs +1 -1
- package/dist/esm/components/DictionaryFieldEditor/SaveForm/SaveForm.mjs +2 -2
- package/dist/esm/components/DictionaryFieldEditor/StructureView/StructureView.mjs +1 -1
- package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs +19 -12
- package/dist/esm/components/ExpandCollapse/ExpandCollapse.mjs.map +1 -1
- package/dist/esm/components/Form/elements/OTPElement.mjs +4 -4
- package/dist/esm/components/Form/elements/OTPElement.mjs.map +1 -1
- package/dist/esm/components/HTMLRender/HTMLRender.mjs +2 -2
- package/dist/esm/components/HTMLRender/HTMLRender.mjs.map +1 -1
- package/dist/esm/components/IDE/Code.mjs +3 -3
- package/dist/esm/components/IDE/Code.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockClient.mjs +16 -4
- package/dist/esm/components/IDE/CodeBlockClient.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockHighlight.mjs +14 -5
- package/dist/esm/components/IDE/CodeBlockHighlight.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockServer.mjs +5 -3
- package/dist/esm/components/IDE/CodeBlockServer.mjs.map +1 -1
- package/dist/esm/components/IDE/CodeBlockShiki.mjs +18 -18
- package/dist/esm/components/IDE/CodeBlockShiki.mjs.map +1 -1
- package/dist/esm/components/IDE/IDE.mjs +2 -5
- package/dist/esm/components/IDE/IDE.mjs.map +1 -1
- package/dist/esm/components/IDE/MarkDownRender.mjs +1 -2
- package/dist/esm/components/IDE/MarkDownRender.mjs.map +1 -1
- package/dist/esm/components/IDE/index.mjs +2 -2
- package/dist/esm/components/IDE/shikiThemes.mjs +18 -0
- package/dist/esm/components/IDE/shikiThemes.mjs.map +1 -0
- package/dist/esm/components/Link/Link.mjs +10 -5
- package/dist/esm/components/Link/Link.mjs.map +1 -1
- package/dist/esm/components/LocaleSwitcherContentDropDown/LocaleSwitcherContent.mjs +1 -1
- package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs +105 -4
- package/dist/esm/components/MarkDownRender/MarkDownIframe.mjs.map +1 -1
- package/dist/esm/components/MarkDownRender/MarkDownRender.mjs +16 -16
- package/dist/esm/components/MarkDownRender/MarkDownRender.mjs.map +1 -1
- package/dist/esm/components/MaxHeightSmoother/index.mjs +1 -1
- package/dist/esm/components/MaxHeightSmoother/index.mjs.map +1 -1
- package/dist/esm/components/MaxWidthSmoother/index.mjs +1 -1
- package/dist/esm/components/MaxWidthSmoother/index.mjs.map +1 -1
- package/dist/esm/components/Modal/Modal.mjs +2 -2
- package/dist/esm/components/Navbar/MobileNavbar.mjs +1 -1
- package/dist/esm/components/Pagination/Pagination.mjs +4 -4
- package/dist/esm/components/Pagination/Pagination.mjs.map +1 -1
- package/dist/esm/components/Pattern/GridDistortionPattern.mjs +103 -17
- package/dist/esm/components/Pattern/GridDistortionPattern.mjs.map +1 -1
- package/dist/esm/components/Popover/dynamic.mjs +73 -28
- package/dist/esm/components/Popover/dynamic.mjs.map +1 -1
- package/dist/esm/components/Popover/index.mjs +2 -2
- package/dist/esm/components/Popover/static.mjs +47 -18
- package/dist/esm/components/Popover/static.mjs.map +1 -1
- package/dist/esm/components/RightDrawer/RightDrawer.mjs +5 -4
- package/dist/esm/components/RightDrawer/RightDrawer.mjs.map +1 -1
- package/dist/esm/components/SwitchSelector/useSwitchSelector.mjs +2 -2
- package/dist/esm/components/SwitchSelector/useSwitchSelector.mjs.map +1 -1
- package/dist/esm/components/Tab/Tab.mjs +38 -17
- package/dist/esm/components/Tab/Tab.mjs.map +1 -1
- package/dist/esm/components/TabSelector/TabSelector.mjs +2 -2
- package/dist/esm/components/TabSelector/TabSelector.mjs.map +1 -1
- package/dist/esm/components/TextArea/AutoSizeTextArea.mjs +3 -2
- package/dist/esm/components/TextArea/AutoSizeTextArea.mjs.map +1 -1
- package/dist/esm/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.mjs +10 -18
- package/dist/esm/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.mjs.map +1 -1
- package/dist/esm/components/ThemeSwitcherDropDown/MobileThemeSwitcher.mjs +14 -13
- package/dist/esm/components/ThemeSwitcherDropDown/MobileThemeSwitcher.mjs.map +1 -1
- package/dist/esm/components/index.mjs +3 -3
- package/dist/esm/hooks/index.mjs +9 -9
- package/dist/esm/hooks/useDevice.mjs +16 -6
- package/dist/esm/hooks/useDevice.mjs.map +1 -1
- package/dist/esm/hooks/useItemSelector.mjs +110 -112
- package/dist/esm/hooks/useItemSelector.mjs.map +1 -1
- package/dist/esm/hooks/useScrollY.mjs +64 -45
- package/dist/esm/hooks/useScrollY.mjs.map +1 -1
- package/dist/esm/hooks/useStickToBottom.mjs +69 -0
- package/dist/esm/hooks/useStickToBottom.mjs.map +1 -0
- package/dist/esm/libs/auth.mjs +16 -3
- package/dist/esm/libs/auth.mjs.map +1 -1
- package/dist/esm/providers/ThemeProvider.mjs +143 -0
- package/dist/esm/providers/ThemeProvider.mjs.map +1 -0
- package/dist/esm/providers/index.mjs +2 -1
- package/dist/esm/routes.mjs +2 -1
- package/dist/esm/routes.mjs.map +1 -1
- package/dist/esm/structured-data/buildCreativeWorkJsonLd.mjs +1 -1
- package/dist/esm/structured-data/buildCreativeWorkJsonLd.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +3 -1
- package/dist/esm/utils/observeScrollExtent.mjs +39 -0
- package/dist/esm/utils/observeScrollExtent.mjs.map +1 -0
- package/dist/esm/utils/scheduleFrameTask.mjs +44 -0
- package/dist/esm/utils/scheduleFrameTask.mjs.map +1 -0
- package/dist/types/api/hooks/analytics.d.ts +5 -3
- package/dist/types/api/hooks/analytics.d.ts.map +1 -1
- package/dist/types/api/hooks/auth.d.ts.map +1 -1
- package/dist/types/components/Browser/Browser.d.ts +7 -1
- package/dist/types/components/Browser/Browser.d.ts.map +1 -1
- package/dist/types/components/CopyButton/index.d.ts +9 -3
- package/dist/types/components/CopyButton/index.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts +0 -1
- package/dist/types/components/DictionaryFieldEditor/ContentEditor.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts +0 -1
- package/dist/types/components/DictionaryFieldEditor/ContentEditorView/TextEditor.d.ts.map +1 -1
- package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.d.ts +0 -1
- package/dist/types/components/DictionaryFieldEditor/DictionaryDetails/DictionaryDetailsForm.d.ts.map +1 -1
- package/dist/types/components/ExpandCollapse/ExpandCollapse.d.ts.map +1 -1
- package/dist/types/components/HTMLRender/HTMLRender.d.ts +1 -2
- package/dist/types/components/HTMLRender/HTMLRender.d.ts.map +1 -1
- package/dist/types/components/IDE/Code.d.ts +6 -1
- package/dist/types/components/IDE/Code.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeBlockClient.d.ts +18 -2
- package/dist/types/components/IDE/CodeBlockClient.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeBlockHighlight.d.ts +3 -2
- package/dist/types/components/IDE/CodeBlockHighlight.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeBlockServer.d.ts +0 -1
- package/dist/types/components/IDE/CodeBlockServer.d.ts.map +1 -1
- package/dist/types/components/IDE/CodeBlockShiki.d.ts +0 -1
- package/dist/types/components/IDE/CodeBlockShiki.d.ts.map +1 -1
- package/dist/types/components/IDE/IDE.d.ts +0 -1
- package/dist/types/components/IDE/IDE.d.ts.map +1 -1
- package/dist/types/components/IDE/MarkDownRender.d.ts +0 -1
- package/dist/types/components/IDE/MarkDownRender.d.ts.map +1 -1
- package/dist/types/components/IDE/index.d.ts +2 -2
- package/dist/types/components/IDE/shikiThemes.d.ts +18 -0
- package/dist/types/components/IDE/shikiThemes.d.ts.map +1 -0
- package/dist/types/components/MarkDownRender/MarkDownIframe.d.ts.map +1 -1
- package/dist/types/components/MarkDownRender/MarkDownRender.d.ts +1 -2
- package/dist/types/components/MarkDownRender/MarkDownRender.d.ts.map +1 -1
- package/dist/types/components/Pattern/GridDistortionPattern.d.ts.map +1 -1
- package/dist/types/components/Popover/dynamic.d.ts.map +1 -1
- package/dist/types/components/Popover/index.d.ts +2 -2
- package/dist/types/components/Popover/static.d.ts +6 -1
- package/dist/types/components/Popover/static.d.ts.map +1 -1
- package/dist/types/components/Tab/Tab.d.ts +2 -2
- package/dist/types/components/Tab/Tab.d.ts.map +1 -1
- package/dist/types/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.d.ts +1 -1
- package/dist/types/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.d.ts.map +1 -1
- package/dist/types/components/ThemeSwitcherDropDown/MobileThemeSwitcher.d.ts +1 -1
- package/dist/types/components/ThemeSwitcherDropDown/MobileThemeSwitcher.d.ts.map +1 -1
- package/dist/types/components/ThemeSwitcherDropDown/types.d.ts +3 -1
- package/dist/types/components/ThemeSwitcherDropDown/types.d.ts.map +1 -1
- package/dist/types/components/index.d.ts +3 -3
- package/dist/types/hooks/index.d.ts +2 -2
- package/dist/types/hooks/useItemSelector.d.ts +2 -0
- package/dist/types/hooks/useItemSelector.d.ts.map +1 -1
- package/dist/types/hooks/useScrollY.d.ts +10 -0
- package/dist/types/hooks/useScrollY.d.ts.map +1 -1
- package/dist/types/hooks/useStickToBottom.d.ts +41 -0
- package/dist/types/hooks/useStickToBottom.d.ts.map +1 -0
- package/dist/types/libs/auth.d.ts +3 -0
- package/dist/types/libs/auth.d.ts.map +1 -1
- package/dist/types/providers/ThemeProvider.d.ts +41 -0
- package/dist/types/providers/ThemeProvider.d.ts.map +1 -0
- package/dist/types/providers/index.d.ts +2 -1
- package/dist/types/routes.d.ts +2 -1
- package/dist/types/routes.d.ts.map +1 -1
- package/dist/types/structured-data/buildCreativeWorkJsonLd.d.ts +9 -3
- package/dist/types/structured-data/buildCreativeWorkJsonLd.d.ts.map +1 -1
- package/dist/types/utils/index.d.ts +3 -1
- package/dist/types/utils/observeScrollExtent.d.ts +22 -0
- package/dist/types/utils/observeScrollExtent.d.ts.map +1 -0
- package/dist/types/utils/scheduleFrameTask.d.ts +25 -0
- package/dist/types/utils/scheduleFrameTask.d.ts.map +1 -0
- package/package.json +46 -41
- package/tailwind.css +27 -1
- package/dist/esm/hooks/useIsDarkMode.mjs +0 -17
- package/dist/esm/hooks/useIsDarkMode.mjs.map +0 -1
- package/dist/types/hooks/useIsDarkMode.d.ts +0 -5
- package/dist/types/hooks/useIsDarkMode.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tab.mjs","names":[],"sources":["../../../../src/components/Tab/Tab.tsx"],"sourcesContent":["'use client';\n\nimport { useHorizontalSwipe } from '@hooks/useHorizontalSwipe';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Children,\n createContext,\n type HTMLAttributes,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useState,\n} from 'react';\nimport { TabSelector } from '../TabSelector';\nimport { useTabContext } from './TabContext';\n\n// Context for managing tab state\ntype TabContextType = {\n activeTab: string;\n setActiveTab: (tab: string) => void;\n};\n\nconst TabContext = createContext<TabContextType | undefined>(undefined);\n\n// Tab container variants\nconst tabContainerVariant = cva('relative w-full rounded-lg', {\n variants: {\n background: {\n with: 'border border-neutral/20 bg-background/2 shadow-[0_0_10px_-15px_rgba(0,0,0,0.3)] backdrop-blur',\n without: '',\n },\n variant: {\n default: '',\n bordered: 'border-2',\n ghost: 'border-0 bg-transparent shadow-none',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n});\n\nexport type TabProps = HTMLAttributes<HTMLDivElement> &\n VariantProps<typeof tabContainerVariant> & {\n defaultTab?: string;\n group?: string;\n children: ReactNode;\n headerClassName?: string;\n fullHeight?: boolean;\n };\n\nexport type TabItemProps = HTMLAttributes<HTMLDivElement> & {\n label: string;\n value: string;\n disabled?: boolean;\n children: ReactNode;\n};\n\n/**\n * TabItem component that represents a single tab\n * Must be used as a child of the Tab component\n */\nconst TabItem = ({ children, ...props }: TabItemProps) => (\n // This component is primarily used for its props by the parent Tab component\n // The actual rendering is handled by the Tab component\n <div {...props}>{children}</div>\n);\n\n// Add display name for better debugging\nTabItem.displayName = 'TabItem';\n\n/**\n * Tab container component that manages tab state and renders tab headers and content\n *\n * Example:\n * ```jsx\n * <Tab defaultTab=\"tab1\">\n * <Tab.Item label=\"First Tab\" value=\"tab1\">\n * Content for first tab\n * </Tab.Item>\n * <Tab.Item label=\"Second Tab\" value=\"tab2\">\n * Content for second tab\n * </Tab.Item>\n * </Tab>\n * ```\n */\nconst TabComponent = ({\n defaultTab,\n group,\n variant,\n children,\n className,\n headerClassName,\n fullHeight,\n ...props\n}: TabProps) => {\n // Extract TabItem children to get their props\n const tabItems = Children.toArray(children).filter((child) => {\n return isValidElement(child) && child.type === TabItem;\n }) as ReactElement<TabItemProps>[];\n\n const firstTabValue = tabItems[0]?.props?.value;\n const { tabsValues, setTabsValues } = useTabContext();\n const [activeTab, setActiveTab] = useState(defaultTab ?? firstTabValue ?? '');\n const hasGroup = group && typeof tabsValues === 'object';\n const currentTabValue =\n (hasGroup ? tabsValues?.[group] : activeTab) ?? defaultTab ?? firstTabValue;\n const activeTabIndex = tabItems.findIndex(\n (tab) => tab.props.value === currentTabValue\n );\n\n const tabsCount = tabItems.length;\n\n const { containerProps, dragDeltaPct, isDragging } = useHorizontalSwipe({\n itemIndex: activeTabIndex,\n itemCount: tabsCount,\n onSwipeLeft: () => {\n const targetIndex = Math.min(tabsCount - 1, activeTabIndex + 1);\n const nextValue = tabItems[targetIndex]?.props?.value;\n if (nextValue) handleSetActiveTab(nextValue);\n },\n onSwipeRight: () => {\n const targetIndex = Math.max(0, activeTabIndex - 1);\n const nextValue = tabItems[targetIndex]?.props?.value;\n if (nextValue) handleSetActiveTab(nextValue);\n },\n });\n\n const handleSetActiveTab = (tab: string) => {\n setActiveTab(tab);\n\n if (typeof setTabsValues === 'function') {\n setTabsValues((prev) => ({ ...prev, [group!]: tab }));\n }\n };\n\n const contextValue: TabContextType = {\n activeTab: activeTab ?? firstTabValue ?? '',\n setActiveTab: handleSetActiveTab,\n };\n\n return (\n <TabContext.Provider value={contextValue}>\n <div\n className={cn(\n tabContainerVariant({ variant }),\n fullHeight && 'flex h-full flex-col overflow-hidden',\n className\n )}\n {...props}\n >\n {/* Tab Headers */}\n <div className={cn('flex shrink-0 gap-3 p-3', headerClassName)}>\n <TabSelector\n selectedChoice={currentTabValue}\n tabs={tabItems.map((child) => {\n const { label, value, disabled } = child.props;\n const isActive = currentTabValue === value;\n\n return (\n <button\n key={value}\n className={cn(\n 'cursor-pointer whitespace-nowrap rounded-md px-4 py-1 font-medium text-sm transition-colors focus:outline-none',\n !isActive && 'text-neutral/70'\n )}\n data-active={isActive}\n disabled={disabled}\n onClick={() => !disabled && handleSetActiveTab(value)}\n role=\"tab\"\n aria-selected={isActive}\n aria-controls={`tabpanel-${value}`}\n id={`tab-${value}`}\n type=\"button\"\n >\n {label ?? value}\n </button>\n );\n })}\n hoverable\n color=\"text\"\n />\n </div>\n {/* Tab Content */}\n {/* Clipper: no overflow; uses clip-path */}\n <div\n className={cn(\n 'relative w-full min-w-0 overflow-x-clip [-webkit-clip-path:inset(0)] [clip-path:inset(0)]',\n fullHeight && 'min-h-0 flex-1'\n )}\n {...containerProps}\n >\n {/* Track */}\n <div\n role=\"tablist\"\n aria-orientation=\"horizontal\"\n className={cn(\n 'grid w-full min-w-0',\n fullHeight && 'h-full',\n isDragging\n ? 'transition-none'\n : 'transition-transform duration-300 ease-in-out'\n )}\n style={{\n gridTemplateColumns: `repeat(${tabItems.length}, 100%)`,\n transform: `translateX(-${activeTabIndex * 100 - (isDragging ? dragDeltaPct : 0)}%)`,\n }}\n >\n {tabItems.map(({ props }, index) => {\n const { value, children, className: itemClassName } = props;\n const isActive = index === activeTabIndex;\n\n return (\n <div\n key={value}\n role=\"tabpanel\"\n aria-labelledby={`tab-${value}`}\n id={`tabpanel-${value}`}\n aria-hidden={!isActive}\n tabIndex={isActive ? 0 : -1}\n data-active={isActive}\n className={cn(\n 'w-full min-w-0 p-3 opacity-100 transition-opacity duration-300 ease-in-out',\n fullHeight && 'h-full overflow-y-auto',\n !isActive && 'pointer-events-none opacity-0', // prevent offscreen interaction\n itemClassName\n )}\n >\n <div\n className={cn(\n 'flex w-full min-w-0 flex-col items-stretch gap-6',\n fullHeight && 'min-h-full'\n )}\n >\n {children}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n </div>\n </TabContext.Provider>\n );\n};\n\n// Create the compound component\nexport const Tab = Object.assign(TabComponent, {\n Item: TabItem,\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tab.mjs","names":[],"sources":["../../../../src/components/Tab/Tab.tsx"],"sourcesContent":["'use client';\n\nimport { useHorizontalSwipe } from '@hooks/useHorizontalSwipe';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n Children,\n createContext,\n type HTMLAttributes,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useState,\n} from 'react';\nimport { TabSelector } from '../TabSelector';\nimport { useTabContext } from './TabContext';\n\n/**\n * Builds an id fragment usable in `id` / `aria-controls`.\n *\n * Tab values come from markdown (`<Tab value=\"Extract command\">`), so they can\n * hold spaces — which makes `aria-controls` reference a list of ids rather than\n * the one panel, and silently breaks the association for screen readers.\n */\nconst toIdFragment = (value: string) =>\n value\n .trim()\n .replace(/\\s+/g, '-')\n .replace(/[^\\w-]/g, '');\n\n// Context for managing tab state\ntype TabContextType = {\n activeTab: string;\n setActiveTab: (tab: string) => void;\n};\n\nconst TabContext = createContext<TabContextType | undefined>(undefined);\n\n// Tab container variants\nconst tabContainerVariant = cva('relative w-full rounded-lg', {\n variants: {\n background: {\n with: 'border border-neutral/20 bg-background/2 shadow-[0_0_10px_-15px_rgba(0,0,0,0.3)] backdrop-blur',\n without: '',\n },\n variant: {\n default: '',\n bordered: 'border-2',\n ghost: 'border-0 bg-transparent shadow-none',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n});\n\nexport type TabProps = HTMLAttributes<HTMLDivElement> &\n VariantProps<typeof tabContainerVariant> & {\n defaultTab?: string;\n group?: string;\n children: ReactNode;\n headerClassName?: string;\n fullHeight?: boolean;\n };\n\nexport type TabItemProps = HTMLAttributes<HTMLDivElement> & {\n label?: string;\n value?: string;\n disabled?: boolean;\n children: ReactNode;\n};\n\n/** A tab item whose identifier has been resolved to a usable string. */\ntype ResolvedTabItem = TabItemProps & { value: string };\n\n/**\n * Resolves the identifier of a tab.\n *\n * Both `label` and `value` are optional for markdown-authored tabs\n * (`<Tab label=\"next-intl\">`), so the identifier falls back to the label, then\n * to the position, keeping every tab addressable instead of rendering ids from\n * `undefined`.\n */\nconst resolveTabValue = (\n { value, label }: TabItemProps,\n index: number\n): string => {\n const identifier = value ?? label;\n\n return typeof identifier === 'string' ? identifier : String(index);\n};\n\n/**\n * TabItem component that represents a single tab\n * Must be used as a child of the Tab component\n */\nconst TabItem = ({ children, ...props }: TabItemProps) => (\n // This component is primarily used for its props by the parent Tab component\n // The actual rendering is handled by the Tab component\n <div {...props}>{children}</div>\n);\n\n// Add display name for better debugging\nTabItem.displayName = 'TabItem';\n\n/**\n * Tab container component that manages tab state and renders tab headers and content\n *\n * Example:\n * ```jsx\n * <Tab defaultTab=\"tab1\">\n * <Tab.Item label=\"First Tab\" value=\"tab1\">\n * Content for first tab\n * </Tab.Item>\n * <Tab.Item label=\"Second Tab\" value=\"tab2\">\n * Content for second tab\n * </Tab.Item>\n * </Tab>\n * ```\n */\nconst TabComponent = ({\n defaultTab,\n group,\n variant,\n children,\n className,\n headerClassName,\n fullHeight,\n ...props\n}: TabProps) => {\n // Extract TabItem children to get their props\n const tabItems: ResolvedTabItem[] = (\n Children.toArray(children).filter((child) => {\n return isValidElement(child) && child.type === TabItem;\n }) as ReactElement<TabItemProps>[]\n ).map((child, index) => ({\n ...child.props,\n value: resolveTabValue(child.props, index),\n }));\n\n const firstTabValue = tabItems[0]?.value;\n const { tabsValues, setTabsValues } = useTabContext();\n const [activeTab, setActiveTab] = useState(defaultTab ?? firstTabValue ?? '');\n const hasGroup = group && typeof tabsValues === 'object';\n const currentTabValue =\n (hasGroup ? tabsValues?.[group] : activeTab) ?? defaultTab ?? firstTabValue;\n const activeTabIndex = tabItems.findIndex(\n (tab) => tab.value === currentTabValue\n );\n\n const tabsCount = tabItems.length;\n\n const { containerProps, dragDeltaPct, isDragging } = useHorizontalSwipe({\n itemIndex: activeTabIndex,\n itemCount: tabsCount,\n onSwipeLeft: () => {\n const targetIndex = Math.min(tabsCount - 1, activeTabIndex + 1);\n const nextValue = tabItems[targetIndex]?.value;\n if (nextValue) handleSetActiveTab(nextValue);\n },\n onSwipeRight: () => {\n const targetIndex = Math.max(0, activeTabIndex - 1);\n const nextValue = tabItems[targetIndex]?.value;\n if (nextValue) handleSetActiveTab(nextValue);\n },\n });\n\n const handleSetActiveTab = (tab: string) => {\n setActiveTab(tab);\n\n if (typeof setTabsValues === 'function') {\n setTabsValues((prev) => ({ ...prev, [group!]: tab }));\n }\n };\n\n const contextValue: TabContextType = {\n activeTab: activeTab ?? firstTabValue ?? '',\n setActiveTab: handleSetActiveTab,\n };\n\n return (\n <TabContext.Provider value={contextValue}>\n <div\n className={cn(\n tabContainerVariant({ variant }),\n fullHeight && 'flex h-full flex-col overflow-hidden',\n className\n )}\n {...props}\n >\n {/* Tab Headers — `TabSelector` is the tablist; wrapping it in another\n one would nest two, leaving the outer without any `tab` child. */}\n <div className={cn('flex shrink-0 gap-3 p-3', headerClassName)}>\n <TabSelector\n selectedChoice={currentTabValue}\n tabs={tabItems.map(({ label, value, disabled }) => {\n const isActive = currentTabValue === value;\n const idFragment = toIdFragment(value);\n\n return (\n <button\n key={value}\n className={cn(\n 'cursor-pointer whitespace-nowrap rounded-md px-4 py-1 font-medium text-sm transition-colors focus:outline-none',\n !isActive && 'text-neutral/70'\n )}\n data-active={isActive}\n disabled={disabled}\n onClick={() => !disabled && handleSetActiveTab(value)}\n role=\"tab\"\n aria-selected={isActive}\n aria-controls={`tabpanel-${idFragment}`}\n id={`tab-${idFragment}`}\n type=\"button\"\n >\n {label ?? value}\n </button>\n );\n })}\n hoverable\n color=\"text\"\n />\n </div>\n {/* Tab Content */}\n {/* Clipper: no overflow; uses clip-path */}\n <div\n className={cn(\n 'relative w-full min-w-0 overflow-x-clip [-webkit-clip-path:inset(0)] [clip-path:inset(0)]',\n fullHeight && 'min-h-0 flex-1'\n )}\n {...containerProps}\n >\n {/* Track — presentational: it carries the sliding transform, while\n the panels it holds are the tablist's controlled regions. */}\n <div\n className={cn(\n 'grid w-full min-w-0',\n fullHeight && 'h-full',\n isDragging\n ? 'transition-none'\n : 'transition-transform duration-300 ease-in-out'\n )}\n style={{\n gridTemplateColumns: `repeat(${tabItems.length}, 100%)`,\n transform: `translateX(-${activeTabIndex * 100 - (isDragging ? dragDeltaPct : 0)}%)`,\n }}\n >\n {tabItems.map(\n ({ value, children, className: itemClassName }, index) => {\n const isActive = index === activeTabIndex;\n const idFragment = toIdFragment(value);\n\n return (\n <div\n key={value}\n role=\"tabpanel\"\n aria-labelledby={`tab-${idFragment}`}\n id={`tabpanel-${idFragment}`}\n // `inert` takes the whole subtree out of the tab order and\n // the accessibility tree at once. `aria-hidden` alone leaves\n // the buttons and links of an off-screen panel focusable,\n // which strands keyboard users on invisible controls.\n inert={!isActive}\n tabIndex={isActive ? 0 : -1}\n data-active={isActive}\n className={cn(\n 'w-full min-w-0 p-3 opacity-100 transition-opacity duration-300 ease-in-out',\n fullHeight && 'h-full overflow-y-auto',\n !isActive && 'pointer-events-none opacity-0', // prevent offscreen interaction\n itemClassName\n )}\n >\n <div\n className={cn(\n 'flex w-full min-w-0 flex-col items-stretch gap-6',\n fullHeight && 'min-h-full'\n )}\n >\n {children}\n </div>\n </div>\n );\n }\n )}\n </div>\n </div>\n </div>\n </TabContext.Provider>\n );\n};\n\n// Create the compound component\nexport const Tab = Object.assign(TabComponent, {\n Item: TabItem,\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;AAwBA,MAAM,gBAAgB,UACpB,MACG,KAAK,CAAC,CACN,QAAQ,QAAQ,GAAG,CAAC,CACpB,QAAQ,WAAW,EAAE;AAQ1B,MAAM,aAAa,cAA0C,MAAS;AAGtE,MAAM,sBAAsB,IAAI,8BAA8B;CAC5D,UAAU;EACR,YAAY;GACV,MAAM;GACN,SAAS;EACX;EACA,SAAS;GACP,SAAS;GACT,UAAU;GACV,OAAO;EACT;CACF;CACA,iBAAiB,EACf,SAAS,UACX;AACF,CAAC;;;;;;;;;AA6BD,MAAM,mBACJ,EAAE,OAAO,SACT,UACW;CACX,MAAM,aAAa,SAAS;CAE5B,OAAO,OAAO,eAAe,WAAW,aAAa,OAAO,KAAK;AACnE;;;;;AAMA,MAAM,WAAW,EAAE,UAAU,GAAG,YAG9B,oBAAC,OAAD;CAAK,GAAI;CAAQ;AAAc;AAIjC,QAAQ,cAAc;;;;;;;;;;;;;;;;AAiBtB,MAAM,gBAAgB,EACpB,YACA,OACA,SACA,UACA,WACA,iBACA,YACA,GAAG,YACW;CAEd,MAAM,WACJ,SAAS,QAAQ,QAAQ,CAAC,CAAC,QAAQ,UAAU;EAC3C,OAAO,eAAe,KAAK,KAAK,MAAM,SAAS;CACjD,CAAC,CAAC,CACF,KAAK,OAAO,WAAW;EACvB,GAAG,MAAM;EACT,OAAO,gBAAgB,MAAM,OAAO,KAAK;CAC3C,EAAE;CAEF,MAAM,gBAAgB,SAAS,EAAE,EAAE;CACnC,MAAM,EAAE,YAAY,kBAAkB,cAAc;CACpD,MAAM,CAAC,WAAW,gBAAgB,SAAS,cAAc,iBAAiB,EAAE;CAE5E,MAAM,mBADW,SAAS,OAAO,eAAe,WAElC,aAAa,SAAS,cAAc,cAAc;CAChE,MAAM,iBAAiB,SAAS,WAC7B,QAAQ,IAAI,UAAU,eACzB;CAEA,MAAM,YAAY,SAAS;CAE3B,MAAM,EAAE,gBAAgB,cAAc,eAAe,mBAAmB;EACtE,WAAW;EACX,WAAW;EACX,mBAAmB;GACjB,MAAM,cAAc,KAAK,IAAI,YAAY,GAAG,iBAAiB,CAAC;GAC9D,MAAM,YAAY,SAAS,YAAY,EAAE;GACzC,IAAI,WAAW,mBAAmB,SAAS;EAC7C;EACA,oBAAoB;GAClB,MAAM,cAAc,KAAK,IAAI,GAAG,iBAAiB,CAAC;GAClD,MAAM,YAAY,SAAS,YAAY,EAAE;GACzC,IAAI,WAAW,mBAAmB,SAAS;EAC7C;CACF,CAAC;CAED,MAAM,sBAAsB,QAAgB;EAC1C,aAAa,GAAG;EAEhB,IAAI,OAAO,kBAAkB,YAC3B,eAAe,UAAU;GAAE,GAAG;IAAO,QAAS;EAAI,EAAE;CAExD;CAEA,MAAM,eAA+B;EACnC,WAAW,aAAa,iBAAiB;EACzC,cAAc;CAChB;CAEA,OACE,oBAAC,WAAW,UAAZ;EAAqB,OAAO;YAC1B,qBAAC,OAAD;GACE,WAAW,GACT,oBAAoB,EAAE,QAAQ,CAAC,GAC/B,cAAc,wCACd,SACF;GACA,GAAI;aANN,CAUE,oBAAC,OAAD;IAAK,WAAW,GAAG,2BAA2B,eAAe;cAC3D,oBAAC,aAAD;KACE,gBAAgB;KAChB,MAAM,SAAS,KAAK,EAAE,OAAO,OAAO,eAAe;MACjD,MAAM,WAAW,oBAAoB;MACrC,MAAM,aAAa,aAAa,KAAK;MAErC,OACE,oBAAC,UAAD;OAEE,WAAW,GACT,kHACA,CAAC,YAAY,iBACf;OACA,eAAa;OACH;OACV,eAAe,CAAC,YAAY,mBAAmB,KAAK;OACpD,MAAK;OACL,iBAAe;OACf,iBAAe,YAAY;OAC3B,IAAI,OAAO;OACX,MAAK;iBAEJ,SAAS;MACJ,GAfD,KAeC;KAEZ,CAAC;KACD;KACA,OAAM;IACP;GACE,IAGL,oBAAC,OAAD;IACE,WAAW,GACT,6FACA,cAAc,gBAChB;IACA,GAAI;cAIJ,oBAAC,OAAD;KACE,WAAW,GACT,uBACA,cAAc,UACd,aACI,oBACA,+CACN;KACA,OAAO;MACL,qBAAqB,UAAU,SAAS,OAAO;MAC/C,WAAW,eAAe,iBAAiB,OAAO,aAAa,eAAe,GAAG;KACnF;eAEC,SAAS,KACP,EAAE,OAAO,UAAU,WAAW,iBAAiB,UAAU;MACxD,MAAM,WAAW,UAAU;MAC3B,MAAM,aAAa,aAAa,KAAK;MAErC,OACE,oBAAC,OAAD;OAEE,MAAK;OACL,mBAAiB,OAAO;OACxB,IAAI,YAAY;OAKhB,OAAO,CAAC;OACR,UAAU,WAAW,IAAI;OACzB,eAAa;OACb,WAAW,GACT,8EACA,cAAc,0BACd,CAAC,YAAY,iCACb,aACF;iBAEA,oBAAC,OAAD;QACE,WAAW,GACT,oDACA,cAAc,YAChB;QAEC;OACE;MACF,GA1BE,KA0BF;KAET,CACF;IACG;GACF,EACF;;CACc;AAEzB;AAGA,MAAa,MAAM,OAAO,OAAO,cAAc,EAC7C,MAAM,QACR,CAAC"}
|
|
@@ -57,12 +57,12 @@ const indicatorVariant = cva("absolute -z-1 rounded-lg duration-300 ease-in-out
|
|
|
57
57
|
const TabSelector = ({ tabs, selectedChoice, onTabClick, color = "primary", hoverable = false, orientation = "horizontal", className }) => {
|
|
58
58
|
const optionsRefs = useRef([]);
|
|
59
59
|
const indicatorRef = useRef(null);
|
|
60
|
-
const { choiceIndicatorPosition,
|
|
60
|
+
const { choiceIndicatorPosition, scheduleMeasurement } = useItemSelector(optionsRefs, {
|
|
61
61
|
isHoverable: hoverable,
|
|
62
62
|
orientation
|
|
63
63
|
});
|
|
64
64
|
useEffect(() => {
|
|
65
|
-
|
|
65
|
+
scheduleMeasurement();
|
|
66
66
|
}, [selectedChoice]);
|
|
67
67
|
return /* @__PURE__ */ jsxs("div", {
|
|
68
68
|
className: cn(tabSelectorVariant({ color }), className),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabSelector.mjs","names":[],"sources":["../../../../src/components/TabSelector/TabSelector.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ItemSelectorOrientation,\n useItemSelector,\n} from '@hooks/useItemSelector';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n cloneElement,\n type HTMLAttributes,\n type MouseEvent,\n type ReactElement,\n useEffect,\n useRef,\n} from 'react';\n\nexport type TabSelectorColor =\n | 'primary'\n | 'secondary'\n | 'error'\n | 'neutral'\n | 'light'\n | 'dark'\n | 'text';\n\nconst tabSelectorVariant = cva(\n 'relative z-0 flex size-full flex-row items-center gap-2',\n {\n variants: {\n color: {\n primary: 'border-primary text-primary',\n secondary: 'border-secondary text-secondary',\n error: 'border-error bg-error text-error',\n neutral: 'border-neutral text-neutral',\n light: 'border-white text-white',\n dark: 'border-neutral-800 text-neutral-800',\n text: 'border-text text-text',\n },\n },\n defaultVariants: {\n color: 'primary',\n },\n }\n);\n\nconst indicatorVariant = cva(\n 'absolute -z-1 rounded-lg duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n primary: 'bg-primary/10 aria-selected:text-text',\n secondary: 'bg-secondary/10 aria-selected:text-text',\n error: 'bg-error/10 aria-selected:text-text',\n neutral: 'bg-neutral/10 aria-selected:text-white/10',\n light: 'bg-white/10 aria-selected:text-black',\n dark: 'bg-neutral-800/10 aria-selected:text-white',\n text: 'bg-text/10 aria-selected:text-text-opposite',\n },\n orientation: {\n horizontal: 'top-0 h-full w-auto transition-[left,width]',\n vertical: 'left-0 h-auto w-full transition-[top,height]',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n }\n);\n\nexport type TabSelectorItemProps = HTMLAttributes<HTMLElement> & {\n key: string | number;\n};\n\nexport type TabSelectorProps<T extends TabSelectorItemProps> = {\n tabs: ReactElement<T>[];\n selectedChoice: T['key'];\n onTabClick?: (choice: T['key']) => void;\n hoverable?: boolean;\n orientation?: ItemSelectorOrientation;\n} & HTMLAttributes<HTMLElement> &\n Omit<VariantProps<typeof tabSelectorVariant>, 'color'> & {\n color?: TabSelectorColor | `${TabSelectorColor}`;\n };\n\n/**\n *\n * Component that allows the user to select one of the provided choices.\n *\n * Example:\n * ```jsx\n * <TabSelector\n * selectedChoice=\"option1\"\n * onTabClick={(choice) => console.log(choice)}\n * hoverable={true}\n * >\n * <Button key=\"option1\"/>\n * <Button key=\"option2\"/>\n * <Button key=\"option3\"/>\n * </TabSelector>\n * ```\n */\nexport const TabSelector = <T extends TabSelectorItemProps>({\n tabs,\n selectedChoice,\n onTabClick,\n color = 'primary',\n hoverable = false,\n orientation = 'horizontal',\n className,\n}: TabSelectorProps<T>) => {\n const optionsRefs = useRef<HTMLElement[]>([]);\n const indicatorRef = useRef<HTMLDivElement | null>(null);\n const { choiceIndicatorPosition,
|
|
1
|
+
{"version":3,"file":"TabSelector.mjs","names":[],"sources":["../../../../src/components/TabSelector/TabSelector.tsx"],"sourcesContent":["'use client';\n\nimport {\n type ItemSelectorOrientation,\n useItemSelector,\n} from '@hooks/useItemSelector';\nimport { cn } from '@utils/cn';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport {\n cloneElement,\n type HTMLAttributes,\n type MouseEvent,\n type ReactElement,\n useEffect,\n useRef,\n} from 'react';\n\nexport type TabSelectorColor =\n | 'primary'\n | 'secondary'\n | 'error'\n | 'neutral'\n | 'light'\n | 'dark'\n | 'text';\n\nconst tabSelectorVariant = cva(\n 'relative z-0 flex size-full flex-row items-center gap-2',\n {\n variants: {\n color: {\n primary: 'border-primary text-primary',\n secondary: 'border-secondary text-secondary',\n error: 'border-error bg-error text-error',\n neutral: 'border-neutral text-neutral',\n light: 'border-white text-white',\n dark: 'border-neutral-800 text-neutral-800',\n text: 'border-text text-text',\n },\n },\n defaultVariants: {\n color: 'primary',\n },\n }\n);\n\nconst indicatorVariant = cva(\n 'absolute -z-1 rounded-lg duration-300 ease-in-out motion-reduce:transition-none',\n {\n variants: {\n color: {\n primary: 'bg-primary/10 aria-selected:text-text',\n secondary: 'bg-secondary/10 aria-selected:text-text',\n error: 'bg-error/10 aria-selected:text-text',\n neutral: 'bg-neutral/10 aria-selected:text-white/10',\n light: 'bg-white/10 aria-selected:text-black',\n dark: 'bg-neutral-800/10 aria-selected:text-white',\n text: 'bg-text/10 aria-selected:text-text-opposite',\n },\n orientation: {\n horizontal: 'top-0 h-full w-auto transition-[left,width]',\n vertical: 'left-0 h-auto w-full transition-[top,height]',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n }\n);\n\nexport type TabSelectorItemProps = HTMLAttributes<HTMLElement> & {\n key: string | number;\n};\n\nexport type TabSelectorProps<T extends TabSelectorItemProps> = {\n tabs: ReactElement<T>[];\n selectedChoice: T['key'];\n onTabClick?: (choice: T['key']) => void;\n hoverable?: boolean;\n orientation?: ItemSelectorOrientation;\n} & HTMLAttributes<HTMLElement> &\n Omit<VariantProps<typeof tabSelectorVariant>, 'color'> & {\n color?: TabSelectorColor | `${TabSelectorColor}`;\n };\n\n/**\n *\n * Component that allows the user to select one of the provided choices.\n *\n * Example:\n * ```jsx\n * <TabSelector\n * selectedChoice=\"option1\"\n * onTabClick={(choice) => console.log(choice)}\n * hoverable={true}\n * >\n * <Button key=\"option1\"/>\n * <Button key=\"option2\"/>\n * <Button key=\"option3\"/>\n * </TabSelector>\n * ```\n */\nexport const TabSelector = <T extends TabSelectorItemProps>({\n tabs,\n selectedChoice,\n onTabClick,\n color = 'primary',\n hoverable = false,\n orientation = 'horizontal',\n className,\n}: TabSelectorProps<T>) => {\n const optionsRefs = useRef<HTMLElement[]>([]);\n const indicatorRef = useRef<HTMLDivElement | null>(null);\n const { choiceIndicatorPosition, scheduleMeasurement } = useItemSelector(\n optionsRefs,\n { isHoverable: hoverable, orientation }\n );\n\n useEffect(() => {\n scheduleMeasurement();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedChoice]);\n\n return (\n <div\n className={cn(\n tabSelectorVariant({\n color,\n }),\n className\n )}\n aria-orientation={orientation}\n aria-multiselectable=\"false\"\n role=\"tablist\"\n >\n {tabs.map((Tab, index) => {\n const key = Tab.key!;\n\n const isSelected = selectedChoice === key;\n\n return cloneElement(Tab, {\n key: key ?? index,\n role: 'tab',\n onClick: (e: MouseEvent<HTMLElement>) => {\n Tab.props?.onClick?.(e);\n onTabClick?.(key);\n },\n 'aria-selected': isSelected,\n 'data-active': isSelected as unknown as string,\n tabIndex: isSelected ? 0 : -1,\n ref: (el: HTMLElement) => {\n optionsRefs.current[index] = el!;\n },\n } as unknown as T);\n })}\n {choiceIndicatorPosition && (\n <div\n className={cn(\n indicatorVariant({\n color,\n orientation,\n })\n )}\n style={choiceIndicatorPosition}\n ref={indicatorRef}\n key={`${selectedChoice}${JSON.stringify(tabs.map((tab) => tab.key))}`}\n />\n )}\n </div>\n );\n};\n"],"mappings":";;;;;;;;;AA0BA,MAAM,qBAAqB,IACzB,2DACA;CACE,UAAU,EACR,OAAO;EACL,SAAS;EACT,WAAW;EACX,OAAO;EACP,SAAS;EACT,OAAO;EACP,MAAM;EACN,MAAM;CACR,EACF;CACA,iBAAiB,EACf,OAAO,UACT;AACF,CACF;AAEA,MAAM,mBAAmB,IACvB,mFACA;CACE,UAAU;EACR,OAAO;GACL,SAAS;GACT,WAAW;GACX,OAAO;GACP,SAAS;GACT,OAAO;GACP,MAAM;GACN,MAAM;EACR;EACA,aAAa;GACX,YAAY;GACZ,UAAU;EACZ;CACF;CACA,iBAAiB,EACf,aAAa,aACf;AACF,CACF;;;;;;;;;;;;;;;;;;AAkCA,MAAa,eAA+C,EAC1D,MACA,gBACA,YACA,QAAQ,WACR,YAAY,OACZ,cAAc,cACd,gBACyB;CACzB,MAAM,cAAc,OAAsB,CAAC,CAAC;CAC5C,MAAM,eAAe,OAA8B,IAAI;CACvD,MAAM,EAAE,yBAAyB,wBAAwB,gBACvD,aACA;EAAE,aAAa;EAAW;CAAY,CACxC;CAEA,gBAAgB;EACd,oBAAoB;CAEtB,GAAG,CAAC,cAAc,CAAC;CAEnB,OACE,qBAAC,OAAD;EACE,WAAW,GACT,mBAAmB,EACjB,MACF,CAAC,GACD,SACF;EACA,oBAAkB;EAClB,wBAAqB;EACrB,MAAK;YATP,CAWG,KAAK,KAAK,KAAK,UAAU;GACxB,MAAM,MAAM,IAAI;GAEhB,MAAM,aAAa,mBAAmB;GAEtC,OAAO,aAAa,KAAK;IACvB,KAAK,OAAO;IACZ,MAAM;IACN,UAAU,MAA+B;KACvC,IAAI,OAAO,UAAU,CAAC;KACtB,aAAa,GAAG;IAClB;IACA,iBAAiB;IACjB,eAAe;IACf,UAAU,aAAa,IAAI;IAC3B,MAAM,OAAoB;KACxB,YAAY,QAAQ,SAAS;IAC/B;GACF,CAAiB;EACnB,CAAC,GACA,2BACC,oBAAC,OAAD;GACE,WAAW,GACT,iBAAiB;IACf;IACA;GACF,CAAC,CACH;GACA,OAAO;GACP,KAAK;EAEN,GADM,GAAG,iBAAiB,KAAK,UAAU,KAAK,KAAK,QAAQ,IAAI,GAAG,CAAC,GACnE,CAEA;;AAET"}
|
|
@@ -79,11 +79,13 @@ const AutoSizedTextArea = ({ className, autoSize = true, onChange, maxRows = 999
|
|
|
79
79
|
textAreaStyle.height = `${Math.max(Math.min(scrollHeight, maxHeight), 36)}px`;
|
|
80
80
|
};
|
|
81
81
|
useLayoutEffect(() => {
|
|
82
|
+
if (!Boolean(props.value ?? props.defaultValue)) return;
|
|
82
83
|
adjustHeight();
|
|
83
84
|
}, [
|
|
84
85
|
props.value,
|
|
85
86
|
props.defaultValue,
|
|
86
|
-
|
|
87
|
+
autoSize,
|
|
88
|
+
maxRows
|
|
87
89
|
]);
|
|
88
90
|
const handleChange = (e) => {
|
|
89
91
|
onChange?.(e);
|
|
@@ -91,7 +93,6 @@ const AutoSizedTextArea = ({ className, autoSize = true, onChange, maxRows = 999
|
|
|
91
93
|
};
|
|
92
94
|
const setRef = (el) => {
|
|
93
95
|
textAreaRef.current = el;
|
|
94
|
-
if (el) adjustHeight();
|
|
95
96
|
};
|
|
96
97
|
return /* @__PURE__ */ jsx(TextArea, {
|
|
97
98
|
ref: setRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoSizeTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/AutoSizeTextArea.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n type ChangeEventHandler,\n type FC,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n} from 'react';\nimport { TextArea, type TextAreaProps } from './TextArea';\n\n/**\n * Props for the AutoSizedTextArea component.\n *\n * Extends TextAreaProps with auto-sizing functionality and row limitations.\n *\n * @example\n * ```tsx\n * // Auto-sizing textarea that grows with content\n * <AutoSizedTextArea\n * placeholder=\"Start typing and watch it grow...\"\n * autoSize={true}\n * maxRows={10}\n * />\n *\n * // Limited height with scroll when exceeded\n * <AutoSizedTextArea\n * value={longText}\n * onChange={handleChange}\n * autoSize={true}\n * maxRows={5}\n * className=\"max-h-30\"\n * />\n *\n * // Disable auto-sizing for fixed height\n * <AutoSizedTextArea\n * autoSize={false}\n * rows={3}\n * placeholder=\"Fixed height textarea\"\n * />\n * ```\n */\nexport type AutoSizedTextAreaProps = TextAreaProps & {\n /** Whether to automatically adjust height based on content */\n autoSize?: boolean;\n /** Maximum number of rows before scrolling is enabled */\n maxRows?: number;\n};\n\nconst LINE_HEIGHT = 24; // px\nconst LINE_PADDING = 12; // px\n\n/**\n * AutoSizedTextArea Component\n *\n * An enhanced textarea that automatically adjusts its height based on content,\n * providing a smooth user experience for variable-length text input.\n *\n * ## Features\n * - **Auto-Sizing**: Dynamically grows and shrinks based on content\n * - **Row Limits**: Configurable maximum rows before scrolling\n * - **Smooth Transitions**: Seamless height adjustments as user types\n * - **Scroll Management**: Automatic overflow handling when max height reached\n * - **Performance Optimized**: Efficient height calculations and updates\n *\n * ## Technical Details\n * - Line height: 24px with 12px padding\n * - Height calculation: `scrollHeight` vs `maxRows * lineHeight + padding`\n * - Resize: Disabled when auto-sizing is active for smooth experience\n * - Ref forwarding: Supports imperative access to textarea element\n *\n * ## Use Cases\n * - Chat message composition with dynamic sizing\n * - Comment forms that expand with content\n * - Note-taking interfaces with variable length\n * - Social media post creation\n * - Code snippet input with growth limits\n *\n * @example\n * ```tsx\n * // Chat-style auto-expanding textarea\n * const [message, setMessage] = useState('');\n *\n * <AutoSizedTextArea\n * value={message}\n * onChange={(e) => setMessage(e.target.value)}\n * placeholder=\"Type your message...\"\n * autoSize={true}\n * maxRows={8}\n * className=\"min-h-10\"\n * onKeyDown={(e) => {\n * if (e.key === 'Enter' && !e.shiftKey) {\n * e.preventDefault();\n * sendMessage(message);\n * setMessage('');\n * }\n * }}\n * />\n *\n * // Note-taking with generous height limits\n * <AutoSizedTextArea\n * defaultValue={note.content}\n * onChange={handleNoteChange}\n * placeholder=\"Write your notes here...\"\n * autoSize={true}\n * maxRows={20}\n * variant=\"default\"\n * />\n * ```\n */\nexport const AutoSizedTextArea: FC<AutoSizedTextAreaProps> = ({\n className,\n autoSize = true,\n onChange,\n maxRows = 999,\n ref,\n ...props\n}) => {\n const textAreaRef = useRef<HTMLTextAreaElement | null>(null);\n\n useImperativeHandle(ref, () => textAreaRef.current!);\n\n const adjustHeight = () => {\n const textAreaEl = textAreaRef.current;\n\n if (!textAreaEl || !autoSize) return;\n\n const textAreaStyle = textAreaEl.style;\n\n // Reset height to get accurate scrollHeight\n textAreaStyle.height = 'auto';\n const scrollHeight = textAreaEl.scrollHeight;\n const maxHeight = LINE_HEIGHT * maxRows + LINE_PADDING;\n const minHeight = LINE_HEIGHT + LINE_PADDING;\n\n // Set the new height\n textAreaStyle.height = `${Math.max(Math.min(scrollHeight, maxHeight), minHeight)}px`;\n };\n\n useLayoutEffect(() => {\n adjustHeight();\n }, [props.value, props.defaultValue,
|
|
1
|
+
{"version":3,"file":"AutoSizeTextArea.mjs","names":[],"sources":["../../../../src/components/TextArea/AutoSizeTextArea.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport {\n type ChangeEventHandler,\n type FC,\n useImperativeHandle,\n useLayoutEffect,\n useRef,\n} from 'react';\nimport { TextArea, type TextAreaProps } from './TextArea';\n\n/**\n * Props for the AutoSizedTextArea component.\n *\n * Extends TextAreaProps with auto-sizing functionality and row limitations.\n *\n * @example\n * ```tsx\n * // Auto-sizing textarea that grows with content\n * <AutoSizedTextArea\n * placeholder=\"Start typing and watch it grow...\"\n * autoSize={true}\n * maxRows={10}\n * />\n *\n * // Limited height with scroll when exceeded\n * <AutoSizedTextArea\n * value={longText}\n * onChange={handleChange}\n * autoSize={true}\n * maxRows={5}\n * className=\"max-h-30\"\n * />\n *\n * // Disable auto-sizing for fixed height\n * <AutoSizedTextArea\n * autoSize={false}\n * rows={3}\n * placeholder=\"Fixed height textarea\"\n * />\n * ```\n */\nexport type AutoSizedTextAreaProps = TextAreaProps & {\n /** Whether to automatically adjust height based on content */\n autoSize?: boolean;\n /** Maximum number of rows before scrolling is enabled */\n maxRows?: number;\n};\n\nconst LINE_HEIGHT = 24; // px\nconst LINE_PADDING = 12; // px\n\n/**\n * AutoSizedTextArea Component\n *\n * An enhanced textarea that automatically adjusts its height based on content,\n * providing a smooth user experience for variable-length text input.\n *\n * ## Features\n * - **Auto-Sizing**: Dynamically grows and shrinks based on content\n * - **Row Limits**: Configurable maximum rows before scrolling\n * - **Smooth Transitions**: Seamless height adjustments as user types\n * - **Scroll Management**: Automatic overflow handling when max height reached\n * - **Performance Optimized**: Efficient height calculations and updates\n *\n * ## Technical Details\n * - Line height: 24px with 12px padding\n * - Height calculation: `scrollHeight` vs `maxRows * lineHeight + padding`\n * - Resize: Disabled when auto-sizing is active for smooth experience\n * - Ref forwarding: Supports imperative access to textarea element\n *\n * ## Use Cases\n * - Chat message composition with dynamic sizing\n * - Comment forms that expand with content\n * - Note-taking interfaces with variable length\n * - Social media post creation\n * - Code snippet input with growth limits\n *\n * @example\n * ```tsx\n * // Chat-style auto-expanding textarea\n * const [message, setMessage] = useState('');\n *\n * <AutoSizedTextArea\n * value={message}\n * onChange={(e) => setMessage(e.target.value)}\n * placeholder=\"Type your message...\"\n * autoSize={true}\n * maxRows={8}\n * className=\"min-h-10\"\n * onKeyDown={(e) => {\n * if (e.key === 'Enter' && !e.shiftKey) {\n * e.preventDefault();\n * sendMessage(message);\n * setMessage('');\n * }\n * }}\n * />\n *\n * // Note-taking with generous height limits\n * <AutoSizedTextArea\n * defaultValue={note.content}\n * onChange={handleNoteChange}\n * placeholder=\"Write your notes here...\"\n * autoSize={true}\n * maxRows={20}\n * variant=\"default\"\n * />\n * ```\n */\nexport const AutoSizedTextArea: FC<AutoSizedTextAreaProps> = ({\n className,\n autoSize = true,\n onChange,\n maxRows = 999,\n ref,\n ...props\n}) => {\n const textAreaRef = useRef<HTMLTextAreaElement | null>(null);\n\n useImperativeHandle(ref, () => textAreaRef.current!);\n\n const adjustHeight = () => {\n const textAreaEl = textAreaRef.current;\n\n if (!textAreaEl || !autoSize) return;\n\n const textAreaStyle = textAreaEl.style;\n\n // Reset height to get accurate scrollHeight\n textAreaStyle.height = 'auto';\n const scrollHeight = textAreaEl.scrollHeight;\n const maxHeight = LINE_HEIGHT * maxRows + LINE_PADDING;\n const minHeight = LINE_HEIGHT + LINE_PADDING;\n\n // Set the new height\n textAreaStyle.height = `${Math.max(Math.min(scrollHeight, maxHeight), minHeight)}px`;\n };\n\n /*\n * `adjustHeight` writes a style and then reads `scrollHeight`, which forces\n * the browser to lay the page out there and then. Depending on the function\n * identity — recreated every render — repeated that reflow on every render of\n * every ancestor; the height only ever changes with the content, so that is\n * what this watches.\n *\n * An empty field is skipped entirely: its height is already the one line the\n * CSS gives it, so measuring only buys a reflow. That is the common case for\n * a chat composer sitting unopened on a large page.\n */\n useLayoutEffect(() => {\n const hasContent = Boolean(props.value ?? props.defaultValue);\n if (!hasContent) return;\n\n adjustHeight();\n }, [props.value, props.defaultValue, autoSize, maxRows]);\n\n const handleChange: ChangeEventHandler<HTMLTextAreaElement> = (e) => {\n onChange?.(e);\n adjustHeight();\n };\n\n const setRef = (el: HTMLTextAreaElement | null) => {\n textAreaRef.current = el;\n };\n\n return (\n <TextArea\n ref={setRef}\n onChange={handleChange}\n className={cn('overflow-y-auto', autoSize && 'resize-none', className)}\n {...props}\n />\n );\n};\n"],"mappings":";;;;;;;;AAkDA,MAAM,cAAc;AACpB,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4DrB,MAAa,qBAAiD,EAC5D,WACA,WAAW,MACX,UACA,UAAU,KACV,KACA,GAAG,YACC;CACJ,MAAM,cAAc,OAAmC,IAAI;CAE3D,oBAAoB,WAAW,YAAY,OAAQ;CAEnD,MAAM,qBAAqB;EACzB,MAAM,aAAa,YAAY;EAE/B,IAAI,CAAC,cAAc,CAAC,UAAU;EAE9B,MAAM,gBAAgB,WAAW;EAGjC,cAAc,SAAS;EACvB,MAAM,eAAe,WAAW;EAChC,MAAM,YAAY,cAAc,UAAU;EAI1C,cAAc,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,cAAc,SAAS,GAAG,EAAS,EAAE;CACnF;CAaA,sBAAsB;EAEpB,IAAI,CADe,QAAQ,MAAM,SAAS,MAAM,YAClC,GAAG;EAEjB,aAAa;CACf,GAAG;EAAC,MAAM;EAAO,MAAM;EAAc;EAAU;CAAO,CAAC;CAEvD,MAAM,gBAAyD,MAAM;EACnE,WAAW,CAAC;EACZ,aAAa;CACf;CAEA,MAAM,UAAU,OAAmC;EACjD,YAAY,UAAU;CACxB;CAEA,OACE,oBAAC,UAAD;EACE,KAAK;EACL,UAAU;EACV,WAAW,GAAG,mBAAmB,YAAY,eAAe,SAAS;EACrE,GAAI;CACL;AAEL"}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { Button } from "../Button/Button.mjs";
|
|
4
4
|
import { Container } from "../Container/index.mjs";
|
|
5
5
|
import { DropDown } from "../DropDown/index.mjs";
|
|
6
|
-
import { useState } from "react";
|
|
7
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { CircleDashed, Moon, Sun } from "lucide-react";
|
|
9
8
|
|
|
@@ -13,20 +12,13 @@ const ButtonItem = ({ Icon, children, ...props }) => /* @__PURE__ */ jsx("div",
|
|
|
13
12
|
children: /* @__PURE__ */ jsx(Button, {
|
|
14
13
|
className: "w-full cursor-pointer rounded-lg p-1 text-left hover:bg-text/10 focus:bg-text-opposite/20 focus:outline-hidden disabled:text-white/25",
|
|
15
14
|
Icon,
|
|
16
|
-
"data-mode": "system",
|
|
17
15
|
role: "option",
|
|
18
16
|
variant: "none",
|
|
19
17
|
...props,
|
|
20
18
|
children
|
|
21
19
|
})
|
|
22
20
|
});
|
|
23
|
-
const DesktopThemeSwitcher = ({ theme, setTheme
|
|
24
|
-
const [mode, setMode] = useState(systemTheme === theme ? "system" : theme);
|
|
25
|
-
const switchMode = (mode) => {
|
|
26
|
-
if (mode === "system") setTheme(systemTheme ?? "light");
|
|
27
|
-
else setTheme(mode);
|
|
28
|
-
setMode(mode);
|
|
29
|
-
};
|
|
21
|
+
const DesktopThemeSwitcher = ({ theme, setTheme }) => {
|
|
30
22
|
const panelIdentifier = "theme-switcher";
|
|
31
23
|
return /* @__PURE__ */ jsxs(DropDown, {
|
|
32
24
|
identifier: panelIdentifier,
|
|
@@ -35,9 +27,9 @@ const DesktopThemeSwitcher = ({ theme, setTheme, systemTheme }) => {
|
|
|
35
27
|
identifier: panelIdentifier,
|
|
36
28
|
"aria-label": "Theme selector",
|
|
37
29
|
children: [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
30
|
+
theme === "system" && /* @__PURE__ */ jsx(CircleDashed, { "data-mode": "system" }),
|
|
31
|
+
theme === "light" && /* @__PURE__ */ jsx(Sun, { "data-mode": "light" }),
|
|
32
|
+
theme === "dark" && /* @__PURE__ */ jsx(Moon, { "data-mode": "dark" })
|
|
41
33
|
]
|
|
42
34
|
}), /* @__PURE__ */ jsx(DropDown.Panel, {
|
|
43
35
|
identifier: panelIdentifier,
|
|
@@ -49,22 +41,22 @@ const DesktopThemeSwitcher = ({ theme, setTheme, systemTheme }) => {
|
|
|
49
41
|
children: [
|
|
50
42
|
/* @__PURE__ */ jsx(ButtonItem, {
|
|
51
43
|
Icon: CircleDashed,
|
|
52
|
-
onClick: () =>
|
|
53
|
-
isActive:
|
|
44
|
+
onClick: () => setTheme("system"),
|
|
45
|
+
isActive: theme === "system",
|
|
54
46
|
label: "Restore to system mode",
|
|
55
47
|
children: "System"
|
|
56
48
|
}),
|
|
57
49
|
/* @__PURE__ */ jsx(ButtonItem, {
|
|
58
50
|
Icon: Sun,
|
|
59
|
-
onClick: () =>
|
|
60
|
-
isActive:
|
|
51
|
+
onClick: () => setTheme("light"),
|
|
52
|
+
isActive: theme === "light",
|
|
61
53
|
label: "Switch to light mode",
|
|
62
54
|
children: "Light"
|
|
63
55
|
}),
|
|
64
56
|
/* @__PURE__ */ jsx(ButtonItem, {
|
|
65
57
|
Icon: Moon,
|
|
66
|
-
onClick: () =>
|
|
67
|
-
isActive:
|
|
58
|
+
onClick: () => setTheme("dark"),
|
|
59
|
+
isActive: theme === "dark",
|
|
68
60
|
label: "Switch to dark mode",
|
|
69
61
|
children: "Dark"
|
|
70
62
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DesktopThemeSwitcher.mjs","names":[],"sources":["../../../../src/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.tsx"],"sourcesContent":["'use client';\n\nimport { CircleDashed, Moon, Sun } from 'lucide-react';\nimport {
|
|
1
|
+
{"version":3,"file":"DesktopThemeSwitcher.mjs","names":[],"sources":["../../../../src/components/ThemeSwitcherDropDown/DesktopThemeSwitcher.tsx"],"sourcesContent":["'use client';\n\nimport { CircleDashed, Moon, Sun } from 'lucide-react';\nimport type { FC } from 'react';\nimport { Button, type ButtonProps } from '../Button';\nimport { Container } from '../Container';\nimport { DropDown } from '../DropDown';\nimport type { Modes } from './types';\n\nconst ButtonItem: FC<ButtonProps> = ({ Icon, children, ...props }) => (\n <div className=\"relative w-full p-0.5\">\n <Button\n className=\"w-full cursor-pointer rounded-lg p-1 text-left hover:bg-text/10 focus:bg-text-opposite/20 focus:outline-hidden disabled:text-white/25\"\n Icon={Icon}\n role=\"option\"\n variant=\"none\"\n {...props}\n >\n {children}\n </Button>\n </div>\n);\n\ntype DesktopThemeSwitcherProps = {\n /** Preference currently persisted — `system` while following the OS. */\n theme: Modes;\n setTheme: (theme: Modes) => void;\n};\n\nexport const DesktopThemeSwitcher: FC<DesktopThemeSwitcherProps> = ({\n theme,\n setTheme,\n}) => {\n const panelIdentifier = 'theme-switcher';\n\n return (\n <DropDown identifier={panelIdentifier}>\n <DropDown.Trigger\n className=\"p-2\"\n identifier={panelIdentifier}\n aria-label=\"Theme selector\"\n >\n {theme === 'system' && <CircleDashed data-mode=\"system\" />}\n {theme === 'light' && <Sun data-mode=\"light\" />}\n {theme === 'dark' && <Moon data-mode=\"dark\" />}\n </DropDown.Trigger>\n\n <DropDown.Panel identifier={panelIdentifier} isFocusable isOverable>\n <Container className=\"min-w-25 items-start p-1\" separator=\"y\">\n <ButtonItem\n Icon={CircleDashed}\n onClick={() => setTheme('system')}\n isActive={theme === 'system'}\n label=\"Restore to system mode\"\n >\n System\n </ButtonItem>\n <ButtonItem\n Icon={Sun}\n onClick={() => setTheme('light')}\n isActive={theme === 'light'}\n label=\"Switch to light mode\"\n >\n Light\n </ButtonItem>\n <ButtonItem\n Icon={Moon}\n onClick={() => setTheme('dark')}\n isActive={theme === 'dark'}\n label=\"Switch to dark mode\"\n >\n Dark\n </ButtonItem>\n </Container>\n </DropDown.Panel>\n </DropDown>\n );\n};\n"],"mappings":";;;;;;;;;AASA,MAAM,cAA+B,EAAE,MAAM,UAAU,GAAG,YACxD,oBAAC,OAAD;CAAK,WAAU;WACb,oBAAC,QAAD;EACE,WAAU;EACJ;EACN,MAAK;EACL,SAAQ;EACR,GAAI;EAEH;CACK;AACL;AASP,MAAa,wBAAuD,EAClE,OACA,eACI;CACJ,MAAM,kBAAkB;CAExB,OACE,qBAAC,UAAD;EAAU,YAAY;YAAtB,CACE,qBAAC,SAAS,SAAV;GACE,WAAU;GACV,YAAY;GACZ,cAAW;aAHb;IAKG,UAAU,YAAY,oBAAC,cAAD,EAAc,aAAU,SAAU;IACxD,UAAU,WAAW,oBAAC,KAAD,EAAK,aAAU,QAAS;IAC7C,UAAU,UAAU,oBAAC,MAAD,EAAM,aAAU,OAAQ;GAC7B;MAElB,oBAAC,SAAS,OAAV;GAAgB,YAAY;GAAiB;GAAY;aACvD,qBAAC,WAAD;IAAW,WAAU;IAA2B,WAAU;cAA1D;KACE,oBAAC,YAAD;MACE,MAAM;MACN,eAAe,SAAS,QAAQ;MAChC,UAAU,UAAU;MACpB,OAAM;gBACP;KAEW;KACZ,oBAAC,YAAD;MACE,MAAM;MACN,eAAe,SAAS,OAAO;MAC/B,UAAU,UAAU;MACpB,OAAM;gBACP;KAEW;KACZ,oBAAC,YAAD;MACE,MAAM;MACN,eAAe,SAAS,MAAM;MAC9B,UAAU,UAAU;MACpB,OAAM;gBACP;KAEW;IACH;;EACG,EACR;;AAEd"}
|
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { cn } from "../../utils/cn.mjs";
|
|
4
|
-
import { useState } from "react";
|
|
5
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
5
|
import { CircleDashed, Moon, Sun } from "lucide-react";
|
|
7
6
|
|
|
8
7
|
//#region src/components/ThemeSwitcherDropDown/MobileThemeSwitcher.tsx
|
|
8
|
+
/** Order a tap cycles through. */
|
|
9
|
+
const modeCycle = [
|
|
10
|
+
"system",
|
|
11
|
+
"light",
|
|
12
|
+
"dark"
|
|
13
|
+
];
|
|
14
|
+
const getNextMode = (mode) => modeCycle[(modeCycle.indexOf(mode) + 1) % modeCycle.length];
|
|
9
15
|
const getIconStyle = ({ isCurrentMode, isNextMode }) => cn(`absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2`, `opacity-0 transition-opacity duration-300 ease-in-out`, isCurrentMode && `opacity-100 group-hover:opacity-0`, isNextMode && `group-hover:opacity-100`);
|
|
10
|
-
const MobileThemeSwitcher = ({ theme,
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const nextMode = mode === "system" ? theme === "dark" ? "light" : "dark" : isThemeSystemTheme ? "system" : mode === "light" ? "dark" : "light";
|
|
14
|
-
const toggleMode = () => {
|
|
15
|
-
if (nextMode === "system") setTheme(systemTheme ?? "light");
|
|
16
|
-
else setTheme(nextMode);
|
|
17
|
-
setMode(nextMode);
|
|
18
|
-
};
|
|
16
|
+
const MobileThemeSwitcher = ({ theme, setTheme }) => {
|
|
17
|
+
const nextMode = getNextMode(theme);
|
|
18
|
+
const toggleMode = () => setTheme(nextMode);
|
|
19
19
|
return /* @__PURE__ */ jsxs("button", {
|
|
20
|
+
type: "button",
|
|
20
21
|
className: "group relative size-10",
|
|
21
22
|
"aria-label": "Theme selector",
|
|
22
23
|
children: [
|
|
23
24
|
/* @__PURE__ */ jsx(CircleDashed, {
|
|
24
25
|
className: getIconStyle({
|
|
25
|
-
isCurrentMode:
|
|
26
|
+
isCurrentMode: theme === "system",
|
|
26
27
|
isNextMode: nextMode === "system"
|
|
27
28
|
}),
|
|
28
29
|
onClick: toggleMode,
|
|
@@ -30,7 +31,7 @@ const MobileThemeSwitcher = ({ theme, systemTheme, setTheme }) => {
|
|
|
30
31
|
}),
|
|
31
32
|
/* @__PURE__ */ jsx(Moon, {
|
|
32
33
|
className: getIconStyle({
|
|
33
|
-
isCurrentMode:
|
|
34
|
+
isCurrentMode: theme === "light",
|
|
34
35
|
isNextMode: nextMode === "light"
|
|
35
36
|
}),
|
|
36
37
|
onClick: toggleMode,
|
|
@@ -38,7 +39,7 @@ const MobileThemeSwitcher = ({ theme, systemTheme, setTheme }) => {
|
|
|
38
39
|
}),
|
|
39
40
|
/* @__PURE__ */ jsx(Sun, {
|
|
40
41
|
className: getIconStyle({
|
|
41
|
-
isCurrentMode:
|
|
42
|
+
isCurrentMode: theme === "dark",
|
|
42
43
|
isNextMode: nextMode === "dark"
|
|
43
44
|
}),
|
|
44
45
|
onClick: toggleMode,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MobileThemeSwitcher.mjs","names":[],"sources":["../../../../src/components/ThemeSwitcherDropDown/MobileThemeSwitcher.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { CircleDashed, Moon, Sun } from 'lucide-react';\nimport {
|
|
1
|
+
{"version":3,"file":"MobileThemeSwitcher.mjs","names":[],"sources":["../../../../src/components/ThemeSwitcherDropDown/MobileThemeSwitcher.tsx"],"sourcesContent":["'use client';\n\nimport { cn } from '@utils/cn';\nimport { CircleDashed, Moon, Sun } from 'lucide-react';\nimport type { FC } from 'react';\nimport type { Modes } from './types';\n\ntype MobileThemeSwitcherProps = {\n /** Preference currently persisted — `system` while following the OS. */\n theme: Modes;\n setTheme: (theme: Modes) => void;\n};\n\n/** Order a tap cycles through. */\nconst modeCycle: Modes[] = ['system', 'light', 'dark'];\n\nconst getNextMode = (mode: Modes): Modes =>\n modeCycle[(modeCycle.indexOf(mode) + 1) % modeCycle.length];\n\nconst getIconStyle = ({\n isCurrentMode,\n isNextMode,\n}: {\n isCurrentMode: boolean;\n isNextMode: boolean;\n}) =>\n cn(\n `absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2`,\n `opacity-0 transition-opacity duration-300 ease-in-out`,\n isCurrentMode && `opacity-100 group-hover:opacity-0`,\n isNextMode && `group-hover:opacity-100`\n );\n\nexport const MobileThemeSwitcher: FC<MobileThemeSwitcherProps> = ({\n theme,\n setTheme,\n}) => {\n const nextMode = getNextMode(theme);\n const toggleMode = () => setTheme(nextMode);\n\n return (\n <button\n type=\"button\"\n className=\"group relative size-10\"\n aria-label=\"Theme selector\"\n >\n <CircleDashed\n className={getIconStyle({\n isCurrentMode: theme === 'system',\n isNextMode: nextMode === 'system',\n })}\n onClick={toggleMode}\n data-mode=\"system\"\n />\n\n <Moon\n className={getIconStyle({\n isCurrentMode: theme === 'light',\n isNextMode: nextMode === 'light',\n })}\n onClick={toggleMode}\n data-mode=\"light\"\n />\n\n <Sun\n className={getIconStyle({\n isCurrentMode: theme === 'dark',\n isNextMode: nextMode === 'dark',\n })}\n onClick={toggleMode}\n data-mode=\"dark\"\n />\n </button>\n );\n};\n"],"mappings":";;;;;;;;AAcA,MAAM,YAAqB;CAAC;CAAU;CAAS;AAAM;AAErD,MAAM,eAAe,SACnB,WAAW,UAAU,QAAQ,IAAI,IAAI,KAAK,UAAU;AAEtD,MAAM,gBAAgB,EACpB,eACA,iBAKA,GACE,+DACA,yDACA,iBAAiB,qCACjB,cAAc,yBAChB;AAEF,MAAa,uBAAqD,EAChE,OACA,eACI;CACJ,MAAM,WAAW,YAAY,KAAK;CAClC,MAAM,mBAAmB,SAAS,QAAQ;CAE1C,OACE,qBAAC,UAAD;EACE,MAAK;EACL,WAAU;EACV,cAAW;YAHb;GAKE,oBAAC,cAAD;IACE,WAAW,aAAa;KACtB,eAAe,UAAU;KACzB,YAAY,aAAa;IAC3B,CAAC;IACD,SAAS;IACT,aAAU;GACX;GAED,oBAAC,MAAD;IACE,WAAW,aAAa;KACtB,eAAe,UAAU;KACzB,YAAY,aAAa;IAC3B,CAAC;IACD,SAAS;IACT,aAAU;GACX;GAED,oBAAC,KAAD;IACE,WAAW,aAAa;KACtB,eAAe,UAAU;KACzB,YAAY,aAAa;IAC3B,CAAC;IACD,SAAS;IACT,aAAU;GACX;EACK;;AAEZ"}
|
|
@@ -20,7 +20,7 @@ import { Radio, radioVariants } from "./Input/Radio.mjs";
|
|
|
20
20
|
import { SearchInput } from "./Input/SearchInput.mjs";
|
|
21
21
|
import { Browser } from "./Browser/Browser.mjs";
|
|
22
22
|
import { KeyboardShortcut } from "./KeyboardShortcut/KeyboardShortcut.mjs";
|
|
23
|
-
import { Detail, PopoverStatic } from "./Popover/static.mjs";
|
|
23
|
+
import { Detail, PopoverStatic, usePopoverIds } from "./Popover/static.mjs";
|
|
24
24
|
import { Popover } from "./Popover/dynamic.mjs";
|
|
25
25
|
import { Carousel } from "./Carousel/index.mjs";
|
|
26
26
|
import { CollapsibleTable } from "./CollapsibleTable/CollapsibleTable.mjs";
|
|
@@ -105,7 +105,7 @@ import { Footer } from "./Footer/index.mjs";
|
|
|
105
105
|
import { HeightResizer } from "./HeightResizer/index.mjs";
|
|
106
106
|
import { HideShow } from "./HideShow/index.mjs";
|
|
107
107
|
import { HTMLRenderer, getIntlayerHTMLOptions } from "./HTMLRender/HTMLRender.mjs";
|
|
108
|
-
import { CodeBlock, CodeDefault } from "./IDE/CodeBlockClient.mjs";
|
|
108
|
+
import { CodeBlock, CodeDefault, CodeHighlighted, shikiWrapperClassName } from "./IDE/CodeBlockClient.mjs";
|
|
109
109
|
import { Code } from "./IDE/Code.mjs";
|
|
110
110
|
import { FileList } from "./IDE/FileList.mjs";
|
|
111
111
|
import { WithResizer } from "./WithResizer/index.mjs";
|
|
@@ -141,4 +141,4 @@ import { Terminal } from "./Terminal/Terminal.mjs";
|
|
|
141
141
|
import { DesktopThemeSwitcher } from "./ThemeSwitcherDropDown/DesktopThemeSwitcher.mjs";
|
|
142
142
|
import { MobileThemeSwitcher } from "./ThemeSwitcherDropDown/MobileThemeSwitcher.mjs";
|
|
143
143
|
|
|
144
|
-
export { Accordion, AutoCompleteTextarea, AutoSizedTextArea, Avatar, Badge, BitbucketLogo, Breadcrumb, Browser, Burger, Button, Carousel, Checkbox, ClickOutsideDiv, Code, CodeBlock, CodeDefault, CollapsibleTable, Command, CommandRoot, Container, ContentEditableTextArea, ContentEditorInput, ContentEditorTextArea, ContentSelector, CopyButton, CopyToClipboard, DesktopNavbar, DesktopThemeSwitcher, Detail, DictionaryCreationForm, DictionaryEditor, DictionaryFieldEditor, DiscordLogo, DotPattern, DropDown, EditableFieldInput, EditableFieldTextArea, ExpandCollapse, FacebookLogo, FileList, Flag, flags_exports as Flags, Footer, Form, AutoSizedTextAreaElement as FormAutoSizedTextArea, Button as FormButton, CheckboxElement as FormCheckbox, FormControl, FormDescription, EditableFieldInputElement as FormEditableFieldInput, EditableFieldTextAreaElement as FormEditableFieldTextArea, FormElement, FormField, InputElement as FormInput, InputPasswordElement as FormInputPassword, FormItem, FormLabelLayout as FormLabel, FormMessage, MultiSelectElement as FormMultiSelect, OTPElement as FormOTP, SearchInputElement as FormSearchInput, SelectElement as FormSelect, SwitchSelectorElement as FormSwitchSelector, TextAreaElement as FormTextArea, GitHubLogo, GitLabLogo, GoogleLogo, GridDistortionPattern, GridPattern, H1, H2, H3, H4, H5, H6, HTMLRenderer, HeightResizer, HideShow, Hr, IDE, InformationTag, Input, InputIndicator, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, InputPassword, InstagramLogo, KeyPathBreadcrumb, KeyboardScreenAdapter, KeyboardShortcut, Label, LanguageBackground, LanguageSection, Link, LinkedInLogo, Loader, LocaleSwitcher, LocaleSwitcherContent, LocaleSwitcherContentProvider, Logo, LogoTextOnly, LogoWithText, LogoWithTextBelow, MarkDownIframe, MarkdownEditor, MarkdownRenderer, MaxHeightSmoother, MaxWidthSmoother, MobileNavbar, MobileThemeSwitcher, Modal, MultiSelect, Navbar, NumberItemsSelector, OTPInput, OTPInputContext, Pagination, Popover, PopoverStatic, PressableSpan, ProductHuntLogo, Radio, RightDrawer, SaveForm, SearchInput, Select, SelectContent, SelectLabel, SelectSeparator, ShowingResultsNumberItems, SmartTable, SocialNetworks, Spotlight, Step, Steps, SwitchSelector, Tab, TabSelector, Table, Tag, Td, TechLogo, techLogos_exports as TechLogos, Terminal, TextArea, TextEditor, TextEditorContainer, Th, TiktokLogo, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Tr, VersionSwitcher, VersionSwitcherProvider, VerticalSwitchSelector, WithResizer, XLogo, YoutubeLogo, badgeVariants, baseMarkdownComponents, buttonVariants, checkIsExternalLink, checkboxVariants, choiceVariant, containerVariants, createEditorUploadFn, defaultChoices, drawerManager, getCapitals, getIntlayerHTMLOptions, getIntlayerMarkdownOptions, indicatorVariant, inputSlotVariants, inputVariants, isTextChildren, linkVariants, paginationVariants, radioVariants, reducer, switchSelectorVariant, toast, toastVariants, traceKeys, useContentEditable, useCopyToClipboard, useForm, useFormField, useLocaleSwitcherContent, usePasswordManagerBadge, usePrevious, useRightDrawer, useToast, useVersionSwitcher };
|
|
144
|
+
export { Accordion, AutoCompleteTextarea, AutoSizedTextArea, Avatar, Badge, BitbucketLogo, Breadcrumb, Browser, Burger, Button, Carousel, Checkbox, ClickOutsideDiv, Code, CodeBlock, CodeDefault, CodeHighlighted, CollapsibleTable, Command, CommandRoot, Container, ContentEditableTextArea, ContentEditorInput, ContentEditorTextArea, ContentSelector, CopyButton, CopyToClipboard, DesktopNavbar, DesktopThemeSwitcher, Detail, DictionaryCreationForm, DictionaryEditor, DictionaryFieldEditor, DiscordLogo, DotPattern, DropDown, EditableFieldInput, EditableFieldTextArea, ExpandCollapse, FacebookLogo, FileList, Flag, flags_exports as Flags, Footer, Form, AutoSizedTextAreaElement as FormAutoSizedTextArea, Button as FormButton, CheckboxElement as FormCheckbox, FormControl, FormDescription, EditableFieldInputElement as FormEditableFieldInput, EditableFieldTextAreaElement as FormEditableFieldTextArea, FormElement, FormField, InputElement as FormInput, InputPasswordElement as FormInputPassword, FormItem, FormLabelLayout as FormLabel, FormMessage, MultiSelectElement as FormMultiSelect, OTPElement as FormOTP, SearchInputElement as FormSearchInput, SelectElement as FormSelect, SwitchSelectorElement as FormSwitchSelector, TextAreaElement as FormTextArea, GitHubLogo, GitLabLogo, GoogleLogo, GridDistortionPattern, GridPattern, H1, H2, H3, H4, H5, H6, HTMLRenderer, HeightResizer, HideShow, Hr, IDE, InformationTag, Input, InputIndicator, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, InputPassword, InstagramLogo, KeyPathBreadcrumb, KeyboardScreenAdapter, KeyboardShortcut, Label, LanguageBackground, LanguageSection, Link, LinkedInLogo, Loader, LocaleSwitcher, LocaleSwitcherContent, LocaleSwitcherContentProvider, Logo, LogoTextOnly, LogoWithText, LogoWithTextBelow, MarkDownIframe, MarkdownEditor, MarkdownRenderer, MaxHeightSmoother, MaxWidthSmoother, MobileNavbar, MobileThemeSwitcher, Modal, MultiSelect, Navbar, NumberItemsSelector, OTPInput, OTPInputContext, Pagination, Popover, PopoverStatic, PressableSpan, ProductHuntLogo, Radio, RightDrawer, SaveForm, SearchInput, Select, SelectContent, SelectLabel, SelectSeparator, ShowingResultsNumberItems, SmartTable, SocialNetworks, Spotlight, Step, Steps, SwitchSelector, Tab, TabSelector, Table, Tag, Td, TechLogo, techLogos_exports as TechLogos, Terminal, TextArea, TextEditor, TextEditorContainer, Th, TiktokLogo, Toast, ToastAction, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Tr, VersionSwitcher, VersionSwitcherProvider, VerticalSwitchSelector, WithResizer, XLogo, YoutubeLogo, badgeVariants, baseMarkdownComponents, buttonVariants, checkIsExternalLink, checkboxVariants, choiceVariant, containerVariants, createEditorUploadFn, defaultChoices, drawerManager, getCapitals, getIntlayerHTMLOptions, getIntlayerMarkdownOptions, indicatorVariant, inputSlotVariants, inputVariants, isTextChildren, linkVariants, paginationVariants, radioVariants, reducer, shikiWrapperClassName, switchSelectorVariant, toast, toastVariants, traceKeys, useContentEditable, useCopyToClipboard, useForm, useFormField, useLocaleSwitcherContent, usePasswordManagerBadge, usePopoverIds, usePrevious, useRightDrawer, useToast, useVersionSwitcher };
|
package/dist/esm/hooks/index.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { useScrollY } from "./useScrollY.mjs";
|
|
1
|
+
import { calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice } from "./useDevice.mjs";
|
|
2
|
+
import { useItemSelector } from "./useItemSelector.mjs";
|
|
4
3
|
import { usePersistedStore } from "./usePersistedStore.mjs";
|
|
4
|
+
import { useGetElementById } from "./useGetElementById.mjs";
|
|
5
|
+
import { useGetElementOrWindow } from "./useGetElementOrWindow.mjs";
|
|
5
6
|
import { useHorizontalSwipe } from "./useHorizontalSwipe.mjs";
|
|
6
|
-
import { useItemSelector } from "./useItemSelector.mjs";
|
|
7
|
-
import { calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice } from "./useDevice.mjs";
|
|
8
|
-
import { useSearch } from "./useSearch.mjs";
|
|
9
7
|
import { useIsMounted } from "./useIsMounted.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import { useIsDarkMode } from "./useIsDarkMode.mjs";
|
|
8
|
+
import { useKeyboardDetector } from "./useKeyboardDetector.mjs";
|
|
12
9
|
import { useScreenWidth } from "./useScreenWidth.mjs";
|
|
13
10
|
import { useScrollBlockage } from "./useScrollBlockage/index.mjs";
|
|
14
11
|
import { useScrollDetection } from "./useScrollDetection.mjs";
|
|
12
|
+
import { useScrollY } from "./useScrollY.mjs";
|
|
13
|
+
import { useSearch } from "./useSearch.mjs";
|
|
14
|
+
import { useStickToBottom } from "./useStickToBottom.mjs";
|
|
15
15
|
|
|
16
|
-
export { calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice, useGetElementById, useGetElementOrWindow, useHorizontalSwipe,
|
|
16
|
+
export { calculateIsMobile, checkIsIOS, checkIsIphoneOrSafariDevice, checkIsMac, checkIsMobileScreen, checkIsMobileUserAgent, getBreakpointFromSize, useDevice, useGetElementById, useGetElementOrWindow, useHorizontalSwipe, useIsMounted, useItemSelector, useKeyboardDetector, usePersistedStore, useScreenWidth, useScrollBlockage, useScrollDetection, useScrollY, useSearch, useStickToBottom };
|
|
@@ -64,12 +64,22 @@ const useDevice = (breakpoint = "md") => {
|
|
|
64
64
|
isMac: void 0
|
|
65
65
|
});
|
|
66
66
|
useEffect(() => {
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
const breakpointValue = getBreakpointFromSize(breakpoint);
|
|
68
|
+
const mobileScreenQuery = window.matchMedia(`(max-width: ${breakpointValue}px)`);
|
|
69
|
+
const isMobileUserAgent = checkIsMobileUserAgent();
|
|
70
|
+
const isIOS = checkIsIOS();
|
|
71
|
+
const isMac = checkIsMac();
|
|
72
|
+
const applyMatches = (isMobileScreen) => setResult({
|
|
73
|
+
isMobileScreen,
|
|
74
|
+
isMobileUserAgent,
|
|
75
|
+
isMobile: isMobileScreen,
|
|
76
|
+
isIOS,
|
|
77
|
+
isMac
|
|
78
|
+
});
|
|
79
|
+
applyMatches(mobileScreenQuery.matches);
|
|
80
|
+
const handleChange = (event) => applyMatches(event.matches);
|
|
81
|
+
mobileScreenQuery.addEventListener("change", handleChange);
|
|
82
|
+
return () => mobileScreenQuery.removeEventListener("change", handleChange);
|
|
73
83
|
}, [breakpoint]);
|
|
74
84
|
return result;
|
|
75
85
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDevice.mjs","names":[],"sources":["../../../src/hooks/useDevice.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\nexport type SizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport const getBreakpointFromSize = (breakpoint: SizeType | number) => {\n switch (breakpoint) {\n case 'xs':\n return 480;\n case 'sm':\n return 640;\n case 'md':\n return 768;\n case 'lg':\n return 1024;\n case 'xl':\n return 1280;\n case '2xl':\n return 1536;\n default:\n return breakpoint;\n }\n};\n\nexport const checkIsMobileUserAgent = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n if (typeof userAgent === 'undefined') return;\n\n return /android|bb\\d+|meego|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(?:hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(?:ob|in)i|palm(?: os)?|phone|p(?:ixi|re)\\/|plucker|pocket|psp|series(?:4|6)0|symbian|treo|up\\.(?:browser|link)|vodafone|wap|windows ce|windows phone|xda|xiino|zte-/i.test(\n userAgent\n );\n};\n\nexport const checkIsIphoneOrSafariDevice = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n /* 1 . is it Safari? (Chrome & co. also contain “Safari/…”, so exclude them) */\n const isSafari =\n /Safari\\/\\d/i.test(userAgent) && // has “Safari/xxx”\n !/Chrome|CriOS|FxiOS|Edg|OPR/i.test(userAgent); // …but not the other browsers\n\n /* 2. is it an Apple device? (macOS or iOS/iPadOS) */\n const isAppleDevice = /Macintosh|iP(?:hone|ad|od)/.test(userAgent);\n\n return isSafari && isAppleDevice; // true for mac-Safari & iOS-Safari\n};\n\nexport const checkIsIOS = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n if (typeof userAgent === 'undefined') return;\n\n // Check for iOS devices: iPhone, iPad, iPod\n return /iP(?:hone|ad|od)/.test(userAgent);\n};\n\nexport const checkIsMac = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n if (typeof userAgent === 'undefined') return;\n\n // Check for macOS/Mac devices\n return /Macintosh|MacIntel|Mac OS X/.test(userAgent);\n};\n\nexport const checkIsMobileScreen = (\n breakpoint: number\n): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n return (window?.innerWidth ?? 0) <= breakpoint;\n};\n\ntype UseDeviceState = {\n isMobileScreen: boolean | undefined;\n isMobileUserAgent: boolean | undefined;\n isMobile: boolean | undefined;\n isIOS: boolean | undefined;\n isMac: boolean | undefined;\n};\n\nexport const calculateIsMobile = (breakpoint: SizeType | number = 'md') => {\n const breakpointValue = getBreakpointFromSize(breakpoint);\n\n const isMobileUserAgent = checkIsMobileUserAgent();\n const isMobileScreen = checkIsMobileScreen(breakpointValue);\n const isMobile = isMobileScreen ?? isMobileUserAgent;\n const isIOS = checkIsIOS();\n const isMac = checkIsMac();\n\n return {\n isMobileScreen, // Is the screen width within a mobile breakpoint.\n isMobileUserAgent, // Is the user agent indicative of a mobile device.\n isMobile, // Combines both checks to determine if the device is mobile.\n isIOS, // Is the device running iOS (iPhone, iPad, iPod).\n isMac, // Is the device a Mac computer (macOS).\n };\n};\n\nexport const useDevice = (breakpoint: SizeType | number = 'md') => {\n const [result, setResult] = useState<UseDeviceState>({\n isMobileScreen: undefined,\n isMobileUserAgent: undefined,\n isMobile: undefined,\n isIOS: undefined,\n isMac: undefined,\n });\n\n useEffect(() => {\n
|
|
1
|
+
{"version":3,"file":"useDevice.mjs","names":[],"sources":["../../../src/hooks/useDevice.ts"],"sourcesContent":["'use client';\n\nimport { useEffect, useState } from 'react';\n\nexport type SizeType = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';\n\nexport const getBreakpointFromSize = (breakpoint: SizeType | number) => {\n switch (breakpoint) {\n case 'xs':\n return 480;\n case 'sm':\n return 640;\n case 'md':\n return 768;\n case 'lg':\n return 1024;\n case 'xl':\n return 1280;\n case '2xl':\n return 1536;\n default:\n return breakpoint;\n }\n};\n\nexport const checkIsMobileUserAgent = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n if (typeof userAgent === 'undefined') return;\n\n return /android|bb\\d+|meego|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(?:hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(?:ob|in)i|palm(?: os)?|phone|p(?:ixi|re)\\/|plucker|pocket|psp|series(?:4|6)0|symbian|treo|up\\.(?:browser|link)|vodafone|wap|windows ce|windows phone|xda|xiino|zte-/i.test(\n userAgent\n );\n};\n\nexport const checkIsIphoneOrSafariDevice = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n /* 1 . is it Safari? (Chrome & co. also contain “Safari/…”, so exclude them) */\n const isSafari =\n /Safari\\/\\d/i.test(userAgent) && // has “Safari/xxx”\n !/Chrome|CriOS|FxiOS|Edg|OPR/i.test(userAgent); // …but not the other browsers\n\n /* 2. is it an Apple device? (macOS or iOS/iPadOS) */\n const isAppleDevice = /Macintosh|iP(?:hone|ad|od)/.test(userAgent);\n\n return isSafari && isAppleDevice; // true for mac-Safari & iOS-Safari\n};\n\nexport const checkIsIOS = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n if (typeof userAgent === 'undefined') return;\n\n // Check for iOS devices: iPhone, iPad, iPod\n return /iP(?:hone|ad|od)/.test(userAgent);\n};\n\nexport const checkIsMac = (): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n const userAgent = window.navigator?.userAgent;\n\n if (typeof userAgent === 'undefined') return;\n\n // Check for macOS/Mac devices\n return /Macintosh|MacIntel|Mac OS X/.test(userAgent);\n};\n\nexport const checkIsMobileScreen = (\n breakpoint: number\n): boolean | undefined => {\n if (typeof window === 'undefined') return;\n\n return (window?.innerWidth ?? 0) <= breakpoint;\n};\n\ntype UseDeviceState = {\n isMobileScreen: boolean | undefined;\n isMobileUserAgent: boolean | undefined;\n isMobile: boolean | undefined;\n isIOS: boolean | undefined;\n isMac: boolean | undefined;\n};\n\nexport const calculateIsMobile = (breakpoint: SizeType | number = 'md') => {\n const breakpointValue = getBreakpointFromSize(breakpoint);\n\n const isMobileUserAgent = checkIsMobileUserAgent();\n const isMobileScreen = checkIsMobileScreen(breakpointValue);\n const isMobile = isMobileScreen ?? isMobileUserAgent;\n const isIOS = checkIsIOS();\n const isMac = checkIsMac();\n\n return {\n isMobileScreen, // Is the screen width within a mobile breakpoint.\n isMobileUserAgent, // Is the user agent indicative of a mobile device.\n isMobile, // Combines both checks to determine if the device is mobile.\n isIOS, // Is the device running iOS (iPhone, iPad, iPod).\n isMac, // Is the device a Mac computer (macOS).\n };\n};\n\nexport const useDevice = (breakpoint: SizeType | number = 'md') => {\n const [result, setResult] = useState<UseDeviceState>({\n isMobileScreen: undefined,\n isMobileUserAgent: undefined,\n isMobile: undefined,\n isIOS: undefined,\n isMac: undefined,\n });\n\n useEffect(() => {\n const breakpointValue = getBreakpointFromSize(breakpoint);\n const mobileScreenQuery = window.matchMedia(\n `(max-width: ${breakpointValue}px)`\n );\n\n const isMobileUserAgent = checkIsMobileUserAgent();\n const isIOS = checkIsIOS();\n const isMac = checkIsMac();\n\n const applyMatches = (isMobileScreen: boolean) =>\n setResult({\n isMobileScreen,\n isMobileUserAgent,\n isMobile: isMobileScreen,\n isIOS,\n isMac,\n });\n\n applyMatches(mobileScreenQuery.matches);\n\n const handleChange = (event: MediaQueryListEvent) =>\n applyMatches(event.matches);\n\n mobileScreenQuery.addEventListener('change', handleChange);\n\n return () => mobileScreenQuery.removeEventListener('change', handleChange);\n }, [breakpoint]);\n\n return result;\n};\n"],"mappings":";;;;;AAMA,MAAa,yBAAyB,eAAkC;CACtE,QAAQ,YAAR;EACE,KAAK,MACH,OAAO;EACT,KAAK,MACH,OAAO;EACT,KAAK,MACH,OAAO;EACT,KAAK,MACH,OAAO;EACT,KAAK,MACH,OAAO;EACT,KAAK,OACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAa,+BAAoD;CAC/D,IAAI,OAAO,WAAW,aAAa;CAEnC,MAAM,YAAY,OAAO,WAAW;CAEpC,IAAI,OAAO,cAAc,aAAa;CAEtC,OAAO,mVAAmV,KACxV,SACF;AACF;AAEA,MAAa,oCAAyD;CACpE,IAAI,OAAO,WAAW,aAAa;CAEnC,MAAM,YAAY,OAAO,WAAW;CAGpC,MAAM,WACJ,cAAc,KAAK,SAAS,KAC5B,CAAC,8BAA8B,KAAK,SAAS;CAG/C,MAAM,gBAAgB,6BAA6B,KAAK,SAAS;CAEjE,OAAO,YAAY;AACrB;AAEA,MAAa,mBAAwC;CACnD,IAAI,OAAO,WAAW,aAAa;CAEnC,MAAM,YAAY,OAAO,WAAW;CAEpC,IAAI,OAAO,cAAc,aAAa;CAGtC,OAAO,mBAAmB,KAAK,SAAS;AAC1C;AAEA,MAAa,mBAAwC;CACnD,IAAI,OAAO,WAAW,aAAa;CAEnC,MAAM,YAAY,OAAO,WAAW;CAEpC,IAAI,OAAO,cAAc,aAAa;CAGtC,OAAO,8BAA8B,KAAK,SAAS;AACrD;AAEA,MAAa,uBACX,eACwB;CACxB,IAAI,OAAO,WAAW,aAAa;CAEnC,QAAQ,QAAQ,cAAc,MAAM;AACtC;AAUA,MAAa,qBAAqB,aAAgC,SAAS;CACzE,MAAM,kBAAkB,sBAAsB,UAAU;CAExD,MAAM,oBAAoB,uBAAuB;CACjD,MAAM,iBAAiB,oBAAoB,eAAe;CAK1D,OAAO;EACL;EACA;EACA,UAPe,kBAAkB;EAQjC,OAPY,WAOR;EACJ,OAPY,WAOR;CACN;AACF;AAEA,MAAa,aAAa,aAAgC,SAAS;CACjE,MAAM,CAAC,QAAQ,aAAa,SAAyB;EACnD,gBAAgB;EAChB,mBAAmB;EACnB,UAAU;EACV,OAAO;EACP,OAAO;CACT,CAAC;CAED,gBAAgB;EACd,MAAM,kBAAkB,sBAAsB,UAAU;EACxD,MAAM,oBAAoB,OAAO,WAC/B,eAAe,gBAAgB,IACjC;EAEA,MAAM,oBAAoB,uBAAuB;EACjD,MAAM,QAAQ,WAAW;EACzB,MAAM,QAAQ,WAAW;EAEzB,MAAM,gBAAgB,mBACpB,UAAU;GACR;GACA;GACA,UAAU;GACV;GACA;EACF,CAAC;EAEH,aAAa,kBAAkB,OAAO;EAEtC,MAAM,gBAAgB,UACpB,aAAa,MAAM,OAAO;EAE5B,kBAAkB,iBAAiB,UAAU,YAAY;EAEzD,aAAa,kBAAkB,oBAAoB,UAAU,YAAY;CAC3E,GAAG,CAAC,UAAU,CAAC;CAEf,OAAO;AACT"}
|