all
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import { createContext } from 'react'
|
||||
|
||||
type DrawerContextType = {
|
||||
isCollapsed: boolean | undefined
|
||||
isTouched: boolean
|
||||
}
|
||||
|
||||
export const DrawerContext = createContext<DrawerContextType>({
|
||||
isCollapsed: undefined,
|
||||
isTouched: false,
|
||||
})
|
||||
@@ -0,0 +1,92 @@
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { Link, useLocation } from 'react-router-dom'
|
||||
import cx from 'classnames'
|
||||
import { DrawerContext } from './drawer-context'
|
||||
|
||||
type DrawerItemProps = {
|
||||
icon: ReactElement
|
||||
href: string
|
||||
primaryText: string
|
||||
secondaryText: string
|
||||
isActive?: boolean
|
||||
}
|
||||
|
||||
export const DrawerItem = ({
|
||||
icon,
|
||||
href,
|
||||
primaryText,
|
||||
secondaryText,
|
||||
}: DrawerItemProps) => {
|
||||
const location = useLocation()
|
||||
const [isActive, setIsActive] = useState<boolean>()
|
||||
const { isCollapsed } = useContext(DrawerContext)
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
(href === '/' && location.pathname === '/') ||
|
||||
(href !== '/' && location.pathname.startsWith(href))
|
||||
) {
|
||||
setIsActive(true)
|
||||
} else {
|
||||
setIsActive(false)
|
||||
}
|
||||
}, [location.pathname, href])
|
||||
|
||||
return (
|
||||
<Link
|
||||
to={href}
|
||||
title={isCollapsed ? `${primaryText}: ${secondaryText}` : secondaryText}
|
||||
className={cx('w-full')}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'gap-1.5',
|
||||
'p-1.5',
|
||||
'rounded-md',
|
||||
{
|
||||
'bg-black': isActive,
|
||||
'dark:bg-white': isActive,
|
||||
},
|
||||
{
|
||||
'hover:bg-gray-100': !isActive,
|
||||
'dark:hover:bg-gray-600': !isActive,
|
||||
},
|
||||
{
|
||||
'hover:bg-gray-200': !isActive,
|
||||
'dark:hover:bg-gray-700': !isActive,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'shrink-0',
|
||||
'w-[21px]',
|
||||
'h-[21px]',
|
||||
{
|
||||
'text-white': isActive,
|
||||
'dark:text-gray-800': isActive,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
{!isCollapsed && (
|
||||
<span
|
||||
className={cx({
|
||||
'text-white': isActive,
|
||||
'dark:text-gray-800': isActive,
|
||||
})}
|
||||
>
|
||||
{primaryText}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
129
Downloads/Voltaserve/ui/src/lib/components/drawer/drawer.tsx
Normal file
129
Downloads/Voltaserve/ui/src/lib/components/drawer/drawer.tsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import { ReactNode, useEffect, useMemo, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import cx from 'classnames'
|
||||
import { StorageOptions } from '../../types'
|
||||
import { IconChevronLeft, IconChevronRight } from '../icons'
|
||||
import { DrawerContext } from './drawer-context'
|
||||
|
||||
type DrawerProps = {
|
||||
children?: ReactNode
|
||||
logo?: ReactNode
|
||||
storage?: StorageOptions
|
||||
}
|
||||
|
||||
export const Drawer = ({ children, storage, logo }: DrawerProps) => {
|
||||
const [isCollapsed, setIsCollapsed] = useState<boolean | undefined>(undefined)
|
||||
const [isTouched, setIsTouched] = useState(false)
|
||||
const localStorageCollapsedKey = useMemo(
|
||||
() =>
|
||||
`${storage?.prefix || 'app'}_${
|
||||
storage?.namespace || 'main'
|
||||
}_drawer_collapsed`,
|
||||
[storage],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
let collapse = false
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
const value = localStorage.getItem(localStorageCollapsedKey)
|
||||
if (value) {
|
||||
collapse = JSON.parse(value)
|
||||
} else {
|
||||
localStorage.setItem(localStorageCollapsedKey, JSON.stringify(false))
|
||||
}
|
||||
}
|
||||
if (collapse) {
|
||||
setIsCollapsed(true)
|
||||
} else {
|
||||
setIsCollapsed(false)
|
||||
}
|
||||
}, [localStorageCollapsedKey, setIsCollapsed])
|
||||
|
||||
if (isCollapsed === undefined) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<DrawerContext.Provider
|
||||
value={{
|
||||
isCollapsed,
|
||||
isTouched,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'h-full',
|
||||
'border-r',
|
||||
'border-r-gray-200',
|
||||
'dark:border-r-gray-700',
|
||||
'shrink-0',
|
||||
'gap-0',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cx('flex', 'items-center', 'justify-center', 'h-[80px]')}
|
||||
>
|
||||
<Link to="/">
|
||||
<div className={cx('flex', 'h-[40px]')}>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'items-center',
|
||||
'justify-center',
|
||||
'w-[40px]',
|
||||
'h-[40px]',
|
||||
)}
|
||||
>
|
||||
{logo}
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'items-center',
|
||||
'gap-0.5',
|
||||
'pt-0',
|
||||
'pr-1.5',
|
||||
'pb-1.5',
|
||||
'pl-1.5',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div className={cx('grow')} />
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'gap-0',
|
||||
{ 'justify-center': isCollapsed, 'justify-end': !isCollapsed },
|
||||
'h-[50px]',
|
||||
'w-full',
|
||||
{ 'px-0': isCollapsed, 'px-1.5': !isCollapsed },
|
||||
'cursor-pointer',
|
||||
'hover:bg-gray-100',
|
||||
'hover:dark:bg-gray-600',
|
||||
'active:bg-gray-200',
|
||||
'active:dark:bg-gray-700',
|
||||
)}
|
||||
onClick={() => {
|
||||
setIsCollapsed(!isCollapsed)
|
||||
setIsTouched(true)
|
||||
localStorage.setItem(
|
||||
localStorageCollapsedKey,
|
||||
JSON.stringify(!isCollapsed),
|
||||
)
|
||||
}}
|
||||
>
|
||||
{isCollapsed ? <IconChevronRight /> : <IconChevronLeft />}
|
||||
</div>
|
||||
</div>
|
||||
</DrawerContext.Provider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './drawer'
|
||||
export * from './drawer-context'
|
||||
export * from './drawer-item'
|
||||
395
Downloads/Voltaserve/ui/src/lib/components/icons.tsx
Normal file
395
Downloads/Voltaserve/ui/src/lib/components/icons.tsx
Normal file
@@ -0,0 +1,395 @@
|
||||
import { cx } from '@emotion/css'
|
||||
|
||||
export type IconBaseProps = {
|
||||
filled?: boolean
|
||||
} & React.HTMLAttributes<HTMLSpanElement>
|
||||
|
||||
type GetClassNameOptions = {
|
||||
filled?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
function getClassName({ filled, className }: GetClassNameOptions) {
|
||||
return cx(
|
||||
'material-symbols-rounded',
|
||||
{ 'material-symbols-rounded__filled': filled },
|
||||
'text-[16px]',
|
||||
className,
|
||||
)
|
||||
}
|
||||
|
||||
export const IconPlayArrow = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
play_arrow
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconUpload = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
upload
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconNotifications = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
notifications
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconMoreVert = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
more_vert
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconLogout = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
logout
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconChevronLeft = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
chevron_left
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconChevronRight = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
chevron_right
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconAdd = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
add
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconEdit = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
edit
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconGroup = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
group
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconDownload = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
download
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconArrowTopRight = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
arrow_top_right
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconFileCopy = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
file_copy
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconDelete = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
delete
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconSend = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
send
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconPersonAdd = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
person_add
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconCheck = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
check
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconLibraryAddCheck = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
library_add_check
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconSelectCheckBox = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
select_check_box
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconCheckBoxOutlineBlank = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
check_box_outline_blank
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconCheckCircle = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled: true, className })} {...props}>
|
||||
check_circle
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconError = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled: true, className })} {...props}>
|
||||
error
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconWarning = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
warning
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconWorkspaces = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
workspaces
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconFlag = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
flag
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconClose = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
close
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconSchedule = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
schedule
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconClearAll = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
clear_all
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconOpenInNew = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
open_in_new
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconInfo = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
info
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconSearch = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
search
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconRefresh = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
refresh
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconGridView = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
grid_view
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconArrowUpward = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
arrow_upward
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconArrowDownward = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
arrow_downward
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconExpandMore = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
expand_more
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconList = ({ className, filled, ...props }: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
list
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconHourglass = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
hourglass
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconKeyboardArrowLeft = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
keyboard_arrow_left
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconKeyboardArrowRight = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
keyboard_arrow_right
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconKeyboardDoubleArrowRight = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
keyboard_double_arrow_right
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconKeyboardDoubleArrowLeft = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
keyboard_double_arrow_left
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconFirstPage = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
first_page
|
||||
</span>
|
||||
)
|
||||
|
||||
export const IconLastPage = ({
|
||||
className,
|
||||
filled,
|
||||
...props
|
||||
}: IconBaseProps) => (
|
||||
<span className={getClassName({ filled, className })} {...props}>
|
||||
last_page
|
||||
</span>
|
||||
)
|
||||
10
Downloads/Voltaserve/ui/src/lib/components/index.ts
Normal file
10
Downloads/Voltaserve/ui/src/lib/components/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export * from './drawer'
|
||||
export * from './icons'
|
||||
export * from './spinner'
|
||||
export * from './section-spinner'
|
||||
export * from './shell'
|
||||
export * from './pagination'
|
||||
export * from './page-pagination'
|
||||
export * from './search-input'
|
||||
export * from './switch-card'
|
||||
export * from './text'
|
||||
@@ -0,0 +1,72 @@
|
||||
import React, { ChangeEvent, useCallback } from 'react'
|
||||
import { Select } from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { usePageMonitor } from '../hooks/page-monitor'
|
||||
import { Pagination } from './pagination'
|
||||
|
||||
type PagePaginationProps = {
|
||||
totalPages: number
|
||||
totalElements: number
|
||||
page: number
|
||||
size: number
|
||||
steps: number[]
|
||||
uiSize?: string
|
||||
style?: React.CSSProperties
|
||||
setPage: (page: number) => void
|
||||
setSize: (size: number) => void
|
||||
}
|
||||
|
||||
export const PagePagination = ({
|
||||
totalElements,
|
||||
totalPages,
|
||||
page,
|
||||
size,
|
||||
uiSize = 'md',
|
||||
steps,
|
||||
style,
|
||||
setPage,
|
||||
setSize,
|
||||
}: PagePaginationProps) => {
|
||||
const { hasPageSwitcher, hasSizeSelector } = usePageMonitor({
|
||||
totalElements,
|
||||
totalPages,
|
||||
steps,
|
||||
})
|
||||
|
||||
const handleSizeChange = useCallback(
|
||||
(event: ChangeEvent<HTMLSelectElement>) => {
|
||||
setSize(parseInt(event.target.value))
|
||||
setPage(1)
|
||||
},
|
||||
[setSize, setPage],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{!hasPageSwitcher && !hasSizeSelector ? null : (
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1.5')}
|
||||
style={style}
|
||||
>
|
||||
{hasPageSwitcher ? (
|
||||
<Pagination
|
||||
uiSize={uiSize}
|
||||
page={page}
|
||||
totalPages={totalPages}
|
||||
onPageChange={setPage}
|
||||
/>
|
||||
) : null}
|
||||
{hasSizeSelector ? (
|
||||
<Select defaultValue={size} onChange={handleSizeChange}>
|
||||
{steps.map((step, index) => (
|
||||
<option key={index} value={step.toString()}>
|
||||
{step} items
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
112
Downloads/Voltaserve/ui/src/lib/components/pagination.tsx
Normal file
112
Downloads/Voltaserve/ui/src/lib/components/pagination.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { ButtonGroup, Button, IconButton } from '@chakra-ui/react'
|
||||
import {
|
||||
IconKeyboardArrowLeft,
|
||||
IconKeyboardArrowRight,
|
||||
IconKeyboardDoubleArrowLeft,
|
||||
IconKeyboardDoubleArrowRight,
|
||||
IconFirstPage,
|
||||
IconLastPage,
|
||||
} from '@/lib'
|
||||
|
||||
type PaginationProps = {
|
||||
totalPages: number
|
||||
page: number
|
||||
maxButtons?: number
|
||||
uiSize?: string
|
||||
onPageChange?: (page: number) => void
|
||||
}
|
||||
|
||||
export const Pagination = ({
|
||||
totalPages,
|
||||
page,
|
||||
maxButtons: maxButtonsProp = 5,
|
||||
uiSize = 'md',
|
||||
onPageChange,
|
||||
}: PaginationProps) => {
|
||||
const maxButtons = totalPages < maxButtonsProp ? totalPages : maxButtonsProp
|
||||
const pages = useMemo(() => {
|
||||
const end = Math.ceil(page / maxButtons) * maxButtons
|
||||
const start = end - maxButtons + 1
|
||||
return Array.from({ length: end - start + 1 }, (_, index) => start + index)
|
||||
}, [page, maxButtons])
|
||||
const firstPage = 1
|
||||
const lastPage = totalPages
|
||||
const fastForwardPage = pages[pages.length - 1] + 1
|
||||
const rewindPage = pages[0] - maxButtons
|
||||
const nextPage = page + 1
|
||||
const previousPage = page - 1
|
||||
|
||||
const handlePageChange = useCallback(
|
||||
(value: number) => {
|
||||
if (value !== page) {
|
||||
onPageChange?.(value)
|
||||
}
|
||||
},
|
||||
[page, onPageChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<IconButton
|
||||
variant="outline"
|
||||
size={uiSize}
|
||||
isDisabled={page === 1}
|
||||
icon={<IconFirstPage />}
|
||||
aria-label="First"
|
||||
onClick={() => handlePageChange(firstPage)}
|
||||
/>
|
||||
<IconButton
|
||||
variant="outline"
|
||||
size={uiSize}
|
||||
isDisabled={rewindPage < 1}
|
||||
icon={<IconKeyboardDoubleArrowLeft />}
|
||||
aria-label="Rewind"
|
||||
onClick={() => handlePageChange(rewindPage)}
|
||||
/>
|
||||
<IconButton
|
||||
variant="outline"
|
||||
size={uiSize}
|
||||
isDisabled={page === 1}
|
||||
icon={<IconKeyboardArrowLeft />}
|
||||
aria-label="Previous"
|
||||
onClick={() => handlePageChange(previousPage)}
|
||||
/>
|
||||
{pages.map((index) => (
|
||||
<Button
|
||||
size={uiSize}
|
||||
key={index}
|
||||
isDisabled={index > totalPages}
|
||||
onClick={() => handlePageChange(index)}
|
||||
colorScheme={index === page ? 'blue' : undefined}
|
||||
>
|
||||
{index}
|
||||
</Button>
|
||||
))}
|
||||
<IconButton
|
||||
variant="outline"
|
||||
size={uiSize}
|
||||
isDisabled={page === lastPage}
|
||||
icon={<IconKeyboardArrowRight />}
|
||||
aria-label="Next"
|
||||
onClick={() => handlePageChange(nextPage)}
|
||||
/>
|
||||
<IconButton
|
||||
variant="outline"
|
||||
size={uiSize}
|
||||
isDisabled={fastForwardPage > lastPage}
|
||||
icon={<IconKeyboardDoubleArrowRight />}
|
||||
aria-label="Fast Forward"
|
||||
onClick={() => handlePageChange(fastForwardPage)}
|
||||
/>
|
||||
<IconButton
|
||||
variant="outline"
|
||||
size={uiSize}
|
||||
isDisabled={page === lastPage}
|
||||
icon={<IconLastPage />}
|
||||
aria-label="Last"
|
||||
onClick={() => handlePageChange(lastPage)}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
)
|
||||
}
|
||||
93
Downloads/Voltaserve/ui/src/lib/components/search-input.tsx
Normal file
93
Downloads/Voltaserve/ui/src/lib/components/search-input.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
ChangeEvent,
|
||||
KeyboardEvent,
|
||||
} from 'react'
|
||||
import {
|
||||
Button,
|
||||
HStack,
|
||||
InputGroup,
|
||||
InputLeftElement,
|
||||
InputRightElement,
|
||||
IconButton,
|
||||
Input,
|
||||
} from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
import { IconClose, IconSearch } from './icons'
|
||||
|
||||
type SearchInputProps = {
|
||||
query?: string
|
||||
onChange?: (value: string) => void
|
||||
}
|
||||
|
||||
export const SearchInput = ({ query, onChange }: SearchInputProps) => {
|
||||
const [draft, setDraft] = useState('')
|
||||
const [text, setText] = useState('')
|
||||
const [isFocused, setIsFocused] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setDraft(query || '')
|
||||
}, [query])
|
||||
|
||||
useEffect(() => {
|
||||
onChange?.(text)
|
||||
}, [text, onChange])
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
setDraft('')
|
||||
setText('')
|
||||
}, [])
|
||||
|
||||
const handleChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
|
||||
setDraft(event.target.value || '')
|
||||
}, [])
|
||||
|
||||
const handleSearch = useCallback((value: string) => {
|
||||
setText(value)
|
||||
}, [])
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(event: KeyboardEvent<HTMLInputElement>, value: string) => {
|
||||
if (event.key === 'Enter') {
|
||||
handleSearch(value)
|
||||
}
|
||||
},
|
||||
[handleSearch],
|
||||
)
|
||||
|
||||
return (
|
||||
<HStack>
|
||||
<InputGroup>
|
||||
<InputLeftElement className={cx('pointer-events-none')}>
|
||||
<IconSearch className={cx('text-gray-300')} />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
value={draft}
|
||||
placeholder={draft || 'Search'}
|
||||
variant="filled"
|
||||
onKeyDown={(event) => handleKeyDown(event, draft)}
|
||||
onChange={handleChange}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
/>
|
||||
{draft && (
|
||||
<InputRightElement>
|
||||
<IconButton
|
||||
icon={<IconClose />}
|
||||
onClick={handleClear}
|
||||
size="xs"
|
||||
aria-label="Clear"
|
||||
/>
|
||||
</InputRightElement>
|
||||
)}
|
||||
</InputGroup>
|
||||
{draft || (isFocused && draft) ? (
|
||||
<Button onClick={() => handleSearch(draft)} isDisabled={!draft}>
|
||||
Search
|
||||
</Button>
|
||||
) : null}
|
||||
</HStack>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import cx from 'classnames'
|
||||
import { Spinner } from './spinner'
|
||||
|
||||
type SectionSpinnerProps = {
|
||||
width?: string
|
||||
height?: string
|
||||
}
|
||||
|
||||
const DEFAULT_WIDTH = '100%'
|
||||
const DEFAULT_HEIGHT = '300px'
|
||||
|
||||
export const SectionSpinner = ({ width, height }: SectionSpinnerProps) => (
|
||||
<div
|
||||
className={cx('flex', 'items-center', 'justify-center')}
|
||||
style={{ width: width || DEFAULT_WIDTH, height: height || DEFAULT_HEIGHT }}
|
||||
>
|
||||
<Spinner />
|
||||
</div>
|
||||
)
|
||||
59
Downloads/Voltaserve/ui/src/lib/components/shell.tsx
Normal file
59
Downloads/Voltaserve/ui/src/lib/components/shell.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ReactElement } from 'react'
|
||||
import cx from 'classnames'
|
||||
import { StorageOptions } from '../types'
|
||||
import { Drawer, DrawerItem } from './drawer'
|
||||
|
||||
type ShellItem = {
|
||||
href: string
|
||||
icon: ReactElement
|
||||
primaryText: string
|
||||
secondaryText: string
|
||||
}
|
||||
|
||||
type ShellProps = {
|
||||
storage?: StorageOptions
|
||||
logo: ReactElement
|
||||
topBar: ReactElement
|
||||
items: ShellItem[]
|
||||
children?: ReactElement
|
||||
}
|
||||
|
||||
export const Shell = ({
|
||||
logo,
|
||||
topBar,
|
||||
items,
|
||||
storage,
|
||||
children,
|
||||
}: ShellProps) => (
|
||||
<div className={cx('flex', 'flex-row', 'items-center', 'gap-0', 'h-full')}>
|
||||
<Drawer storage={storage} logo={logo}>
|
||||
{items.map((item, index) => (
|
||||
<DrawerItem
|
||||
key={index}
|
||||
href={item.href}
|
||||
icon={item.icon}
|
||||
primaryText={item.primaryText}
|
||||
secondaryText={item.secondaryText}
|
||||
/>
|
||||
))}
|
||||
</Drawer>
|
||||
<div className={cx('flex', 'flex-col', 'items-center', 'h-full', 'w-full')}>
|
||||
{topBar}
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'w-full',
|
||||
'lg:w-[1250px]',
|
||||
'px-3.5',
|
||||
'pt-3.5',
|
||||
'overflow-y-auto',
|
||||
'overflow-x-hidden',
|
||||
'grow',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
5
Downloads/Voltaserve/ui/src/lib/components/spinner.tsx
Normal file
5
Downloads/Voltaserve/ui/src/lib/components/spinner.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Spinner as ChakraSpinner } from '@chakra-ui/react'
|
||||
|
||||
export const Spinner = (props: any) => (
|
||||
<ChakraSpinner size="sm" thickness="4px" {...props} />
|
||||
)
|
||||
123
Downloads/Voltaserve/ui/src/lib/components/switch-card.tsx
Normal file
123
Downloads/Voltaserve/ui/src/lib/components/switch-card.tsx
Normal file
@@ -0,0 +1,123 @@
|
||||
import {
|
||||
ChangeEvent,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import {
|
||||
IconButton,
|
||||
Popover,
|
||||
PopoverBody,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Switch,
|
||||
} from '@chakra-ui/react'
|
||||
import cx from 'classnames'
|
||||
|
||||
type SwitchCardProps = {
|
||||
children?: ReactNode
|
||||
icon: ReactElement
|
||||
label: string
|
||||
isCollapsed?: boolean
|
||||
localStorageNamespace: string
|
||||
expandedMinWidth?: string
|
||||
}
|
||||
|
||||
export const SwitchCard = ({
|
||||
children,
|
||||
icon,
|
||||
label,
|
||||
isCollapsed,
|
||||
localStorageNamespace,
|
||||
expandedMinWidth,
|
||||
}: SwitchCardProps) => {
|
||||
const [isActive, setIsActive] = useState(false)
|
||||
const localStorageActiveKey = useMemo(
|
||||
() => `voltaserve_${localStorageNamespace}_switch_card_active`,
|
||||
[localStorageNamespace],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
let active = false
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
const value = localStorage.getItem(localStorageActiveKey)
|
||||
if (value) {
|
||||
active = JSON.parse(value)
|
||||
} else {
|
||||
localStorage.setItem(localStorageActiveKey, JSON.stringify(false))
|
||||
}
|
||||
}
|
||||
if (active) {
|
||||
setIsActive(true)
|
||||
} else {
|
||||
setIsActive(false)
|
||||
}
|
||||
}, [localStorageActiveKey, setIsActive])
|
||||
|
||||
const handleChange = useCallback(
|
||||
(event: ChangeEvent<HTMLInputElement>) => {
|
||||
setIsActive(event.target.checked)
|
||||
localStorage.setItem(
|
||||
localStorageActiveKey,
|
||||
JSON.stringify(event.target.checked),
|
||||
)
|
||||
},
|
||||
[localStorageActiveKey],
|
||||
)
|
||||
|
||||
if (isCollapsed) {
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<IconButton
|
||||
icon={icon}
|
||||
variant="outline"
|
||||
className={cx('w-[50px]', 'h-[50px]', 'p-1.5', 'rounded-md')}
|
||||
aria-label={label}
|
||||
title={label}
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverBody>{children}</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-col',
|
||||
'gap-0',
|
||||
'border',
|
||||
'border-gray-200',
|
||||
'dark:border-gray-600',
|
||||
'rounded-md',
|
||||
)}
|
||||
style={{ minWidth: expandedMinWidth }}
|
||||
>
|
||||
<div
|
||||
className={cx(
|
||||
'flex',
|
||||
'flex-row',
|
||||
'items-center',
|
||||
'gap-1',
|
||||
'h-[50px]',
|
||||
'px-1',
|
||||
'shrink-0',
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
<span className={cx('grow')}>{label}</span>
|
||||
<Switch isChecked={isActive} onChange={handleChange} />
|
||||
</div>
|
||||
{isActive && (
|
||||
<div className={cx('pt-0', 'pr-1', 'pb-1', 'pl-1')}>{children}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
47
Downloads/Voltaserve/ui/src/lib/components/text.tsx
Normal file
47
Downloads/Voltaserve/ui/src/lib/components/text.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { ReactNode, useEffect, useRef } from 'react'
|
||||
import cx from 'classnames'
|
||||
|
||||
interface TextProps extends React.HTMLAttributes<HTMLSpanElement> {
|
||||
children?: ReactNode
|
||||
noOfLines?: number
|
||||
maxCharacters?: number
|
||||
}
|
||||
|
||||
export const Text: React.FC<TextProps> = ({
|
||||
children,
|
||||
noOfLines,
|
||||
maxCharacters,
|
||||
className,
|
||||
...props
|
||||
}: TextProps) => {
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const text = children?.toString() || ''
|
||||
if (ref.current && maxCharacters && text.length > maxCharacters) {
|
||||
ref.current.textContent = text.slice(0, maxCharacters).trim() + '…'
|
||||
}
|
||||
}, [children, maxCharacters])
|
||||
|
||||
return (
|
||||
<span
|
||||
{...props}
|
||||
ref={ref}
|
||||
style={{
|
||||
display: noOfLines !== undefined ? '-webkit-box' : undefined,
|
||||
WebkitBoxOrient: noOfLines !== undefined ? 'vertical' : undefined,
|
||||
WebkitLineClamp: noOfLines,
|
||||
}}
|
||||
className={cx(
|
||||
{ 'whitespace-nowrap': maxCharacters !== undefined },
|
||||
{
|
||||
'overflow-hidden':
|
||||
noOfLines !== undefined || maxCharacters !== undefined,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
2
Downloads/Voltaserve/ui/src/lib/hooks/index.ts
Normal file
2
Downloads/Voltaserve/ui/src/lib/hooks/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './page-pagination'
|
||||
export * from './page-monitor'
|
||||
16
Downloads/Voltaserve/ui/src/lib/hooks/page-monitor.ts
Normal file
16
Downloads/Voltaserve/ui/src/lib/hooks/page-monitor.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export type UsePageMonitorMonitorOptions = {
|
||||
totalPages: number
|
||||
totalElements: number
|
||||
steps: number[]
|
||||
}
|
||||
|
||||
export const usePageMonitor = ({
|
||||
totalPages,
|
||||
totalElements,
|
||||
steps,
|
||||
}: UsePageMonitorMonitorOptions) => {
|
||||
const hasPageSwitcher = totalPages > 1
|
||||
const hasSizeSelector = totalElements > steps[0]
|
||||
|
||||
return { hasPageSwitcher, hasSizeSelector }
|
||||
}
|
||||
77
Downloads/Voltaserve/ui/src/lib/hooks/page-pagination.ts
Normal file
77
Downloads/Voltaserve/ui/src/lib/hooks/page-pagination.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
export type NavigateArgs = {
|
||||
search: string
|
||||
}
|
||||
|
||||
export type NavigateFunction = (args: NavigateArgs) => void
|
||||
|
||||
export type LocationObject = {
|
||||
search: string
|
||||
}
|
||||
|
||||
export type StorageOptions = {
|
||||
enabled?: boolean
|
||||
prefix?: string
|
||||
namespace?: string
|
||||
}
|
||||
|
||||
export type UsePagePaginationOptions = {
|
||||
navigate: NavigateFunction
|
||||
location: LocationObject
|
||||
storage?: StorageOptions
|
||||
steps?: number[]
|
||||
}
|
||||
|
||||
export const usePagePagination = ({
|
||||
navigate,
|
||||
location,
|
||||
storage = {
|
||||
enabled: false,
|
||||
prefix: 'app',
|
||||
namespace: 'main',
|
||||
},
|
||||
steps = [5, 10, 20, 40, 80, 100],
|
||||
}: UsePagePaginationOptions) => {
|
||||
const queryParams = useMemo(
|
||||
() => new URLSearchParams(location.search),
|
||||
[location.search],
|
||||
)
|
||||
const page = Number(queryParams.get('page')) || 1
|
||||
const storageSizeKey = useMemo(
|
||||
() => `${storage.prefix}_${storage.namespace}_pagination_size`,
|
||||
[storage],
|
||||
)
|
||||
const [size, setSize] = useState(
|
||||
localStorage.getItem(storageSizeKey) && storage.enabled
|
||||
? parseInt(localStorage.getItem(storageSizeKey) as string)
|
||||
: steps[0],
|
||||
)
|
||||
|
||||
const _setSize = useCallback(
|
||||
(size: number) => {
|
||||
setSize(size)
|
||||
if (size && storage.enabled) {
|
||||
localStorage.setItem(storageSizeKey, JSON.stringify(size))
|
||||
}
|
||||
},
|
||||
[storageSizeKey, storage],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!queryParams.has('page')) {
|
||||
queryParams.set('page', '1')
|
||||
navigate({ search: `?${queryParams.toString()}` })
|
||||
}
|
||||
}, [queryParams, navigate])
|
||||
|
||||
const setPage = useCallback(
|
||||
(page: number) => {
|
||||
queryParams.set('page', String(page))
|
||||
navigate({ search: `?${queryParams.toString()}` })
|
||||
},
|
||||
[queryParams, navigate],
|
||||
)
|
||||
|
||||
return { page, size, steps, setPage, setSize: _setSize }
|
||||
}
|
||||
4
Downloads/Voltaserve/ui/src/lib/index.ts
Normal file
4
Downloads/Voltaserve/ui/src/lib/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './theme'
|
||||
export * from './variables'
|
||||
export * from './components'
|
||||
export * from './hooks'
|
||||
9
Downloads/Voltaserve/ui/src/lib/theme/breakpoints.ts
Normal file
9
Downloads/Voltaserve/ui/src/lib/theme/breakpoints.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
const breakpoints = {
|
||||
sm: '320px',
|
||||
md: '768px',
|
||||
lg: '960px',
|
||||
xl: '1200px',
|
||||
'2xl': '1536px',
|
||||
}
|
||||
|
||||
export default breakpoints
|
||||
16
Downloads/Voltaserve/ui/src/lib/theme/colors.ts
Normal file
16
Downloads/Voltaserve/ui/src/lib/theme/colors.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
const colors = {
|
||||
blue: {
|
||||
'50': '#E7EDFE',
|
||||
'100': '#BBCDFC',
|
||||
'200': '#8FACF9',
|
||||
'300': '#648CF7',
|
||||
'400': '#386CF5',
|
||||
'500': '#0C4CF3',
|
||||
'600': '#0A3CC2',
|
||||
'700': '#072D92',
|
||||
'800': '#051E61',
|
||||
'900': '#020F31',
|
||||
},
|
||||
}
|
||||
|
||||
export default colors
|
||||
@@ -0,0 +1,14 @@
|
||||
const breadcrumb = {
|
||||
baseStyle: {
|
||||
link: {
|
||||
_active: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
_focus: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default breadcrumb
|
||||
27
Downloads/Voltaserve/ui/src/lib/theme/components/button.ts
Normal file
27
Downloads/Voltaserve/ui/src/lib/theme/components/button.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { mode, StyleFunctionProps } from '@chakra-ui/theme-tools'
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const button = {
|
||||
baseStyle: {
|
||||
borderRadius: variables.borderRadiusMd,
|
||||
fontWeight: variables.bodyFontWeight,
|
||||
},
|
||||
sizes: {
|
||||
md: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
},
|
||||
xs: {
|
||||
fontSize: '12px',
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
'solid-gray': (props: StyleFunctionProps) => ({
|
||||
bg: mode('gray.100', 'gray.700')(props),
|
||||
_hover: {
|
||||
bg: mode('gray.200', 'gray.600')(props),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
export default button
|
||||
19
Downloads/Voltaserve/ui/src/lib/theme/components/checkbox.ts
Normal file
19
Downloads/Voltaserve/ui/src/lib/theme/components/checkbox.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const checkbox = {
|
||||
baseStyle: {
|
||||
control: {
|
||||
borderRadius: '50%',
|
||||
},
|
||||
},
|
||||
sizes: {
|
||||
md: {
|
||||
control: { w: '20px', h: '20px' },
|
||||
label: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default checkbox
|
||||
10
Downloads/Voltaserve/ui/src/lib/theme/components/heading.ts
Normal file
10
Downloads/Voltaserve/ui/src/lib/theme/components/heading.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const heading = {
|
||||
baseStyle: {
|
||||
fontFamily: variables.headingFontFamily,
|
||||
fontWeight: variables.headingFontWeight,
|
||||
},
|
||||
}
|
||||
|
||||
export default heading
|
||||
14
Downloads/Voltaserve/ui/src/lib/theme/components/input.ts
Normal file
14
Downloads/Voltaserve/ui/src/lib/theme/components/input.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const input = {
|
||||
sizes: {
|
||||
md: {
|
||||
field: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
borderRadius: variables.borderRadiusMd,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default input
|
||||
21
Downloads/Voltaserve/ui/src/lib/theme/components/link.ts
Normal file
21
Downloads/Voltaserve/ui/src/lib/theme/components/link.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
const link = {
|
||||
baseStyle: {
|
||||
textDecoration: 'underline',
|
||||
_active: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
_focus: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
'no-underline': {
|
||||
textDecoration: 'none',
|
||||
_hover: {
|
||||
textDecoration: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default link
|
||||
13
Downloads/Voltaserve/ui/src/lib/theme/components/menu.ts
Normal file
13
Downloads/Voltaserve/ui/src/lib/theme/components/menu.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
const menu = {
|
||||
baseStyle: {
|
||||
list: {
|
||||
borderRadius: '15px',
|
||||
py: '12px',
|
||||
},
|
||||
item: {
|
||||
px: '12px',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default menu
|
||||
14
Downloads/Voltaserve/ui/src/lib/theme/components/modal.ts
Normal file
14
Downloads/Voltaserve/ui/src/lib/theme/components/modal.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const modal = {
|
||||
baseStyle: {
|
||||
dialog: {
|
||||
borderRadius: variables.borderRadius,
|
||||
},
|
||||
closeButton: {
|
||||
borderRadius: '50%',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default modal
|
||||
21
Downloads/Voltaserve/ui/src/lib/theme/components/popover.ts
Normal file
21
Downloads/Voltaserve/ui/src/lib/theme/components/popover.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const popover = {
|
||||
baseStyle: {
|
||||
content: {
|
||||
borderRadius: '15px',
|
||||
padding: variables.spacingXs,
|
||||
boxShadow: 'none',
|
||||
_focus: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
closeButton: {
|
||||
borderRadius: '50%',
|
||||
top: '10px',
|
||||
right: '10px',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default popover
|
||||
37
Downloads/Voltaserve/ui/src/lib/theme/components/progress.ts
Normal file
37
Downloads/Voltaserve/ui/src/lib/theme/components/progress.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { progressAnatomy as parts } from '@chakra-ui/anatomy'
|
||||
import {
|
||||
mode,
|
||||
PartsStyleFunction,
|
||||
StyleFunctionProps,
|
||||
SystemStyleFunction,
|
||||
SystemStyleObject,
|
||||
} from '@chakra-ui/theme-tools'
|
||||
import { variables } from '../../variables'
|
||||
|
||||
function filledStyle(props: StyleFunctionProps): SystemStyleObject {
|
||||
const { colorScheme, hasStripe } = props
|
||||
if (hasStripe) {
|
||||
return { bg: variables.gradiant }
|
||||
} else {
|
||||
return { bgColor: mode(`${colorScheme}.500`, `${colorScheme}.200`)(props) }
|
||||
}
|
||||
}
|
||||
|
||||
const baseStyleFilledTrack: SystemStyleFunction = (props: any) => {
|
||||
return {
|
||||
...filledStyle(props),
|
||||
}
|
||||
}
|
||||
|
||||
const baseStyle: PartsStyleFunction<typeof parts> = (props: any) => ({
|
||||
filledTrack: baseStyleFilledTrack(props),
|
||||
track: {
|
||||
borderRadius: variables.borderRadius,
|
||||
},
|
||||
})
|
||||
|
||||
const progress = {
|
||||
baseStyle,
|
||||
}
|
||||
|
||||
export default progress
|
||||
14
Downloads/Voltaserve/ui/src/lib/theme/components/select.ts
Normal file
14
Downloads/Voltaserve/ui/src/lib/theme/components/select.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const select = {
|
||||
sizes: {
|
||||
md: {
|
||||
field: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
borderRadius: variables.borderRadiusMd,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default select
|
||||
31
Downloads/Voltaserve/ui/src/lib/theme/components/tabs.ts
Normal file
31
Downloads/Voltaserve/ui/src/lib/theme/components/tabs.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { mode, StyleFunctionProps } from '@chakra-ui/theme-tools'
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const tab = {
|
||||
variants: {
|
||||
'solid-rounded': (props: StyleFunctionProps) => ({
|
||||
tab: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
_focus: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
_selected: {
|
||||
bg: mode('black', 'white')(props),
|
||||
},
|
||||
},
|
||||
tabpanel: {
|
||||
p: '60px 0 0 0',
|
||||
},
|
||||
}),
|
||||
'line': {
|
||||
tab: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
_focus: {
|
||||
boxShadow: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default tab
|
||||
12
Downloads/Voltaserve/ui/src/lib/theme/components/textarea.ts
Normal file
12
Downloads/Voltaserve/ui/src/lib/theme/components/textarea.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const textarea = {
|
||||
sizes: {
|
||||
md: {
|
||||
fontSize: variables.bodyFontSize,
|
||||
borderRadius: variables.borderRadiusSm,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default textarea
|
||||
10
Downloads/Voltaserve/ui/src/lib/theme/components/tooltip.ts
Normal file
10
Downloads/Voltaserve/ui/src/lib/theme/components/tooltip.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { variables } from '../../variables'
|
||||
|
||||
const tooltip = {
|
||||
baseStyle: {
|
||||
borderRadius: variables.borderRadius,
|
||||
padding: '5px 15px 5px 15px',
|
||||
},
|
||||
}
|
||||
|
||||
export default tooltip
|
||||
44
Downloads/Voltaserve/ui/src/lib/theme/index.ts
Normal file
44
Downloads/Voltaserve/ui/src/lib/theme/index.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { extendTheme } from '@chakra-ui/react'
|
||||
import breakpoints from './breakpoints'
|
||||
import colors from './colors'
|
||||
import Breadcrumb from './components/breadcrumb'
|
||||
import Button from './components/button'
|
||||
import Checkbox from './components/checkbox'
|
||||
import Heading from './components/heading'
|
||||
import Input from './components/input'
|
||||
import Link from './components/link'
|
||||
import Menu from './components/menu'
|
||||
import Modal from './components/modal'
|
||||
import Popover from './components/popover'
|
||||
import Progress from './components/progress'
|
||||
import Select from './components/select'
|
||||
import Tabs from './components/tabs'
|
||||
import Textarea from './components/textarea'
|
||||
import Tooltip from './components/tooltip'
|
||||
import styles from './styles'
|
||||
import typography from './typography'
|
||||
|
||||
const overrides = {
|
||||
breakpoints,
|
||||
styles,
|
||||
colors,
|
||||
...typography,
|
||||
components: {
|
||||
Button,
|
||||
Heading,
|
||||
Checkbox,
|
||||
Select,
|
||||
Input,
|
||||
Textarea,
|
||||
Modal,
|
||||
Link,
|
||||
Progress,
|
||||
Tabs,
|
||||
Tooltip,
|
||||
Popover,
|
||||
Breadcrumb,
|
||||
Menu,
|
||||
},
|
||||
}
|
||||
|
||||
export const theme = extendTheme(overrides)
|
||||
13
Downloads/Voltaserve/ui/src/lib/theme/styles.ts
Normal file
13
Downloads/Voltaserve/ui/src/lib/theme/styles.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { variables } from '../variables'
|
||||
|
||||
const styles = {
|
||||
global: {
|
||||
body: {
|
||||
fontFamily: variables.bodyFontFamily,
|
||||
fontSize: variables.bodyFontSize,
|
||||
fontWeight: variables.bodyFontWeight,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default styles
|
||||
10
Downloads/Voltaserve/ui/src/lib/theme/typography.ts
Normal file
10
Downloads/Voltaserve/ui/src/lib/theme/typography.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
const typography = {
|
||||
fontSizes: {
|
||||
xl: '18px',
|
||||
lg: '16px',
|
||||
md: '14px',
|
||||
sm: '12px',
|
||||
},
|
||||
}
|
||||
|
||||
export default typography
|
||||
4
Downloads/Voltaserve/ui/src/lib/types.ts
Normal file
4
Downloads/Voltaserve/ui/src/lib/types.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type StorageOptions = {
|
||||
prefix?: string
|
||||
namespace?: string
|
||||
}
|
||||
23
Downloads/Voltaserve/ui/src/lib/variables.cjs
Normal file
23
Downloads/Voltaserve/ui/src/lib/variables.cjs
Normal file
@@ -0,0 +1,23 @@
|
||||
module.exports = {
|
||||
variables: {
|
||||
headingFontFamily:
|
||||
"'Unbounded', Almarai, 'Noto Sans JP', 'Noto Sans TC', 'Noto Sans SC', 'Noto Sans KR', Poppins, 'Noto Sans Bengali'",
|
||||
headingFontSize: '24px',
|
||||
headingFontWeight: '500',
|
||||
bodyFontFamily: 'IBM Plex Sans',
|
||||
bodyFontSize: '14px',
|
||||
bodyFontWeight: '400',
|
||||
spacingXs: '5px',
|
||||
spacingSm: '10px',
|
||||
spacing: '15px',
|
||||
spacingMd: '20px',
|
||||
spacingLg: '25px',
|
||||
spacingXl: '30px',
|
||||
spacing2Xl: '40px',
|
||||
borderRadiusMd: '30px',
|
||||
borderRadius: '20px',
|
||||
borderRadiusSm: '10px',
|
||||
borderRadiusXs: '5px',
|
||||
gradiant: 'linear-gradient(90deg, #00c9ff 0%, #92fe9d 100%)',
|
||||
},
|
||||
}
|
||||
21
Downloads/Voltaserve/ui/src/lib/variables.ts
Normal file
21
Downloads/Voltaserve/ui/src/lib/variables.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export const variables = {
|
||||
headingFontFamily:
|
||||
"'Unbounded', Almarai, 'Noto Sans JP', 'Noto Sans TC', 'Noto Sans SC', 'Noto Sans KR', Poppins, 'Noto Sans Bengali'",
|
||||
headingFontSize: '24px',
|
||||
headingFontWeight: '500',
|
||||
bodyFontFamily: 'IBM Plex Sans',
|
||||
bodyFontSize: '14px',
|
||||
bodyFontWeight: '400',
|
||||
spacingXs: '5px',
|
||||
spacingSm: '10px',
|
||||
spacing: '15px',
|
||||
spacingMd: '20px',
|
||||
spacingLg: '25px',
|
||||
spacingXl: '30px',
|
||||
spacing2Xl: '40px',
|
||||
borderRadiusMd: '30px',
|
||||
borderRadius: '20px',
|
||||
borderRadiusSm: '10px',
|
||||
borderRadiusXs: '5px',
|
||||
gradiant: 'linear-gradient(90deg, #00c9ff 0%, #92fe9d 100%)',
|
||||
}
|
||||
Reference in New Issue
Block a user