@scripso-homepad/ui 0.3.9 → 0.4.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/dist/chunk-C7GHBVMM.js +614 -0
- package/dist/chunk-C7GHBVMM.js.map +1 -0
- package/dist/index.js +29 -635
- package/dist/index.js.map +1 -1
- package/dist/web/index.cjs +1211 -0
- package/dist/web/index.cjs.map +1 -0
- package/dist/web/index.d.cts +156 -0
- package/dist/web/index.d.ts +156 -0
- package/dist/web/index.js +761 -0
- package/dist/web/index.js.map +1 -0
- package/package.json +26 -2
- package/src/web/hooks/useMediaQuery.ts +23 -0
- package/src/web/hooks/useOnClickOutside.ts +30 -0
- package/src/web/icons/BellIcon.tsx +27 -0
- package/src/web/icons/BuildingIcon.tsx +55 -0
- package/src/web/index.ts +37 -0
- package/src/web/layout/AppHeader.stories.tsx +85 -0
- package/src/web/layout/AppHeader.tsx +115 -0
- package/src/web/layout/BuildingSelect.stories.tsx +60 -0
- package/src/web/layout/BuildingSelect.tsx +208 -0
- package/src/web/layout/DashboardLayout.stories.tsx +87 -0
- package/src/web/layout/DashboardLayout.tsx +37 -0
- package/src/web/layout/Sidebar.stories.tsx +80 -0
- package/src/web/layout/Sidebar.tsx +244 -0
- package/src/web/layout/SidebarMobileHeader.stories.tsx +47 -0
- package/src/web/layout/SidebarMobileHeader.tsx +48 -0
- package/src/web/layout/SidebarNavItem.tsx +60 -0
- package/src/web/layout/SidebarUserCard.tsx +79 -0
- package/src/web/layout/story-fixtures.tsx +93 -0
- package/src/web/layout/story-helpers.tsx +5 -0
- package/src/web/layout/types.ts +48 -0
- package/src/web/utils/cn.ts +6 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/web/utils/cn.ts","../../src/web/icons/BellIcon.tsx","../../src/web/icons/BuildingIcon.tsx","../../src/web/hooks/useOnClickOutside.ts","../../src/web/layout/BuildingSelect.tsx","../../src/web/layout/AppHeader.tsx","../../src/web/hooks/useMediaQuery.ts","../../src/web/layout/SidebarNavItem.tsx","../../src/web/layout/SidebarUserCard.tsx","../../src/web/layout/Sidebar.tsx","../../src/web/layout/SidebarMobileHeader.tsx","../../src/web/layout/DashboardLayout.tsx"],"names":["jsx","jsxs","useState","useEffect","useCallback","useRef","Fragment"],"mappings":"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","file":"index.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","import { cn } from '../utils/cn';\n\ntype BellIconProps = {\n className?: string;\n};\n\nexport function BellIcon({ className }: BellIconProps) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 20 20\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden\n className={cn('shrink-0', className)}\n >\n <path\n d=\"M8.58333 17.4998C8.72282 17.7535 8.92787 17.9651 9.17708 18.1125C9.42628 18.2599 9.71048 18.3376 10 18.3376C10.2895 18.3376 10.5737 18.2599 10.8229 18.1125C11.0721 17.9651 11.2772 17.7535 11.4167 17.4998M5 6.6665C5 5.34042 5.52678 4.06865 6.46447 3.13097C7.40215 2.19329 8.67392 1.6665 10 1.6665C11.3261 1.6665 12.5979 2.19329 13.5355 3.13097C14.4732 4.06865 15 5.34042 15 6.6665C15 12.4998 17.5 14.1665 17.5 14.1665H2.5C2.5 14.1665 5 12.4998 5 6.6665Z\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n","import { cn } from '../utils/cn';\n\ntype BuildingIconProps = {\n className?: string;\n};\n\nexport function BuildingIcon({ className }: BuildingIconProps) {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden\n className={cn('shrink-0', className)}\n >\n <path\n d=\"M10 12h4\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M10 8h4\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M14 21v-3a2 2 0 0 0-4 0v3\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n );\n}\n","import { useEffect, type RefObject } from 'react';\n\nexport function useOnClickOutside<T extends HTMLElement>(\n ref: RefObject<T | null>,\n handler: () => void,\n enabled = true,\n) {\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n const onPointerDown = (event: MouseEvent | TouchEvent) => {\n const target = event.target as Node | null;\n if (!ref.current || !target || ref.current.contains(target)) {\n return;\n }\n\n handler();\n };\n\n document.addEventListener('mousedown', onPointerDown);\n document.addEventListener('touchstart', onPointerDown);\n\n return () => {\n document.removeEventListener('mousedown', onPointerDown);\n document.removeEventListener('touchstart', onPointerDown);\n };\n }, [enabled, handler, ref]);\n}\n","import { ChevronsUpDown } from 'lucide-react';\nimport { useCallback, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react';\n\nimport { BuildingIcon } from '../icons/BuildingIcon';\nimport { useOnClickOutside } from '../hooks/useOnClickOutside';\nimport { cn } from '../utils/cn';\nimport type { AdminBuildingOption, AdminHeaderLabels } from './types';\n\nexport type BuildingSelectProps = {\n options: AdminBuildingOption[];\n value: string;\n labels: Pick<AdminHeaderLabels, 'selectBuilding'>;\n onChange?: (buildingId: string) => void;\n disabled?: boolean;\n className?: string;\n menuClassName?: string;\n buildingIcon?: ReactNode;\n chevronIcon?: ReactNode;\n};\n\nexport function BuildingSelect({\n options,\n value,\n labels,\n onChange,\n disabled = false,\n className,\n menuClassName,\n buildingIcon,\n chevronIcon,\n}: BuildingSelectProps) {\n const [open, setOpen] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n const rootRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const selectedOption = options.find((option) => option.value === value);\n const displayLabel = selectedOption?.label ?? labels.selectBuilding;\n const selectableOptions = options.filter((option) => !option.disabled);\n\n const closeMenu = useCallback(() => {\n setOpen(false);\n setHighlightedIndex(-1);\n }, []);\n\n const openMenu = useCallback(() => {\n if (disabled || selectableOptions.length === 0) {\n return;\n }\n\n const selectedIndex = selectableOptions.findIndex((option) => option.value === value);\n setHighlightedIndex(selectedIndex >= 0 ? selectedIndex : 0);\n setOpen(true);\n }, [disabled, selectableOptions, value]);\n\n const selectOption = useCallback(\n (option: AdminBuildingOption) => {\n if (option.disabled) {\n return;\n }\n\n onChange?.(option.value);\n closeMenu();\n },\n [closeMenu, onChange],\n );\n\n useOnClickOutside(rootRef, closeMenu, open);\n\n const handleTriggerKeyDown = (event: KeyboardEvent<HTMLButtonElement>) => {\n if (disabled) {\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n case 'Enter':\n case ' ':\n event.preventDefault();\n openMenu();\n break;\n case 'Escape':\n closeMenu();\n break;\n default:\n break;\n }\n };\n\n const handleListKeyDown = (event: KeyboardEvent<HTMLUListElement>) => {\n if (!open || selectableOptions.length === 0) {\n return;\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n setHighlightedIndex((current) => (current + 1) % selectableOptions.length);\n break;\n case 'ArrowUp':\n event.preventDefault();\n setHighlightedIndex(\n (current) => (current - 1 + selectableOptions.length) % selectableOptions.length,\n );\n break;\n case 'Enter':\n case ' ':\n event.preventDefault();\n if (highlightedIndex >= 0) {\n selectOption(selectableOptions[highlightedIndex]);\n }\n break;\n case 'Escape':\n event.preventDefault();\n closeMenu();\n break;\n case 'Tab':\n closeMenu();\n break;\n default:\n break;\n }\n };\n\n return (\n <div ref={rootRef} className={cn('relative w-57.5', className)}>\n <button\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={listboxId}\n aria-label={labels.selectBuilding}\n onClick={() => (open ? closeMenu() : openMenu())}\n onKeyDown={handleTriggerKeyDown}\n className={cn(\n 'flex h-11 w-full items-center gap-3 rounded-xl border border-storm-gray-50 bg-white p-3 text-left transition-colors',\n !disabled && 'cursor-pointer hover:bg-storm-gray-50/60',\n open && 'bg-storm-gray-50/60',\n disabled && 'cursor-not-allowed opacity-60',\n )}\n >\n {buildingIcon ?? <BuildingIcon className=\"shrink-0 text-navy\" />}\n <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-slate-blue\">\n {displayLabel}\n </span>\n {chevronIcon ?? (\n <ChevronsUpDown\n size={16}\n strokeWidth={1.75}\n className={cn(\n 'shrink-0 text-storm-gray-100 transition-transform duration-200',\n open && 'rotate-180',\n )}\n aria-hidden\n />\n )}\n </button>\n\n {open ? (\n <ul\n id={listboxId}\n role=\"listbox\"\n aria-label={labels.selectBuilding}\n tabIndex={-1}\n onKeyDown={handleListKeyDown}\n className={cn(\n 'absolute top-[calc(100%+4px)] left-0 z-50 flex w-full flex-col gap-1 overflow-hidden rounded-xl border border-storm-gray-50 bg-white p-2 shadow-[0_8px_24px_rgba(21,26,30,0.08)]',\n menuClassName,\n )}\n >\n {options.map((option) => {\n const selectableIndex = selectableOptions.findIndex(\n (selectable) => selectable.value === option.value,\n );\n const isSelected = option.value === value;\n const isHighlighted = selectableIndex === highlightedIndex;\n\n return (\n <li key={option.value} role=\"presentation\">\n <button\n type=\"button\"\n role=\"option\"\n aria-selected={isSelected}\n disabled={option.disabled}\n onMouseEnter={() => {\n if (!option.disabled && selectableIndex >= 0) {\n setHighlightedIndex(selectableIndex);\n }\n }}\n onClick={() => selectOption(option)}\n className={cn(\n 'flex w-full rounded-lg px-3 py-2.5 text-left text-sm font-medium text-slate-blue transition-colors',\n !option.disabled && 'cursor-pointer hover:bg-storm-gray-50',\n (isSelected || isHighlighted) && !option.disabled && 'bg-storm-gray-50',\n option.disabled && 'cursor-not-allowed opacity-50',\n )}\n >\n <span className=\"truncate\">{option.label}</span>\n </button>\n </li>\n );\n })}\n </ul>\n ) : null}\n </div>\n );\n}\n","import { Search } from 'lucide-react';\nimport type { ReactNode } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\n\nimport { Input } from '../../components/Input';\nimport { BellIcon } from '../icons/BellIcon';\nimport { cn } from '../utils/cn';\nimport { BuildingSelect } from './BuildingSelect';\nimport type { AdminBuildingOption, AdminHeaderLabels } from './types';\n\nexport type AppHeaderProps = {\n title: string;\n labels: AdminHeaderLabels;\n buildingOptions: AdminBuildingOption[];\n selectedBuildingId: string;\n className?: string;\n buildingSelectClassName?: string;\n searchClassName?: string;\n notificationsButtonClassName?: string;\n searchValue?: string;\n searchFieldStyle?: StyleProp<ViewStyle>;\n buildingIcon?: ReactNode;\n notificationsIcon?: ReactNode;\n searchIcon?: ReactNode;\n buildingChevronIcon?: ReactNode;\n buildingSelectDisabled?: boolean;\n searchDisabled?: boolean;\n notificationsDisabled?: boolean;\n onBuildingChange?: (buildingId: string) => void;\n onNotificationsClick?: () => void;\n onSearchChange?: (value: string) => void;\n};\n\nconst defaultSearchFieldStyle: ViewStyle = {\n height: 44,\n minHeight: 44,\n maxHeight: 44,\n backgroundColor: 'transparent',\n padding: 12,\n width: 260,\n borderRadius: 12,\n};\n\nexport function AppHeader({\n title,\n labels,\n buildingOptions,\n selectedBuildingId,\n className,\n buildingSelectClassName,\n searchClassName,\n notificationsButtonClassName,\n searchValue,\n searchFieldStyle,\n buildingIcon,\n notificationsIcon,\n searchIcon,\n buildingChevronIcon,\n buildingSelectDisabled = false,\n searchDisabled = false,\n notificationsDisabled = false,\n onBuildingChange,\n onNotificationsClick,\n onSearchChange,\n}: AppHeaderProps) {\n return (\n <header\n className={cn(\n 'flex shrink-0 items-center justify-between gap-4 border-b border-storm-gray-50 bg-storm-gray-0 px-6 py-3',\n className,\n )}\n >\n <h1 className=\"truncate text-xl font-bold text-slate-blue\">{title}</h1>\n\n <div className=\"flex shrink-0 items-center gap-3\">\n <BuildingSelect\n options={buildingOptions}\n value={selectedBuildingId}\n labels={labels}\n onChange={onBuildingChange}\n disabled={buildingSelectDisabled}\n className={buildingSelectClassName}\n buildingIcon={buildingIcon}\n chevronIcon={buildingChevronIcon}\n />\n\n <Input\n leftIcon={searchIcon ?? <Search strokeWidth={1.75} />}\n placeholder={labels.searchPlaceholder}\n value={searchValue}\n onChangeText={(value) => onSearchChange?.(value)}\n editable={!searchDisabled}\n className={cn('w-65!', searchClassName)}\n containerStyle={{ width: 260, maxWidth: 260, flexShrink: 0 }}\n fieldStyle={searchFieldStyle ?? defaultSearchFieldStyle}\n accessibilityLabel={labels.searchPlaceholder}\n />\n\n <button\n type=\"button\"\n onClick={onNotificationsClick}\n disabled={notificationsDisabled}\n className={cn(\n 'h-11 cursor-pointer rounded-xl border border-storm-gray-50 p-3 text-slate-blue transition-colors hover:bg-storm-gray-50/60',\n notificationsDisabled && 'pointer-events-none opacity-60',\n notificationsButtonClassName,\n )}\n aria-label={labels.notifications}\n >\n {notificationsIcon ?? <BellIcon className=\"text-navy\" />}\n </button>\n </div>\n </header>\n );\n}\n","import { useEffect, useState } from 'react';\n\nexport function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(() => {\n if (typeof window === 'undefined') {\n return false;\n }\n\n return window.matchMedia(query).matches;\n });\n\n useEffect(() => {\n const mediaQuery = window.matchMedia(query);\n const handleChange = () => setMatches(mediaQuery.matches);\n\n handleChange();\n mediaQuery.addEventListener('change', handleChange);\n\n return () => mediaQuery.removeEventListener('change', handleChange);\n }, [query]);\n\n return matches;\n}\n","import { NavLink } from 'react-router-dom';\n\nimport { cn } from '../utils/cn';\nimport type { AdminNavItem } from './types';\n\nexport type SidebarNavItemProps = {\n item: AdminNavItem;\n isOpen: boolean;\n onNavigate?: (item: AdminNavItem) => void;\n};\n\nexport function SidebarNavItem({ item, isOpen, onNavigate }: SidebarNavItemProps) {\n if (item.hidden) {\n return null;\n }\n\n const handleClick = () => {\n item.onClick?.();\n onNavigate?.(item);\n };\n\n return (\n <NavLink\n to={item.to}\n end={item.end}\n onClick={handleClick}\n aria-disabled={item.disabled}\n tabIndex={item.disabled ? -1 : undefined}\n className={({ isActive }) =>\n cn(\n 'relative flex items-center rounded-xl px-4 py-3 transition-[background-color,color,gap,padding] duration-300 ease-in-out',\n isOpen ? 'w-full gap-4' : 'justify-center gap-0 px-3',\n item.disabled && 'pointer-events-none opacity-50',\n isActive ? 'bg-black/12 text-white' : 'text-navy-100 hover:bg-white/5',\n )\n }\n >\n {({ isActive }) => (\n <>\n <span\n aria-hidden\n className={cn(\n 'absolute top-1.5 -left-1 h-8 w-2 rounded-full bg-white transition-[opacity,transform] duration-300 ease-in-out',\n isActive ? 'scale-100 opacity-100' : 'scale-75 opacity-0',\n )}\n />\n <span className=\"flex size-5 shrink-0 items-center justify-center\">{item.icon}</span>\n <span\n className={cn(\n 'overflow-hidden text-sm font-semibold whitespace-nowrap transition-[max-width,opacity] duration-300 ease-in-out',\n isOpen ? 'max-w-[180px] opacity-100' : 'max-w-0 opacity-0',\n )}\n >\n {item.label}\n </span>\n </>\n )}\n </NavLink>\n );\n}\n","import { LogOut } from 'lucide-react';\n\nimport { cn } from '../utils/cn';\nimport type { AdminSidebarUser } from './types';\n\nexport type SidebarUserCardProps = {\n user: AdminSidebarUser;\n isOpen: boolean;\n logoutLabel: string;\n onLogout?: () => void;\n className?: string;\n};\n\nfunction getInitials(fullName: string, email: string): string {\n const parts = fullName.trim().split(/\\s+/).filter(Boolean);\n if (parts.length > 0) {\n return parts\n .slice(0, 2)\n .map((part) => part[0]?.toUpperCase() ?? '')\n .join('');\n }\n\n return email.slice(0, 2).toUpperCase();\n}\n\nexport function SidebarUserCard({\n user,\n isOpen,\n logoutLabel,\n onLogout,\n className,\n}: SidebarUserCardProps) {\n const initials = user.initials ?? getInitials(user.fullName, user.email);\n\n return (\n <div\n className={cn(\n 'flex w-full items-center rounded-xl p-3 transition-[background-color,justify-content,gap] duration-300 ease-in-out',\n isOpen ? 'justify-between gap-3 bg-black/12' : 'justify-center',\n className,\n )}\n >\n <div\n className={cn(\n 'flex min-w-0 items-center transition-[gap,flex] duration-300 ease-in-out',\n isOpen ? 'flex-1 gap-3' : 'gap-0',\n )}\n >\n <div\n className=\"flex size-11 shrink-0 items-center justify-center rounded-full bg-white/5 text-sm font-bold text-white\"\n aria-hidden\n >\n {initials}\n </div>\n <div\n className={cn(\n 'min-w-0 overflow-hidden transition-[max-width,opacity] duration-300 ease-in-out',\n isOpen ? 'max-w-[160px] opacity-100' : 'max-w-0 opacity-0',\n )}\n >\n <p className=\"truncate text-sm font-bold text-white\">{user.fullName}</p>\n <p className=\"truncate text-xs text-storm-gray-100\">{user.email}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => onLogout?.()}\n className={cn(\n 'shrink-0 cursor-pointer text-navy-150 transition-[opacity,transform,max-width] duration-300 ease-in-out hover:opacity-80 active:scale-95',\n isOpen ? 'max-w-8 scale-100 opacity-100' : 'pointer-events-none max-w-0 scale-90 opacity-0',\n )}\n aria-label={logoutLabel}\n tabIndex={isOpen ? 0 : -1}\n >\n <LogOut size={20} strokeWidth={1.75} />\n </button>\n </div>\n );\n}\n","import { PanelLeftClose, PanelLeftOpen, X } from 'lucide-react';\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';\nimport { useLocation } from 'react-router-dom';\n\nimport { useMediaQuery } from '../hooks/useMediaQuery';\nimport { cn } from '../utils/cn';\nimport { SidebarNavItem } from './SidebarNavItem';\nimport { SidebarUserCard } from './SidebarUserCard';\nimport type { AdminNavItem, AdminSidebarBranding, AdminSidebarLabels, AdminSidebarUser } from './types';\n\nexport type SidebarProps = {\n navItems: AdminNavItem[];\n footerNavItems: AdminNavItem[];\n branding: AdminSidebarBranding;\n labels: AdminSidebarLabels;\n sidebarStorageKey: string;\n user?: AdminSidebarUser;\n onLogout?: () => void;\n className?: string;\n mobileOpen: boolean;\n onMobileClose?: () => void;\n onNavItemClick?: (item: AdminNavItem) => void;\n collapseIcon?: ReactNode;\n expandIcon?: ReactNode;\n closeIcon?: ReactNode;\n initialCollapsed?: boolean;\n collapsed?: boolean;\n onCollapsedChange?: (collapsed: boolean) => void;\n};\n\nfunction readInitialCollapsedState(storageKey: string, initialCollapsed?: boolean): boolean {\n if (initialCollapsed !== undefined) {\n return initialCollapsed;\n }\n\n if (typeof window === 'undefined') {\n return false;\n }\n\n const stored = window.localStorage.getItem(storageKey);\n if (stored === null) {\n return false;\n }\n\n return stored !== 'true';\n}\n\nexport function Sidebar({\n navItems,\n footerNavItems,\n branding,\n labels,\n sidebarStorageKey,\n user,\n onLogout,\n className,\n mobileOpen,\n onMobileClose,\n onNavItemClick,\n collapseIcon,\n expandIcon,\n closeIcon,\n initialCollapsed,\n collapsed,\n onCollapsedChange,\n}: SidebarProps) {\n const location = useLocation();\n const isDesktop = useMediaQuery('(min-width: 768px)');\n const [internalCollapsed, setInternalCollapsed] = useState(() =>\n readInitialCollapsedState(sidebarStorageKey, initialCollapsed),\n );\n\n const isCollapsed = collapsed ?? internalCollapsed;\n const isExpanded = isDesktop ? !isCollapsed : true;\n\n const setCollapsed = useCallback(\n (next: boolean) => {\n if (collapsed === undefined) {\n setInternalCollapsed(next);\n window.localStorage.setItem(sidebarStorageKey, String(!next));\n }\n\n onCollapsedChange?.(next);\n },\n [collapsed, onCollapsedChange, sidebarStorageKey],\n );\n\n const toggleCollapsed = useCallback(() => {\n setCollapsed(!isCollapsed);\n }, [isCollapsed, setCollapsed]);\n\n const handleHeaderAction = useCallback(() => {\n if (isDesktop) {\n toggleCollapsed();\n\n return;\n }\n\n onMobileClose?.();\n }, [isDesktop, onMobileClose, toggleCollapsed]);\n\n const handleNavItemNavigate = useCallback(\n (item: AdminNavItem) => {\n onNavItemClick?.(item);\n\n if (!isDesktop) {\n onMobileClose?.();\n }\n },\n [isDesktop, onMobileClose, onNavItemClick],\n );\n\n const pathnameRef = useRef(location.pathname);\n\n useEffect(() => {\n if (pathnameRef.current === location.pathname) {\n return;\n }\n\n pathnameRef.current = location.pathname;\n\n if (!isDesktop) {\n onMobileClose?.();\n }\n }, [location.pathname, isDesktop, onMobileClose]);\n\n useEffect(() => {\n if (!mobileOpen || isDesktop) {\n return;\n }\n\n const previousOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n\n return () => {\n document.body.style.overflow = previousOverflow;\n };\n }, [mobileOpen, isDesktop]);\n\n const visibleNavItems = navItems.filter((item) => !item.hidden);\n const visibleFooterNavItems = footerNavItems.filter((item) => !item.hidden);\n\n return (\n <>\n <button\n type=\"button\"\n aria-label={labels.closeMenu}\n onClick={onMobileClose}\n className={cn(\n 'fixed inset-0 z-40 bg-navy-800/60 backdrop-blur-[1px] transition-opacity duration-300 ease-in-out md:hidden',\n mobileOpen ? 'opacity-100' : 'pointer-events-none opacity-0',\n )}\n />\n\n <aside\n className={cn(\n 'fixed inset-y-0 left-0 z-50 flex h-screen w-[min(300px,85vw)] flex-col gap-8 overflow-hidden bg-navy p-4 shadow-xl',\n 'transition-[transform,width,padding,gap] duration-300 ease-in-out will-change-[transform,width]',\n mobileOpen ? 'translate-x-0' : '-translate-x-full',\n 'md:relative md:z-auto md:w-[300px] md:translate-x-0 md:shadow-none',\n isExpanded ? 'md:w-[300px]' : 'md:w-[52px] md:items-center md:gap-4 md:px-3 md:py-4',\n className,\n )}\n aria-hidden={!isDesktop && !mobileOpen}\n >\n <div\n className={cn(\n 'flex w-full items-center transition-[justify-content] duration-300 ease-in-out',\n isExpanded ? 'justify-between' : 'justify-center',\n )}\n >\n <div\n className={cn(\n 'flex min-w-0 items-center gap-1.5 overflow-hidden transition-[max-width,opacity] duration-300 ease-in-out',\n isExpanded ? 'max-w-[240px] opacity-100' : 'max-w-0 opacity-0',\n )}\n >\n <img src={branding.logoIconSrc} alt=\"\" className=\"h-5 w-[29px] shrink-0\" />\n <div className=\"flex min-w-0 flex-col leading-none\">\n <span className=\"text-[21px] font-bold tracking-tight whitespace-nowrap text-white\">\n {branding.logoTitle}\n </span>\n <span className=\"text-[6.5px] font-normal tracking-[-0.26px] text-navy-300 uppercase\">\n {branding.logoTagline}\n </span>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={handleHeaderAction}\n className=\"shrink-0 cursor-pointer text-white transition-[opacity,transform] duration-200 hover:opacity-80 active:scale-95\"\n aria-label={\n isDesktop\n ? isExpanded\n ? labels.collapse\n : labels.expand\n : labels.closeMenu\n }\n >\n {isDesktop ? (\n isExpanded ? (\n collapseIcon ?? <PanelLeftClose size={20} strokeWidth={1.75} />\n ) : (\n expandIcon ?? <PanelLeftOpen size={20} strokeWidth={1.75} />\n )\n ) : (\n closeIcon ?? <X size={20} strokeWidth={1.75} />\n )}\n </button>\n </div>\n\n <nav className=\"flex flex-1 flex-col gap-2 overflow-y-auto overflow-x-hidden\">\n {visibleNavItems.map((item) => (\n <SidebarNavItem\n key={item.to}\n item={item}\n isOpen={isExpanded}\n onNavigate={handleNavItemNavigate}\n />\n ))}\n </nav>\n\n <div className=\"flex w-full flex-col gap-4\">\n {visibleFooterNavItems.map((item) => (\n <SidebarNavItem\n key={item.to}\n item={item}\n isOpen={isExpanded}\n onNavigate={handleNavItemNavigate}\n />\n ))}\n {user ? (\n <SidebarUserCard\n user={user}\n isOpen={isExpanded}\n logoutLabel={labels.logout}\n onLogout={onLogout}\n />\n ) : null}\n </div>\n </aside>\n </>\n );\n}\n","import { Menu } from 'lucide-react';\n\nimport { cn } from '../utils/cn';\nimport type { AdminSidebarBranding } from './types';\n\nexport type SidebarMobileHeaderProps = {\n branding: Pick<AdminSidebarBranding, 'logoIconSrc' | 'logoTitle' | 'logoTagline'>;\n openMenuLabel: string;\n onOpenMenu: () => void;\n menuOpen?: boolean;\n className?: string;\n};\n\nexport function SidebarMobileHeader({\n branding,\n openMenuLabel,\n onOpenMenu,\n menuOpen = false,\n className,\n}: SidebarMobileHeaderProps) {\n return (\n <header\n className={cn(\n 'sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-storm-gray-50 bg-storm-gray-0 px-4 md:hidden',\n className,\n )}\n >\n <button\n type=\"button\"\n onClick={onOpenMenu}\n className=\"flex size-10 items-center justify-center rounded-xl text-navy transition-colors hover:bg-storm-gray-50\"\n aria-label={openMenuLabel}\n aria-expanded={menuOpen}\n >\n <Menu size={22} strokeWidth={1.75} />\n </button>\n <div className=\"flex min-w-0 items-center gap-2\">\n <img src={branding.logoIconSrc} alt=\"\" className=\"h-[30px] w-[42px] shrink-0\" />\n <div className=\"min-w-0 leading-none\">\n <p className=\"truncate text-base font-bold tracking-tight text-navy\">{branding.logoTitle}</p>\n <p className=\"truncate text-[6.5px] font-normal tracking-[-0.26px] text-navy-300 uppercase\">\n {branding.logoTagline}\n </p>\n </div>\n </div>\n </header>\n );\n}\n","import type { ReactNode } from 'react';\n\nimport { AppHeader } from './AppHeader';\nimport type { AppHeaderProps } from './AppHeader';\nimport { Sidebar } from './Sidebar';\nimport type { SidebarProps } from './Sidebar';\nimport { SidebarMobileHeader } from './SidebarMobileHeader';\nimport type { SidebarMobileHeaderProps } from './SidebarMobileHeader';\n\nexport type DashboardLayoutProps = {\n sidebar: SidebarProps;\n header: AppHeaderProps;\n mobileHeader: SidebarMobileHeaderProps;\n children: ReactNode;\n className?: string;\n mainClassName?: string;\n};\n\nexport function DashboardLayout({\n sidebar,\n header,\n mobileHeader,\n children,\n className,\n mainClassName,\n}: DashboardLayoutProps) {\n return (\n <div className={className ?? 'flex min-h-screen bg-storm-gray-0'}>\n <Sidebar {...sidebar} />\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <SidebarMobileHeader {...mobileHeader} />\n <AppHeader {...header} />\n <main className={mainClassName ?? 'flex-1 overflow-auto p-4 md:p-6'}>{children}</main>\n </div>\n </div>\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scripso-homepad/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Cross-platform UI components for Homepad (React Web + React Native)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"import": "./src/index.ts",
|
|
24
24
|
"require": "./dist/index.cjs",
|
|
25
25
|
"default": "./src/index.ts"
|
|
26
|
+
},
|
|
27
|
+
"./web": {
|
|
28
|
+
"types": "./dist/web/index.d.ts",
|
|
29
|
+
"import": "./src/web/index.ts",
|
|
30
|
+
"require": "./dist/web/index.cjs",
|
|
31
|
+
"default": "./src/web/index.ts"
|
|
26
32
|
}
|
|
27
33
|
},
|
|
28
34
|
"files": [
|
|
@@ -35,6 +41,7 @@
|
|
|
35
41
|
"lint": "eslint .",
|
|
36
42
|
"clean": "rm -rf dist",
|
|
37
43
|
"storybook": "storybook dev -p 6006",
|
|
44
|
+
"storybook:clean": "rm -rf node_modules/.cache/storybook node_modules/.vite storybook-static && storybook dev -p 6006",
|
|
38
45
|
"build-storybook": "storybook build",
|
|
39
46
|
"prepublishOnly": "npm run build"
|
|
40
47
|
},
|
|
@@ -42,7 +49,9 @@
|
|
|
42
49
|
"react": ">=18",
|
|
43
50
|
"react-native": ">=0.74",
|
|
44
51
|
"react-native-svg": ">=13",
|
|
45
|
-
"react-native-web": ">=0.19"
|
|
52
|
+
"react-native-web": ">=0.19",
|
|
53
|
+
"react-router-dom": ">=6",
|
|
54
|
+
"lucide-react": ">=0.400"
|
|
46
55
|
},
|
|
47
56
|
"peerDependenciesMeta": {
|
|
48
57
|
"react-native-svg": {
|
|
@@ -50,23 +59,38 @@
|
|
|
50
59
|
},
|
|
51
60
|
"react-native-web": {
|
|
52
61
|
"optional": true
|
|
62
|
+
},
|
|
63
|
+
"react-router-dom": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"lucide-react": {
|
|
67
|
+
"optional": true
|
|
53
68
|
}
|
|
54
69
|
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"clsx": "^2.1.1",
|
|
72
|
+
"tailwind-merge": "^3.4.0"
|
|
73
|
+
},
|
|
55
74
|
"devDependencies": {
|
|
56
75
|
"@eslint/js": "^9.17.0",
|
|
57
76
|
"@storybook/addon-essentials": "^8.4.7",
|
|
58
77
|
"@storybook/addon-interactions": "^8.4.7",
|
|
59
78
|
"@storybook/react-vite": "^8.4.7",
|
|
79
|
+
"@tailwindcss/postcss": "^4.1.13",
|
|
80
|
+
"@tailwindcss/vite": "^4.1.13",
|
|
60
81
|
"@types/react": "^18.3.18",
|
|
61
82
|
"eslint": "^9.17.0",
|
|
62
83
|
"eslint-plugin-react-hooks": "^5.1.0",
|
|
63
84
|
"globals": "^15.14.0",
|
|
85
|
+
"lucide-react": "^0.563.0",
|
|
64
86
|
"react": "^18.3.1",
|
|
65
87
|
"react-dom": "^18.3.1",
|
|
88
|
+
"react-router-dom": "^7.9.1",
|
|
66
89
|
"react-native": "^0.76.5",
|
|
67
90
|
"react-native-svg": "^15.11.2",
|
|
68
91
|
"react-native-web": "^0.19.13",
|
|
69
92
|
"storybook": "^8.4.7",
|
|
93
|
+
"tailwindcss": "^4.1.13",
|
|
70
94
|
"tsup": "^8.3.5",
|
|
71
95
|
"typescript": "^5.7.2",
|
|
72
96
|
"typescript-eslint": "^8.18.2",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useMediaQuery(query: string): boolean {
|
|
4
|
+
const [matches, setMatches] = useState(() => {
|
|
5
|
+
if (typeof window === 'undefined') {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
return window.matchMedia(query).matches;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const mediaQuery = window.matchMedia(query);
|
|
14
|
+
const handleChange = () => setMatches(mediaQuery.matches);
|
|
15
|
+
|
|
16
|
+
handleChange();
|
|
17
|
+
mediaQuery.addEventListener('change', handleChange);
|
|
18
|
+
|
|
19
|
+
return () => mediaQuery.removeEventListener('change', handleChange);
|
|
20
|
+
}, [query]);
|
|
21
|
+
|
|
22
|
+
return matches;
|
|
23
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useEffect, type RefObject } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useOnClickOutside<T extends HTMLElement>(
|
|
4
|
+
ref: RefObject<T | null>,
|
|
5
|
+
handler: () => void,
|
|
6
|
+
enabled = true,
|
|
7
|
+
) {
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (!enabled) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const onPointerDown = (event: MouseEvent | TouchEvent) => {
|
|
14
|
+
const target = event.target as Node | null;
|
|
15
|
+
if (!ref.current || !target || ref.current.contains(target)) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
handler();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
document.addEventListener('mousedown', onPointerDown);
|
|
23
|
+
document.addEventListener('touchstart', onPointerDown);
|
|
24
|
+
|
|
25
|
+
return () => {
|
|
26
|
+
document.removeEventListener('mousedown', onPointerDown);
|
|
27
|
+
document.removeEventListener('touchstart', onPointerDown);
|
|
28
|
+
};
|
|
29
|
+
}, [enabled, handler, ref]);
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cn } from '../utils/cn';
|
|
2
|
+
|
|
3
|
+
type BellIconProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function BellIcon({ className }: BellIconProps) {
|
|
8
|
+
return (
|
|
9
|
+
<svg
|
|
10
|
+
width="20"
|
|
11
|
+
height="20"
|
|
12
|
+
viewBox="0 0 20 20"
|
|
13
|
+
fill="none"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
aria-hidden
|
|
16
|
+
className={cn('shrink-0', className)}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M8.58333 17.4998C8.72282 17.7535 8.92787 17.9651 9.17708 18.1125C9.42628 18.2599 9.71048 18.3376 10 18.3376C10.2895 18.3376 10.5737 18.2599 10.8229 18.1125C11.0721 17.9651 11.2772 17.7535 11.4167 17.4998M5 6.6665C5 5.34042 5.52678 4.06865 6.46447 3.13097C7.40215 2.19329 8.67392 1.6665 10 1.6665C11.3261 1.6665 12.5979 2.19329 13.5355 3.13097C14.4732 4.06865 15 5.34042 15 6.6665C15 12.4998 17.5 14.1665 17.5 14.1665H2.5C2.5 14.1665 5 12.4998 5 6.6665Z"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth="2"
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
strokeLinejoin="round"
|
|
24
|
+
/>
|
|
25
|
+
</svg>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { cn } from '../utils/cn';
|
|
2
|
+
|
|
3
|
+
type BuildingIconProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export function BuildingIcon({ className }: BuildingIconProps) {
|
|
8
|
+
return (
|
|
9
|
+
<svg
|
|
10
|
+
width="20"
|
|
11
|
+
height="20"
|
|
12
|
+
viewBox="0 0 24 24"
|
|
13
|
+
fill="none"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
aria-hidden
|
|
16
|
+
className={cn('shrink-0', className)}
|
|
17
|
+
>
|
|
18
|
+
<path
|
|
19
|
+
d="M10 12h4"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth="2"
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
strokeLinejoin="round"
|
|
24
|
+
/>
|
|
25
|
+
<path
|
|
26
|
+
d="M10 8h4"
|
|
27
|
+
stroke="currentColor"
|
|
28
|
+
strokeWidth="2"
|
|
29
|
+
strokeLinecap="round"
|
|
30
|
+
strokeLinejoin="round"
|
|
31
|
+
/>
|
|
32
|
+
<path
|
|
33
|
+
d="M14 21v-3a2 2 0 0 0-4 0v3"
|
|
34
|
+
stroke="currentColor"
|
|
35
|
+
strokeWidth="2"
|
|
36
|
+
strokeLinecap="round"
|
|
37
|
+
strokeLinejoin="round"
|
|
38
|
+
/>
|
|
39
|
+
<path
|
|
40
|
+
d="M6 10H4a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2"
|
|
41
|
+
stroke="currentColor"
|
|
42
|
+
strokeWidth="2"
|
|
43
|
+
strokeLinecap="round"
|
|
44
|
+
strokeLinejoin="round"
|
|
45
|
+
/>
|
|
46
|
+
<path
|
|
47
|
+
d="M6 21V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v16"
|
|
48
|
+
stroke="currentColor"
|
|
49
|
+
strokeWidth="2"
|
|
50
|
+
strokeLinecap="round"
|
|
51
|
+
strokeLinejoin="round"
|
|
52
|
+
/>
|
|
53
|
+
</svg>
|
|
54
|
+
);
|
|
55
|
+
}
|
package/src/web/index.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export { AppHeader } from './layout/AppHeader';
|
|
2
|
+
export type { AppHeaderProps } from './layout/AppHeader';
|
|
3
|
+
|
|
4
|
+
export { BuildingSelect } from './layout/BuildingSelect';
|
|
5
|
+
export type { BuildingSelectProps } from './layout/BuildingSelect';
|
|
6
|
+
|
|
7
|
+
export { DashboardLayout } from './layout/DashboardLayout';
|
|
8
|
+
export type { DashboardLayoutProps } from './layout/DashboardLayout';
|
|
9
|
+
|
|
10
|
+
export { Sidebar } from './layout/Sidebar';
|
|
11
|
+
export type { SidebarProps } from './layout/Sidebar';
|
|
12
|
+
|
|
13
|
+
export { SidebarMobileHeader } from './layout/SidebarMobileHeader';
|
|
14
|
+
export type { SidebarMobileHeaderProps } from './layout/SidebarMobileHeader';
|
|
15
|
+
|
|
16
|
+
export { SidebarNavItem } from './layout/SidebarNavItem';
|
|
17
|
+
export type { SidebarNavItemProps } from './layout/SidebarNavItem';
|
|
18
|
+
|
|
19
|
+
export { SidebarUserCard } from './layout/SidebarUserCard';
|
|
20
|
+
export type { SidebarUserCardProps } from './layout/SidebarUserCard';
|
|
21
|
+
|
|
22
|
+
export { BellIcon } from './icons/BellIcon';
|
|
23
|
+
export { BuildingIcon } from './icons/BuildingIcon';
|
|
24
|
+
|
|
25
|
+
export { useMediaQuery } from './hooks/useMediaQuery';
|
|
26
|
+
export { useOnClickOutside } from './hooks/useOnClickOutside';
|
|
27
|
+
export { cn } from './utils/cn';
|
|
28
|
+
|
|
29
|
+
export type {
|
|
30
|
+
AdminBuildingOption,
|
|
31
|
+
AdminHeaderLabels,
|
|
32
|
+
AdminLayoutLabels,
|
|
33
|
+
AdminNavItem,
|
|
34
|
+
AdminSidebarBranding,
|
|
35
|
+
AdminSidebarLabels,
|
|
36
|
+
AdminSidebarUser,
|
|
37
|
+
} from './layout/types';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { fn } from '@storybook/test';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { WebLayoutCanvas } from './story-helpers';
|
|
6
|
+
import { AppHeader, type AppHeaderProps } from './AppHeader';
|
|
7
|
+
import { storyBuildingOptions, storyLabels } from './story-fixtures';
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
title: 'Web Layout/AppHeader',
|
|
11
|
+
component: AppHeader,
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: 'fullscreen',
|
|
14
|
+
webLayout: true,
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
title: 'Dashboard',
|
|
18
|
+
labels: storyLabels.header,
|
|
19
|
+
buildingOptions: storyBuildingOptions,
|
|
20
|
+
selectedBuildingId: storyBuildingOptions[0].value,
|
|
21
|
+
onBuildingChange: fn(),
|
|
22
|
+
onNotificationsClick: fn(),
|
|
23
|
+
onSearchChange: fn(),
|
|
24
|
+
},
|
|
25
|
+
} satisfies Meta<typeof AppHeader>;
|
|
26
|
+
|
|
27
|
+
export default meta;
|
|
28
|
+
type Story = StoryObj<typeof meta>;
|
|
29
|
+
|
|
30
|
+
export const Default: Story = {
|
|
31
|
+
render: (args) => (
|
|
32
|
+
<WebLayoutCanvas>
|
|
33
|
+
<AppHeader {...args} />
|
|
34
|
+
</WebLayoutCanvas>
|
|
35
|
+
),
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function WithSearchStory(args: AppHeaderProps) {
|
|
39
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<WebLayoutCanvas>
|
|
43
|
+
<AppHeader
|
|
44
|
+
{...args}
|
|
45
|
+
searchValue={searchQuery}
|
|
46
|
+
onSearchChange={setSearchQuery}
|
|
47
|
+
/>
|
|
48
|
+
</WebLayoutCanvas>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const WithSearch: Story = {
|
|
53
|
+
render: (args) => <WithSearchStory {...args} />,
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function InteractiveBuildingSelectStory(args: AppHeaderProps) {
|
|
57
|
+
const [selectedBuildingId, setSelectedBuildingId] = useState(args.selectedBuildingId);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<WebLayoutCanvas>
|
|
61
|
+
<AppHeader
|
|
62
|
+
{...args}
|
|
63
|
+
selectedBuildingId={selectedBuildingId}
|
|
64
|
+
onBuildingChange={setSelectedBuildingId}
|
|
65
|
+
/>
|
|
66
|
+
</WebLayoutCanvas>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const InteractiveBuildingSelect: Story = {
|
|
71
|
+
render: (args) => <InteractiveBuildingSelectStory {...args} />,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const SingleBuildingDisabled: Story = {
|
|
75
|
+
args: {
|
|
76
|
+
buildingOptions: [storyBuildingOptions[0]],
|
|
77
|
+
selectedBuildingId: storyBuildingOptions[0].value,
|
|
78
|
+
buildingSelectDisabled: true,
|
|
79
|
+
},
|
|
80
|
+
render: (args) => (
|
|
81
|
+
<WebLayoutCanvas>
|
|
82
|
+
<AppHeader {...args} />
|
|
83
|
+
</WebLayoutCanvas>
|
|
84
|
+
),
|
|
85
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Search } from 'lucide-react';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
4
|
+
|
|
5
|
+
import { Input } from '../../components/Input';
|
|
6
|
+
import { BellIcon } from '../icons/BellIcon';
|
|
7
|
+
import { cn } from '../utils/cn';
|
|
8
|
+
import { BuildingSelect } from './BuildingSelect';
|
|
9
|
+
import type { AdminBuildingOption, AdminHeaderLabels } from './types';
|
|
10
|
+
|
|
11
|
+
export type AppHeaderProps = {
|
|
12
|
+
title: string;
|
|
13
|
+
labels: AdminHeaderLabels;
|
|
14
|
+
buildingOptions: AdminBuildingOption[];
|
|
15
|
+
selectedBuildingId: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
buildingSelectClassName?: string;
|
|
18
|
+
searchClassName?: string;
|
|
19
|
+
notificationsButtonClassName?: string;
|
|
20
|
+
searchValue?: string;
|
|
21
|
+
searchFieldStyle?: StyleProp<ViewStyle>;
|
|
22
|
+
buildingIcon?: ReactNode;
|
|
23
|
+
notificationsIcon?: ReactNode;
|
|
24
|
+
searchIcon?: ReactNode;
|
|
25
|
+
buildingChevronIcon?: ReactNode;
|
|
26
|
+
buildingSelectDisabled?: boolean;
|
|
27
|
+
searchDisabled?: boolean;
|
|
28
|
+
notificationsDisabled?: boolean;
|
|
29
|
+
onBuildingChange?: (buildingId: string) => void;
|
|
30
|
+
onNotificationsClick?: () => void;
|
|
31
|
+
onSearchChange?: (value: string) => void;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const defaultSearchFieldStyle: ViewStyle = {
|
|
35
|
+
height: 44,
|
|
36
|
+
minHeight: 44,
|
|
37
|
+
maxHeight: 44,
|
|
38
|
+
backgroundColor: 'transparent',
|
|
39
|
+
padding: 12,
|
|
40
|
+
width: 260,
|
|
41
|
+
borderRadius: 12,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export function AppHeader({
|
|
45
|
+
title,
|
|
46
|
+
labels,
|
|
47
|
+
buildingOptions,
|
|
48
|
+
selectedBuildingId,
|
|
49
|
+
className,
|
|
50
|
+
buildingSelectClassName,
|
|
51
|
+
searchClassName,
|
|
52
|
+
notificationsButtonClassName,
|
|
53
|
+
searchValue,
|
|
54
|
+
searchFieldStyle,
|
|
55
|
+
buildingIcon,
|
|
56
|
+
notificationsIcon,
|
|
57
|
+
searchIcon,
|
|
58
|
+
buildingChevronIcon,
|
|
59
|
+
buildingSelectDisabled = false,
|
|
60
|
+
searchDisabled = false,
|
|
61
|
+
notificationsDisabled = false,
|
|
62
|
+
onBuildingChange,
|
|
63
|
+
onNotificationsClick,
|
|
64
|
+
onSearchChange,
|
|
65
|
+
}: AppHeaderProps) {
|
|
66
|
+
return (
|
|
67
|
+
<header
|
|
68
|
+
className={cn(
|
|
69
|
+
'flex shrink-0 items-center justify-between gap-4 border-b border-storm-gray-50 bg-storm-gray-0 px-6 py-3',
|
|
70
|
+
className,
|
|
71
|
+
)}
|
|
72
|
+
>
|
|
73
|
+
<h1 className="truncate text-xl font-bold text-slate-blue">{title}</h1>
|
|
74
|
+
|
|
75
|
+
<div className="flex shrink-0 items-center gap-3">
|
|
76
|
+
<BuildingSelect
|
|
77
|
+
options={buildingOptions}
|
|
78
|
+
value={selectedBuildingId}
|
|
79
|
+
labels={labels}
|
|
80
|
+
onChange={onBuildingChange}
|
|
81
|
+
disabled={buildingSelectDisabled}
|
|
82
|
+
className={buildingSelectClassName}
|
|
83
|
+
buildingIcon={buildingIcon}
|
|
84
|
+
chevronIcon={buildingChevronIcon}
|
|
85
|
+
/>
|
|
86
|
+
|
|
87
|
+
<Input
|
|
88
|
+
leftIcon={searchIcon ?? <Search strokeWidth={1.75} />}
|
|
89
|
+
placeholder={labels.searchPlaceholder}
|
|
90
|
+
value={searchValue}
|
|
91
|
+
onChangeText={(value) => onSearchChange?.(value)}
|
|
92
|
+
editable={!searchDisabled}
|
|
93
|
+
className={cn('w-65!', searchClassName)}
|
|
94
|
+
containerStyle={{ width: 260, maxWidth: 260, flexShrink: 0 }}
|
|
95
|
+
fieldStyle={searchFieldStyle ?? defaultSearchFieldStyle}
|
|
96
|
+
accessibilityLabel={labels.searchPlaceholder}
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
<button
|
|
100
|
+
type="button"
|
|
101
|
+
onClick={onNotificationsClick}
|
|
102
|
+
disabled={notificationsDisabled}
|
|
103
|
+
className={cn(
|
|
104
|
+
'h-11 cursor-pointer rounded-xl border border-storm-gray-50 p-3 text-slate-blue transition-colors hover:bg-storm-gray-50/60',
|
|
105
|
+
notificationsDisabled && 'pointer-events-none opacity-60',
|
|
106
|
+
notificationsButtonClassName,
|
|
107
|
+
)}
|
|
108
|
+
aria-label={labels.notifications}
|
|
109
|
+
>
|
|
110
|
+
{notificationsIcon ?? <BellIcon className="text-navy" />}
|
|
111
|
+
</button>
|
|
112
|
+
</div>
|
|
113
|
+
</header>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { fn } from '@storybook/test';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { WebLayoutCanvas } from './story-helpers';
|
|
6
|
+
import { BuildingSelect, type BuildingSelectProps } from './BuildingSelect';
|
|
7
|
+
import { storyBuildingOptions, storyLabels } from './story-fixtures';
|
|
8
|
+
|
|
9
|
+
const meta = {
|
|
10
|
+
title: 'Web Layout/BuildingSelect',
|
|
11
|
+
component: BuildingSelect,
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: 'centered',
|
|
14
|
+
webLayout: true,
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
options: storyBuildingOptions,
|
|
18
|
+
value: storyBuildingOptions[0].value,
|
|
19
|
+
labels: storyLabels.header,
|
|
20
|
+
onChange: fn(),
|
|
21
|
+
},
|
|
22
|
+
} satisfies Meta<typeof BuildingSelect>;
|
|
23
|
+
|
|
24
|
+
export default meta;
|
|
25
|
+
type Story = StoryObj<typeof meta>;
|
|
26
|
+
|
|
27
|
+
export const Default: Story = {
|
|
28
|
+
render: (args) => (
|
|
29
|
+
<WebLayoutCanvas>
|
|
30
|
+
<BuildingSelect {...args} />
|
|
31
|
+
</WebLayoutCanvas>
|
|
32
|
+
),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function InteractiveBuildingSelectStory(args: BuildingSelectProps) {
|
|
36
|
+
const [value, setValue] = useState(args.value);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<WebLayoutCanvas>
|
|
40
|
+
<div className="pb-64 pt-2">
|
|
41
|
+
<BuildingSelect {...args} value={value} onChange={setValue} />
|
|
42
|
+
</div>
|
|
43
|
+
</WebLayoutCanvas>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const Interactive: Story = {
|
|
48
|
+
render: (args) => <InteractiveBuildingSelectStory {...args} />,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const Disabled: Story = {
|
|
52
|
+
args: {
|
|
53
|
+
disabled: true,
|
|
54
|
+
},
|
|
55
|
+
render: (args) => (
|
|
56
|
+
<WebLayoutCanvas>
|
|
57
|
+
<BuildingSelect {...args} />
|
|
58
|
+
</WebLayoutCanvas>
|
|
59
|
+
),
|
|
60
|
+
};
|