first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
import MaterialIcon from '@/shared/components/material-icon'
export default function OLTagIcon() {
return <MaterialIcon type="sell" />
}

View File

@@ -0,0 +1,17 @@
import Badge from '@/features/ui/components/bootstrap-5/badge'
function OLBadge(props: React.ComponentProps<typeof Badge>) {
let { bg, text, ...rest } = props
// For warning badges, use a light background by default. We still want the
// Bootstrap warning colour to be dark for text though, so make an
// adjustment here
if (bg === 'warning') {
bg = 'warning-light-bg'
text = 'warning'
}
return <Badge bg={bg} text={text} {...rest} />
}
export default OLBadge

View File

@@ -0,0 +1,7 @@
import { ButtonGroup, ButtonGroupProps } from 'react-bootstrap-5'
function OLButtonGroup({ as, ...rest }: ButtonGroupProps) {
return <ButtonGroup {...rest} as={as} />
}
export default OLButtonGroup

View File

@@ -0,0 +1,7 @@
import { ButtonToolbar, ButtonToolbarProps } from 'react-bootstrap-5'
function OLButtonToolbar(props: ButtonToolbarProps) {
return <ButtonToolbar {...props} />
}
export default OLButtonToolbar

View File

@@ -0,0 +1,13 @@
import { forwardRef } from 'react'
import type { ButtonProps } from '@/features/ui/components/types/button-props'
import Button from '../bootstrap-5/button'
export type OLButtonProps = ButtonProps
const OLButton = forwardRef<HTMLButtonElement, OLButtonProps>((props, ref) => {
return <Button {...props} ref={ref} />
})
OLButton.displayName = 'OLButton'
export default OLButton

View File

@@ -0,0 +1,12 @@
import { Card } from 'react-bootstrap-5'
import { FC } from 'react'
const OLCard: FC<{ className?: string }> = ({ children, className }) => {
return (
<Card className={className}>
<Card.Body>{children}</Card.Body>
</Card>
)
}
export default OLCard

View File

@@ -0,0 +1,15 @@
import { CloseButton, CloseButtonProps } from 'react-bootstrap-5'
import { useTranslation } from 'react-i18next'
import { forwardRef } from 'react'
const OLCloseButton = forwardRef<HTMLButtonElement, CloseButtonProps>(
(props, ref) => {
const { t } = useTranslation()
return <CloseButton ref={ref} aria-label={t('close')} {...props} />
}
)
OLCloseButton.displayName = 'OLCloseButton'
export default OLCloseButton

View File

@@ -0,0 +1,7 @@
import { Col } from 'react-bootstrap-5'
function OLCol(props: React.ComponentProps<typeof Col>) {
return <Col {...props} />
}
export default OLCol

View File

@@ -0,0 +1,14 @@
import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu'
import { DropdownItemProps } from '@/features/ui/components/types/dropdown-menu-props'
import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item'
// This represents a menu item. It wraps the item within an <li> element.
function OLDropdownMenuItem(props: DropdownItemProps) {
return (
<DropdownListItem>
<DropdownItem {...props} />
</DropdownListItem>
)
}
export default OLDropdownMenuItem

View File

@@ -0,0 +1,42 @@
import { Form, FormCheckProps } from 'react-bootstrap-5'
import { MergeAndOverride } from '../../../../../../types/utils'
import FormText from '../bootstrap-5/form/form-text'
type OLFormCheckboxProps = MergeAndOverride<
FormCheckProps,
{
inputRef?: React.MutableRefObject<HTMLInputElement | null>
} & (
| { description: string; id: string }
| { description?: undefined; id?: string }
)
>
function OLFormCheckbox(props: OLFormCheckboxProps) {
const { inputRef, ...rest } = props
return rest.type === 'radio' ? (
<Form.Check
ref={inputRef}
aria-describedby={rest.description ? `${rest.id}-description` : undefined}
{...rest}
label={
<>
{rest.label}
{rest.description && (
<FormText
id={`${rest.id}-description`}
className="form-check-label-description"
>
{rest.description}
</FormText>
)}
</>
}
/>
) : (
<Form.Check ref={inputRef} {...rest} />
)
}
export default OLFormCheckbox

View File

@@ -0,0 +1,30 @@
import { forwardRef } from 'react'
import FormControl, {
type OLBS5FormControlProps,
} from '@/features/ui/components/bootstrap-5/form/form-control'
import OLSpinner from '@/features/ui/components/ol/ol-spinner'
import type { BsPrefixRefForwardingComponent } from 'react-bootstrap-5/helpers'
type OLFormControlProps = OLBS5FormControlProps & {
'data-ol-dirty'?: unknown
'main-field'?: any // For the CM6's benefit in the editor search panel
loading?: boolean
}
const OLFormControl: BsPrefixRefForwardingComponent<
'input',
OLFormControlProps
> = forwardRef<HTMLInputElement, OLFormControlProps>((props, ref) => {
const { append, ...rest } = props
return (
<FormControl
ref={ref}
{...rest}
append={rest.loading ? <OLSpinner size="sm" /> : append}
/>
)
})
OLFormControl.displayName = 'OLFormControl'
export default OLFormControl

View File

@@ -0,0 +1,14 @@
import { Form } from 'react-bootstrap-5'
import { ComponentProps } from 'react'
import FormFeedback from '@/features/ui/components/bootstrap-5/form/form-feedback'
type OLFormFeedbackProps = Pick<
ComponentProps<typeof Form.Control.Feedback>,
'type' | 'className' | 'children'
>
function OLFormFeedback(props: OLFormFeedbackProps) {
return <FormFeedback {...props} />
}
export default OLFormFeedback

View File

@@ -0,0 +1,8 @@
import { FormGroupProps } from 'react-bootstrap-5'
import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
function OLFormGroup(props: FormGroupProps) {
return <FormGroup {...props} />
}
export default OLFormGroup

View File

@@ -0,0 +1,7 @@
import { Form } from 'react-bootstrap-5'
function OLFormLabel(props: React.ComponentProps<(typeof Form)['Label']>) {
return <Form.Label {...props} />
}
export default OLFormLabel

View File

@@ -0,0 +1,11 @@
import { forwardRef } from 'react'
import { Form, FormSelectProps } from 'react-bootstrap-5'
const OLFormSelect = forwardRef<HTMLSelectElement, FormSelectProps>(
(props, ref) => {
return <Form.Select ref={ref} {...props} />
}
)
OLFormSelect.displayName = 'OLFormSelect'
export default OLFormSelect

View File

@@ -0,0 +1,20 @@
import { FormCheck, FormCheckProps, FormLabel } from 'react-bootstrap-5'
type OLFormSwitchProps = FormCheckProps & {
inputRef?: React.MutableRefObject<HTMLInputElement | null>
}
function OLFormSwitch(props: OLFormSwitchProps) {
const { inputRef, label, id, ...rest } = props
return (
<>
<FormCheck type="switch" ref={inputRef} id={id} {...rest} />
<FormLabel htmlFor={id} visuallyHidden>
{label}
</FormLabel>
</>
)
}
export default OLFormSwitch

View File

@@ -0,0 +1,9 @@
import FormText, {
FormTextProps,
} from '@/features/ui/components/bootstrap-5/form/form-text'
function OLFormText({ as = 'div', ...rest }: FormTextProps) {
return <FormText {...rest} as={as} />
}
export default OLFormText

View File

@@ -0,0 +1,8 @@
import { Form } from 'react-bootstrap-5'
import { ComponentProps } from 'react'
function OLForm(props: ComponentProps<typeof Form>) {
return <Form {...props} />
}
export default OLForm

View File

@@ -0,0 +1,15 @@
import { forwardRef } from 'react'
import type { IconButtonProps } from '@/features/ui/components/types/icon-button-props'
import IconButton from '../bootstrap-5/icon-button'
export type OLIconButtonProps = IconButtonProps
const OLIconButton = forwardRef<HTMLButtonElement, OLIconButtonProps>(
(props, ref) => {
// BS5 tooltip relies on the ref
return <IconButton {...props} ref={ref} />
}
)
OLIconButton.displayName = 'OLIconButton'
export default OLIconButton

View File

@@ -0,0 +1,15 @@
import { ListGroupItem, ListGroupItemProps } from 'react-bootstrap-5'
function OLListGroupItem(props: ListGroupItemProps) {
const as = props.as ?? 'button'
return (
<ListGroupItem
{...props}
as={as}
type={as === 'button' ? 'button' : undefined}
/>
)
}
export default OLListGroupItem

View File

@@ -0,0 +1,9 @@
import { ListGroup, ListGroupProps } from 'react-bootstrap-5'
function OLListGroup(props: ListGroupProps) {
const as = props.as ?? 'div'
return <ListGroup {...props} as={as} />
}
export default OLListGroup

View File

@@ -0,0 +1,37 @@
import {
Modal,
ModalProps,
ModalHeaderProps,
ModalTitleProps,
ModalFooterProps,
} from 'react-bootstrap-5'
import { ModalBodyProps } from 'react-bootstrap-5/ModalBody'
type OLModalProps = ModalProps & {
size?: 'sm' | 'lg'
onHide: () => void
}
export default function OLModal({ children, ...props }: OLModalProps) {
return <Modal {...props}>{children}</Modal>
}
export function OLModalHeader({ children, ...props }: ModalHeaderProps) {
return <Modal.Header {...props}>{children}</Modal.Header>
}
export function OLModalTitle({ children, ...props }: ModalTitleProps) {
return (
<Modal.Title as="h2" {...props}>
{children}
</Modal.Title>
)
}
export function OLModalBody({ children, ...props }: ModalBodyProps) {
return <Modal.Body {...props}>{children}</Modal.Body>
}
export function OLModalFooter({ children, ...props }: ModalFooterProps) {
return <Modal.Footer {...props}>{children}</Modal.Footer>
}

View File

@@ -0,0 +1,11 @@
import Notification from '@/shared/components/notification'
function OLNotification(props: React.ComponentProps<typeof Notification>) {
return (
<div className="notification-list">
<Notification {...props} />
</div>
)
}
export default OLNotification

View File

@@ -0,0 +1,7 @@
import { Overlay, OverlayProps } from 'react-bootstrap-5'
function OLOverlay(props: OverlayProps) {
return <Overlay {...props} />
}
export default OLOverlay

View File

@@ -0,0 +1,19 @@
import { Card, CardBody } from 'react-bootstrap-5'
import { FC } from 'react'
import classNames from 'classnames'
// This wraps the Bootstrap 5 Card component but is restricted to the very
// basic way we're using it, which is as a container for page content. The
// Bootstrap 3 equivalent previously in our codebase is a div with class "card"
const OLPageContentCard: FC<{ className?: string }> = ({
children,
className,
}) => {
return (
<Card className={classNames('page-content-card', className)}>
<CardBody>{children}</CardBody>
</Card>
)
}
export default OLPageContentCard

View File

@@ -0,0 +1,20 @@
import { forwardRef } from 'react'
import { Popover, PopoverProps } from 'react-bootstrap-5'
type OLPopoverProps = Omit<PopoverProps, 'title'> & {
title?: React.ReactNode
}
const OLPopover = forwardRef<HTMLDivElement, OLPopoverProps>((props, ref) => {
const { title, children, ...bs5Props } = props
return (
<Popover {...bs5Props} ref={ref}>
{title && <Popover.Header>{title}</Popover.Header>}
<Popover.Body>{children}</Popover.Body>
</Popover>
)
})
OLPopover.displayName = 'OLPopover'
export default OLPopover

View File

@@ -0,0 +1,7 @@
import { Row } from 'react-bootstrap-5'
function OLRow(props: React.ComponentProps<typeof Row>) {
return <Row {...props} />
}
export default OLRow

View File

@@ -0,0 +1,16 @@
import { Spinner } from 'react-bootstrap-5'
export type OLSpinnerSize = 'sm' | 'lg'
function OLSpinner({ size = 'sm' }: { size: OLSpinnerSize }) {
return (
<Spinner
size={size === 'sm' ? 'sm' : undefined}
animation="border"
aria-hidden="true"
role="status"
/>
)
}
export default OLSpinner

View File

@@ -0,0 +1,7 @@
import Table from '@/features/ui/components/bootstrap-5/table'
function OLTable(props: React.ComponentProps<typeof Table>) {
return <Table {...props} />
}
export default OLTable

View File

@@ -0,0 +1,12 @@
import Tag from '@/features/ui/components/bootstrap-5/tag'
import { forwardRef } from 'react'
type OLTagProps = React.ComponentProps<typeof Tag>
const OLTag = forwardRef<HTMLElement, OLTagProps>((props: OLTagProps, ref) => {
return <Tag ref={ref} {...props} />
})
OLTag.displayName = 'OLTag'
export default OLTag

View File

@@ -0,0 +1,19 @@
import { CSSProperties, FC } from 'react'
import { ToastContainer as BS5ToastContainer } from 'react-bootstrap-5'
type OLToastContainerProps = {
style?: CSSProperties
className?: string
}
export const OLToastContainer: FC<OLToastContainerProps> = ({
children,
className,
style,
}) => {
return (
<BS5ToastContainer className={className} style={style}>
{children}
</BS5ToastContainer>
)
}

View File

@@ -0,0 +1,89 @@
import classNames from 'classnames'
import { Toast as BS5Toast } from 'react-bootstrap-5'
import {
NotificationIcon,
NotificationType,
} from '../../../../shared/components/notification'
import { useTranslation } from 'react-i18next'
import MaterialIcon from '../../../../shared/components/material-icon'
import { ReactNode, useCallback, useState } from 'react'
export type OLToastProps = {
type: NotificationType
className?: string
title?: string
content: string | ReactNode
isDismissible?: boolean
onDismiss?: () => void
autoHide?: boolean
delay?: number
}
export const OLToast = ({
type = 'info',
className = '',
content,
title,
isDismissible,
onDismiss,
autoHide,
delay,
}: OLToastProps) => {
const { t } = useTranslation()
const [show, setShow] = useState(true)
const toastClassName = classNames(
'notification',
`notification-type-${type}`,
className,
'toast-content'
)
const handleClose = useCallback(() => {
setShow(false)
}, [])
const handleOnHidden = useCallback(() => {
if (onDismiss) onDismiss()
}, [onDismiss])
const toastElement = (
<div className={toastClassName}>
<NotificationIcon notificationType={type} />
<div className="notification-content-and-cta">
<div className="notification-content">
{title && (
<p>
<b>{title}</b>
</p>
)}
{content}
</div>
</div>
{isDismissible && (
<div className="notification-close-btn">
<button
aria-label={t('close')}
data-bs-dismiss="toast"
onClick={handleClose}
>
<MaterialIcon type="close" />
</button>
</div>
)}
</div>
)
return (
<BS5Toast
onClose={handleClose}
autohide={autoHide}
onExited={handleOnHidden}
delay={delay}
show={show}
>
{toastElement}
</BS5Toast>
)
}

View File

@@ -0,0 +1,7 @@
import { ToggleButtonGroup, ToggleButtonGroupProps } from 'react-bootstrap-5'
function OLToggleButtonGroup<T>(props: ToggleButtonGroupProps<T>) {
return <ToggleButtonGroup {...props} />
}
export default OLToggleButtonGroup

View File

@@ -0,0 +1,7 @@
import { ToggleButton, ToggleButtonProps } from 'react-bootstrap-5'
function OLToggleButton(props: ToggleButtonProps) {
return <ToggleButton {...props} />
}
export default OLToggleButton

View File

@@ -0,0 +1,7 @@
import Tooltip from '@/features/ui/components/bootstrap-5/tooltip'
function OLTooltip(props: React.ComponentProps<typeof Tooltip>) {
return <Tooltip {...props} />
}
export default OLTooltip