From 8b7d78725d013264eeafbf041a3e42119257cb81 Mon Sep 17 00:00:00 2001 From: ShinCurry Date: Tue, 24 Nov 2020 18:57:57 +0800 Subject: [PATCH 1/6] Reorganize code structure --- docs/PRINCIPLE.md | 8 +- docs/PRINCIPLE.zh-CN.md | 8 +- src/components/Accordion/Accordion.tsx | 6 +- src/components/Accordion/AccordionPane.tsx | 6 +- src/components/Breadcrumb/Breadcrumb.tsx | 6 +- src/components/Button/Button.tsx | 4 +- src/components/Cascader/Cascader.tsx | 6 +- src/components/Checkbox/Checkbox.tsx | 6 +- src/components/Collapse/Collapse.tsx | 6 +- src/components/DatePicker/DatePicker.tsx | 6 +- src/components/Dialog/Dialog.tsx | 6 +- src/components/Drawer/Drawer.tsx | 8 +- src/components/Icon/Icon.tsx | 6 +- src/components/Input/NumberField.tsx | 6 +- src/components/Input/TextArea.tsx | 6 +- src/components/Input/TextField.tsx | 6 +- src/components/Label/CountdownLabel.tsx | 6 +- src/components/Label/DateLabel.tsx | 6 +- src/components/Label/MoneyLabel.tsx | 6 +- src/components/Label/NumberAbbrLabel.tsx | 6 +- src/components/Label/TimeLabel.tsx | 6 +- src/components/Layout/Layout.tsx | 6 +- src/components/Layout/LayoutAside.tsx | 6 +- src/components/Layout/LayoutFooter.tsx | 6 +- src/components/Layout/LayoutHeader.tsx | 6 +- src/components/Layout/LayoutMain.tsx | 6 +- src/components/Layout/LayoutNav.tsx | 6 +- src/components/ListBox/ListBox.tsx | 4 +- src/components/Loading/LoadingCover.tsx | 6 +- src/components/Loading/LoadingSpinner.tsx | 6 +- src/components/Page/Page.tsx | 6 +- src/components/Page/PageAnnotatedSection.tsx | 6 +- src/components/Page/PageSection.tsx | 6 +- src/components/Pagination/PageInfo.tsx | 6 +- src/components/Pagination/PageJumper.tsx | 6 +- src/components/Pagination/PageList.tsx | 6 +- src/components/Pagination/PageNextButton.tsx | 6 +- src/components/Pagination/PagePrevButton.tsx | 6 +- src/components/Pagination/PageSelector.tsx | 6 +- src/components/Pagination/PageSize.tsx | 6 +- src/components/Pagination/Pagination.tsx | 6 +- src/components/Popover/Popover.tsx | 6 +- src/components/ProgressBar/ProgressBar.tsx | 6 +- src/components/Radio/Radio.tsx | 6 +- src/components/Radio/RadioGroup.tsx | 6 +- .../SegmentControl/SegmentControl.tsx | 6 +- src/components/Select/HTMLSelect.tsx | 4 +- src/components/Select/Select.tsx | 6 +- src/components/Skeleton/Paragraph.tsx | 6 +- src/components/Skeleton/Picture.tsx | 6 +- src/components/Skeleton/Skeleton.tsx | 4 +- src/components/Skeleton/Title.tsx | 6 +- src/components/Slider/Slider.tsx | 6 +- src/components/Stepper/Stepper.tsx | 6 +- src/components/Switch/Switch.tsx | 6 +- src/components/Table/Table.tsx | 6 +- src/components/Tabs/Tab.tsx | 6 +- src/components/Tabs/Tabs.tsx | 6 +- src/components/Tag/Tag.tsx | 6 +- src/components/Toast/Toast.tsx | 6 +- src/components/Toast/Toaster.tsx | 6 +- src/core/UUIComponent.tsx | 180 +++++++ src/core/index.ts | 3 + src/core/modules/UUIComponentProps.ts | 55 ++ .../UUICustomizeAriaAttributes.ts | 0 src/core/modules/UUICustomizeNode.tsx | 194 +++++++ src/core/utils/mergeCustomize.tsx | 29 ++ src/{ => core}/utils/mergeRefs.ts | 0 src/core/utils/typeHelper.ts | 1 + src/core/uui.tsx | 481 ------------------ src/index.ts | 13 +- stories/style/Nodes.scss | 44 -- stories/style/storybook.scss | 1 - stories/utils/Nodes.tsx | 101 ---- tests/core/uui.test.tsx | 40 +- 75 files changed, 670 insertions(+), 836 deletions(-) create mode 100644 src/core/UUIComponent.tsx create mode 100644 src/core/index.ts create mode 100644 src/core/modules/UUIComponentProps.ts rename src/core/{types => modules}/UUICustomizeAriaAttributes.ts (100%) create mode 100644 src/core/modules/UUICustomizeNode.tsx create mode 100644 src/core/utils/mergeCustomize.tsx rename src/{ => core}/utils/mergeRefs.ts (100%) create mode 100644 src/core/utils/typeHelper.ts delete mode 100644 src/core/uui.tsx delete mode 100644 stories/style/Nodes.scss delete mode 100644 stories/utils/Nodes.tsx diff --git a/docs/PRINCIPLE.md b/docs/PRINCIPLE.md index 1e1e40c..39edf1b 100644 --- a/docs/PRINCIPLE.md +++ b/docs/PRINCIPLE.md @@ -52,7 +52,7 @@ Let's take the component `Button` as an example: import classNames from 'classnames'; import { omit } from 'lodash-es'; import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { LoadingSpinner } from '../Loading'; export interface ButtonStylingProps { @@ -69,7 +69,7 @@ export interface ButtonFeatureProps extends React.ButtonHTMLAttributes[0] +export type ButtonProps = UUIFunctionComponentProps ``` The first is that we define two Props, namely `ButtonStylingProps` and `ButtonFeatureProps`. These two Props are used as attributes of the feature function of the component `Button`, so they are defined in the `src/components/Button/Button.tsx` file instead of the `src/core/uui.tsx` file. @@ -298,7 +298,7 @@ UUI's components support defining the `prefix` and `separator` of the component For example, we can define a component during the development phase: ```tsx -const Test = UUI.FunctionComponent({ +const Test = UUIFunctionComponent({ prefix: "XUI", name: "Test", separator: "+", diff --git a/docs/PRINCIPLE.zh-CN.md b/docs/PRINCIPLE.zh-CN.md index 9cb8ad0..9fd5a2f 100644 --- a/docs/PRINCIPLE.zh-CN.md +++ b/docs/PRINCIPLE.zh-CN.md @@ -52,7 +52,7 @@ UUI 的 UI 组件有一些共有通用的功能,为了不重复在每个组件 import classNames from 'classnames'; import { omit } from 'lodash-es'; import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { LoadingSpinner } from '../Loading'; export interface ButtonStylingProps { @@ -69,7 +69,7 @@ export interface ButtonFeatureProps extends React.ButtonHTMLAttributes[0] +export type ButtonProps = UUIFunctionComponentProps ``` 首先是我们定义了两个 Props,分别是 `ButtonStylingProps` 和 `ButtonFeatureProps`。这两个 Props 是作为 `按钮 Button` 这个组件业务功能的属性,所以它们被定义在了 `src/components/Button/Button.tsx` 文件,而不是 `src/core/uui.tsx` 文件里。 @@ -299,7 +299,7 @@ UUI 的组件支持在开发阶段和使用阶段定义组件的前缀和分隔 比如我们可以在开发阶段定义一个组件: ```tsx -const Test = UUI.FunctionComponent({ +const Test = UUIFunctionComponent({ prefix: "XUI", name: "Test", separator: "+", diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx index 7301a38..2b94f38 100644 --- a/src/components/Accordion/Accordion.tsx +++ b/src/components/Accordion/Accordion.tsx @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Collapse } from '../Collapse'; import { createGroupedComponent } from '../../utils/createGroupedComponent'; import { AccordionPane, AccordionPaneProps } from './AccordionPane'; @@ -11,7 +11,7 @@ export interface AccordionFeatureProps { children: React.ReactElement[]; } -const _Accordion = UUI.FunctionComponent({ +const _Accordion = UUIFunctionComponent({ name: 'Accordion', nodes: { Root: 'div', @@ -64,7 +64,7 @@ const _Accordion = UUI.FunctionComponent({ ) }) -export type AccordionProps = Parameters[0] +export type AccordionProps = UUIFunctionComponentProps const Accordion = createGroupedComponent(_Accordion, { Pane: AccordionPane, diff --git a/src/components/Accordion/AccordionPane.tsx b/src/components/Accordion/AccordionPane.tsx index d68c8df..b880a8b 100644 --- a/src/components/Accordion/AccordionPane.tsx +++ b/src/components/Accordion/AccordionPane.tsx @@ -1,5 +1,5 @@ import React, { useContext, useMemo } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Collapse } from '../Collapse'; import { Icons } from '../../icons/Icons'; import classNames from 'classnames'; @@ -13,7 +13,7 @@ export interface AccordionPaneFeatureProps { disabled?: boolean; } -export const AccordionPane = UUI.FunctionComponent({ +export const AccordionPane = UUIFunctionComponent({ name: 'AccordionPane', nodes: { Root: 'div', @@ -88,4 +88,4 @@ export const AccordionPane = UUI.FunctionComponent({ ) }) -export type AccordionPaneProps = Parameters[0] +export type AccordionPaneProps = UUIFunctionComponentProps diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 77b8c47..7bd98a9 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import ReactHelper from '../../utils/ReactHelper'; import classNames from 'classnames'; @@ -16,7 +16,7 @@ export interface BreadcrumbFeatureProps { items: BreadcrumbItem[]; } -export const Breadcrumb = UUI.FunctionComponent({ +export const Breadcrumb = UUIFunctionComponent({ name: 'Breadcrumb', nodes: { Root: 'nav', @@ -62,4 +62,4 @@ export const Breadcrumb = UUI.FunctionComponent({ ) }) -export type BreadcrumbProps = Parameters[0] +export type BreadcrumbProps = UUIFunctionComponentProps diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx index 1ade304..51d91b0 100644 --- a/src/components/Button/Button.tsx +++ b/src/components/Button/Button.tsx @@ -1,9 +1,9 @@ import classNames from 'classnames'; import { omit } from 'lodash-es'; import React, { useRef } from 'react'; -import { UUI, UUIFunctionComponentProps } from '../../core/uui'; import { LoadingSpinner } from '../Loading'; import { KeyCode } from '../../utils/keyboardHelper'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface ButtonStylingProps { styling?: { @@ -19,7 +19,7 @@ export interface ButtonFeatureProps extends React.ButtonHTMLAttributes[0] +export type CascaderProps = UUIFunctionComponentProps function findOneInAllOptions(value: string | null, options: CascaderOption[]): CascaderOption | null { if (value === null) return null diff --git a/src/components/Checkbox/Checkbox.tsx b/src/components/Checkbox/Checkbox.tsx index 644b141..c686295 100644 --- a/src/components/Checkbox/Checkbox.tsx +++ b/src/components/Checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ import React, { useRef } from 'react'; import { omit } from 'lodash-es'; import classNames from 'classnames'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { KeyCode } from '../../utils/keyboardHelper'; export interface CheckboxFeatureProps { @@ -34,7 +34,7 @@ export interface CheckboxFeatureProps { disabled?: boolean; } -export const Checkbox = UUI.FunctionComponent({ +export const Checkbox = UUIFunctionComponent({ name: 'Checkbox', nodes: { Root: 'label', @@ -88,4 +88,4 @@ export const Checkbox = UUI.FunctionComponent({ ) }) -export type CheckboxProps = Parameters[0] \ No newline at end of file +export type CheckboxProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Collapse/Collapse.tsx b/src/components/Collapse/Collapse.tsx index d5c6482..e2cbadc 100644 --- a/src/components/Collapse/Collapse.tsx +++ b/src/components/Collapse/Collapse.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import classNames from 'classnames'; export interface CollapseFeatureProps { @@ -7,7 +7,7 @@ export interface CollapseFeatureProps { children: React.ReactNode; } -export const Collapse = UUI.FunctionComponent({ +export const Collapse = UUIFunctionComponent({ name: 'Collapse', nodes: { Root: 'div', @@ -24,4 +24,4 @@ export const Collapse = UUI.FunctionComponent({ ) }) -export type CollapseProps = Parameters[0] +export type CollapseProps = UUIFunctionComponentProps diff --git a/src/components/DatePicker/DatePicker.tsx b/src/components/DatePicker/DatePicker.tsx index 350330d..4f3a2f9 100644 --- a/src/components/DatePicker/DatePicker.tsx +++ b/src/components/DatePicker/DatePicker.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { format, isValid } from 'date-fns'; export interface DatePickerFeatureProps { @@ -17,7 +17,7 @@ export interface DatePickerFeatureProps { onChange?: (value: Date, event: React.ChangeEvent) => void; } -export const DatePicker = UUI.FunctionComponent({ +export const DatePicker = UUIFunctionComponent({ name: 'DatePicker', nodes: { Root: 'div', @@ -50,4 +50,4 @@ export const DatePicker = UUI.FunctionComponent({ ) }) -export type DatePickerProps = Parameters[0] +export type DatePickerProps = UUIFunctionComponentProps diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 88b5e58..f91f8df 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -2,7 +2,7 @@ import React, { useRef, useMemo, useEffect } from 'react'; import useFocusTrap from '@charlietango/use-focus-trap'; import classNames from 'classnames'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { useClickAway, useLockBodyScroll } from 'react-use'; import ReactDOM from 'react-dom'; import ReactHelper from '../../utils/ReactHelper'; @@ -48,7 +48,7 @@ export interface DialogFeatureProps { children?: React.ReactNode | string; } -export const Dialog = UUI.FunctionComponent({ +export const Dialog = UUIFunctionComponent({ name: 'Dialog', nodes: { Root: 'div', @@ -134,4 +134,4 @@ export const Dialog = UUI.FunctionComponent({ ) }) -export type DialogProps = Parameters[0] \ No newline at end of file +export type DialogProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Drawer/Drawer.tsx b/src/components/Drawer/Drawer.tsx index bfda614..3224130 100644 --- a/src/components/Drawer/Drawer.tsx +++ b/src/components/Drawer/Drawer.tsx @@ -1,12 +1,12 @@ import React, { useMemo, useRef, useCallback, useEffect } from 'react'; import ReactDOM from 'react-dom'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import classNames from 'classnames'; import { useClickAway, useLockBodyScroll } from 'react-use'; import ReactHelper from '../../utils/ReactHelper'; import useFocusTrap from '@charlietango/use-focus-trap'; import { KeyCode } from '../../utils/keyboardHelper'; -import { mergeRefs } from '../../utils/mergeRefs'; +import { mergeRefs } from '../../core/utils/mergeRefs'; export type DrawerPlacement = 'top' | 'right' | 'bottom' | 'left' @@ -55,7 +55,7 @@ export interface DrawerFeatureProps { placement?: DrawerPlacement; } -export const Drawer = UUI.FunctionComponent({ +export const Drawer = UUIFunctionComponent({ name: 'Drawer', nodes: { Root: 'div', @@ -139,4 +139,4 @@ export const Drawer = UUI.FunctionComponent({ ) }) -export type DrawerProps = Parameters[0] +export type DrawerProps = UUIFunctionComponentProps diff --git a/src/components/Icon/Icon.tsx b/src/components/Icon/Icon.tsx index 8b2e0d2..a543619 100644 --- a/src/components/Icon/Icon.tsx +++ b/src/components/Icon/Icon.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { isString } from 'lodash-es'; export type SvgrComponentProps = React.SVGAttributes @@ -43,7 +43,7 @@ export interface IconAnyFeatureProps extends BaseIconFeatureProps { } export type IconFeatureProps = IconImageFeatureProps | IconSvgFeatureProps | IconAnyFeatureProps -export const Icon = UUI.FunctionComponent({ +export const Icon = UUIFunctionComponent({ name: 'Icon', nodes: { Root: 'div', @@ -84,4 +84,4 @@ export const Icon = UUI.FunctionComponent({ ) }) -export type IconProps = Parameters[0] +export type IconProps = UUIFunctionComponentProps diff --git a/src/components/Input/NumberField.tsx b/src/components/Input/NumberField.tsx index a2b45fc..255c0d3 100644 --- a/src/components/Input/NumberField.tsx +++ b/src/components/Input/NumberField.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { limitPrecision, limitRange } from '../../utils/numberHelper'; import { LoadingSpinner } from '../Loading/LoadingSpinner'; import classNames from 'classnames'; @@ -60,7 +60,7 @@ export interface NumberFieldFeatureProps { onKeyDown?: (event: React.KeyboardEvent) => void; } -export const NumberField = UUI.FunctionComponent({ +export const NumberField = UUIFunctionComponent({ name: 'NumberField', nodes: { Root: 'div', @@ -113,4 +113,4 @@ export const NumberField = UUI.FunctionComponent({ ) }) -export type NumberFieldProps = Parameters[0] +export type NumberFieldProps = UUIFunctionComponentProps diff --git a/src/components/Input/TextArea.tsx b/src/components/Input/TextArea.tsx index 83152fd..10f6911 100644 --- a/src/components/Input/TextArea.tsx +++ b/src/components/Input/TextArea.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { LoadingSpinner } from '../Loading/LoadingSpinner'; import classNames from 'classnames'; @@ -43,7 +43,7 @@ export interface TextAreaFeatureProps { showLengthIndicator?: boolean; } -export const TextArea = UUI.FunctionComponent({ +export const TextArea = UUIFunctionComponent({ name: 'TextArea', nodes: { Root: 'div', @@ -96,4 +96,4 @@ export const TextArea = UUI.FunctionComponent({ ) }) -export type TextAreaProps = Parameters[0] +export type TextAreaProps = UUIFunctionComponentProps diff --git a/src/components/Input/TextField.tsx b/src/components/Input/TextField.tsx index 7f5738f..d2964bf 100644 --- a/src/components/Input/TextField.tsx +++ b/src/components/Input/TextField.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Button } from '../Button'; import { Icons } from '../../icons/Icons'; import { LoadingSpinner } from '../Loading/LoadingSpinner'; @@ -69,7 +69,7 @@ export interface TextFieldFeatureProps { onPasswordVisibleChange?: (value: boolean) => void; } -export const TextField = UUI.FunctionComponent({ +export const TextField = UUIFunctionComponent({ name: 'TextField', nodes: { Root: 'div', @@ -157,4 +157,4 @@ export const TextField = UUI.FunctionComponent({ ) }) -export type TextFieldProps = Parameters[0] +export type TextFieldProps = UUIFunctionComponentProps diff --git a/src/components/Label/CountdownLabel.tsx b/src/components/Label/CountdownLabel.tsx index d22f601..e681668 100644 --- a/src/components/Label/CountdownLabel.tsx +++ b/src/components/Label/CountdownLabel.tsx @@ -1,5 +1,5 @@ import React, { useState, useCallback } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { useInterval } from 'react-use'; import { format, differenceInMilliseconds } from 'date-fns'; import { addHours, addMilliseconds } from 'date-fns/esm'; @@ -27,7 +27,7 @@ export interface CountdownLabelFeatureProps { allowNegative?: boolean; } -export const CountdownLabel = UUI.FunctionComponent({ +export const CountdownLabel = UUIFunctionComponent({ name: 'CountdownLabel', nodes: { Root: 'label', @@ -61,4 +61,4 @@ export const CountdownLabel = UUI.FunctionComponent({ -export type CountdownLabelProps = Parameters[0] +export type CountdownLabelProps = UUIFunctionComponentProps diff --git a/src/components/Label/DateLabel.tsx b/src/components/Label/DateLabel.tsx index e43ce1c..bd3445e 100644 --- a/src/components/Label/DateLabel.tsx +++ b/src/components/Label/DateLabel.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { DateFormatterLocale, DateFormatterLocaleKinds, dateFormat } from '../../utils/dateFormatter'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface DateLabelFeatureProps { /** @@ -22,7 +22,7 @@ export interface DateLabelFeatureProps { kind: DateFormatterLocaleKinds[T][number]; } -export const DateLabel = UUI.FunctionComponent({ +export const DateLabel = UUIFunctionComponent({ name: 'DateLabel', nodes: { Root: 'label', @@ -36,4 +36,4 @@ export const DateLabel = UUI.FunctionComponent({ ) }) -export type DateLabelProps = Parameters[0] \ No newline at end of file +export type DateLabelProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Label/MoneyLabel.tsx b/src/components/Label/MoneyLabel.tsx index c8f9071..8f78f9e 100644 --- a/src/components/Label/MoneyLabel.tsx +++ b/src/components/Label/MoneyLabel.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { formatMoney } from '../../utils/moneyHelper'; export interface MoneyLabelFeatureProps { @@ -28,7 +28,7 @@ export interface MoneyLabelFeatureProps { decimal?: string; } -export const MoneyLabel = UUI.FunctionComponent({ +export const MoneyLabel = UUIFunctionComponent({ name: 'MoneyLabel', nodes: { Root: 'label', @@ -50,4 +50,4 @@ export const MoneyLabel = UUI.FunctionComponent({ ) }) -export type MoneyLabelProps = Parameters[0] +export type MoneyLabelProps = UUIFunctionComponentProps diff --git a/src/components/Label/NumberAbbrLabel.tsx b/src/components/Label/NumberAbbrLabel.tsx index 5bc58a4..00339ed 100644 --- a/src/components/Label/NumberAbbrLabel.tsx +++ b/src/components/Label/NumberAbbrLabel.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { formatMoney } from '../../utils/moneyHelper'; import { numberAbbr, NumberAbbrUnit } from '../../utils/numberHelper'; @@ -18,7 +18,7 @@ export interface NumberAbbrLabelFeatureProps { maxPrecision?: number; } -export const NumberAbbrLabel = UUI.FunctionComponent({ +export const NumberAbbrLabel = UUIFunctionComponent({ name: 'NumberAbbrLabel', nodes: { Root: 'abbr', @@ -34,4 +34,4 @@ export const NumberAbbrLabel = UUI.FunctionComponent({ ) }) -export type NumberAbbrLabelProps = Parameters[0] \ No newline at end of file +export type NumberAbbrLabelProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Label/TimeLabel.tsx b/src/components/Label/TimeLabel.tsx index d1f51eb..28604a4 100644 --- a/src/components/Label/TimeLabel.tsx +++ b/src/components/Label/TimeLabel.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { TimeFormatterLocale, TimeFormatterLocaleKinds, timeFormat } from '../../utils/timeFormatter'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface TimeLabelFeatureProps { /** @@ -22,7 +22,7 @@ export interface TimeLabelFeatureProps { kind: TimeFormatterLocaleKinds[T][number]; } -export const TimeLabel = UUI.FunctionComponent({ +export const TimeLabel = UUIFunctionComponent({ name: 'TimeLabel', nodes: { Root: 'div', @@ -36,4 +36,4 @@ export const TimeLabel = UUI.FunctionComponent({ ) }) -export type TimeLabelProps = Parameters[0] \ No newline at end of file +export type TimeLabelProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Layout/Layout.tsx b/src/components/Layout/Layout.tsx index e7a4b02..44eba45 100644 --- a/src/components/Layout/Layout.tsx +++ b/src/components/Layout/Layout.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { LayoutMainProps, LayoutMain } from './LayoutMain'; import { LayoutFooterProps, LayoutFooter } from './LayoutFooter'; import { LayoutHeaderProps, LayoutHeader } from './LayoutHeader'; @@ -22,7 +22,7 @@ export interface LayoutFeatureProps { )[]; } -export const _Layout = UUI.FunctionComponent({ +export const _Layout = UUIFunctionComponent({ name: 'Layout', nodes: { Root: 'section', @@ -49,7 +49,7 @@ export const _Layout = UUI.FunctionComponent({ ) }) -export type LayoutProps = Parameters[0] +export type LayoutProps = UUIFunctionComponentProps const Layout = createGroupedComponent(_Layout, { Nav: LayoutNav, diff --git a/src/components/Layout/LayoutAside.tsx b/src/components/Layout/LayoutAside.tsx index e7041ea..e38eac4 100644 --- a/src/components/Layout/LayoutAside.tsx +++ b/src/components/Layout/LayoutAside.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface LayoutAsideFeatureProps { /** @@ -8,7 +8,7 @@ export interface LayoutAsideFeatureProps { children?: React.ReactNode; } -export const LayoutAside = UUI.FunctionComponent({ +export const LayoutAside = UUIFunctionComponent({ name: 'LayoutAside', nodes: { Root: 'aside', @@ -23,4 +23,4 @@ export const LayoutAside = UUI.FunctionComponent({ ) }) -export type LayoutAsideProps = Parameters[0] \ No newline at end of file +export type LayoutAsideProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Layout/LayoutFooter.tsx b/src/components/Layout/LayoutFooter.tsx index 125c0aa..649de64 100644 --- a/src/components/Layout/LayoutFooter.tsx +++ b/src/components/Layout/LayoutFooter.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface LayoutFooterFeatureProps { /** @@ -8,7 +8,7 @@ export interface LayoutFooterFeatureProps { children?: React.ReactNode; } -export const LayoutFooter = UUI.FunctionComponent({ +export const LayoutFooter = UUIFunctionComponent({ name: 'LayoutFooter', nodes: { Root: 'footer', @@ -23,4 +23,4 @@ export const LayoutFooter = UUI.FunctionComponent({ ) }) -export type LayoutFooterProps = Parameters[0] \ No newline at end of file +export type LayoutFooterProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Layout/LayoutHeader.tsx b/src/components/Layout/LayoutHeader.tsx index 8f8cd09..71b1192 100644 --- a/src/components/Layout/LayoutHeader.tsx +++ b/src/components/Layout/LayoutHeader.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface LayoutHeaderFeatureProps { /** @@ -8,7 +8,7 @@ export interface LayoutHeaderFeatureProps { children?: React.ReactNode; } -export const LayoutHeader = UUI.FunctionComponent({ +export const LayoutHeader = UUIFunctionComponent({ name: 'LayoutHeader', nodes: { Root: 'header', @@ -22,4 +22,4 @@ export const LayoutHeader = UUI.FunctionComponent({ ) }) -export type LayoutHeaderProps = Parameters[0] \ No newline at end of file +export type LayoutHeaderProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Layout/LayoutMain.tsx b/src/components/Layout/LayoutMain.tsx index 988151c..7b25432 100644 --- a/src/components/Layout/LayoutMain.tsx +++ b/src/components/Layout/LayoutMain.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface LayoutMainFeatureProps { /** @@ -8,7 +8,7 @@ export interface LayoutMainFeatureProps { children?: React.ReactNode | string; } -export const LayoutMain = UUI.FunctionComponent({ +export const LayoutMain = UUIFunctionComponent({ name: 'LayoutMain', nodes: { Root: 'main', @@ -22,4 +22,4 @@ export const LayoutMain = UUI.FunctionComponent({ ) }) -export type LayoutMainProps = Parameters[0] \ No newline at end of file +export type LayoutMainProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Layout/LayoutNav.tsx b/src/components/Layout/LayoutNav.tsx index c4cd9df..b6dc0db 100644 --- a/src/components/Layout/LayoutNav.tsx +++ b/src/components/Layout/LayoutNav.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface LayoutNavFeatureProps { /** @@ -8,7 +8,7 @@ export interface LayoutNavFeatureProps { children?: React.ReactNode; } -export const LayoutNav = UUI.FunctionComponent({ +export const LayoutNav = UUIFunctionComponent({ name: 'LayoutNav', nodes: { Root: 'nav', @@ -23,4 +23,4 @@ export const LayoutNav = UUI.FunctionComponent({ ) }) -export type LayoutNavProps = Parameters[0] \ No newline at end of file +export type LayoutNavProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/ListBox/ListBox.tsx b/src/components/ListBox/ListBox.tsx index a5bdbb0..145604e 100644 --- a/src/components/ListBox/ListBox.tsx +++ b/src/components/ListBox/ListBox.tsx @@ -1,7 +1,7 @@ import React, { useState, useRef, useCallback } from 'react'; -import { UUI, UUIFunctionComponentProps } from '../../core/uui'; import classNames from 'classnames'; import { KeyCode } from '../../utils/keyboardHelper'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface ListBoxItem { disabled?: boolean; @@ -23,7 +23,7 @@ export interface ListBoxFeatureProps { onUnselect?: (id: string) => void; } -export const ListBox = UUI.FunctionComponent({ +export const ListBox = UUIFunctionComponent({ name: 'ListBox', nodes: { Root: 'div', diff --git a/src/components/Loading/LoadingCover.tsx b/src/components/Loading/LoadingCover.tsx index d706905..ee9b1ad 100644 --- a/src/components/Loading/LoadingCover.tsx +++ b/src/components/Loading/LoadingCover.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { LoadingSpinner } from './LoadingSpinner'; export interface LoadingCoverFeatureProps { @@ -9,7 +9,7 @@ export interface LoadingCoverFeatureProps { children: React.ReactNode; } -export const LoadingCover = UUI.FunctionComponent({ +export const LoadingCover = UUIFunctionComponent({ name: 'LoadingCover', nodes: { Root: 'div', @@ -35,4 +35,4 @@ export const LoadingCover = UUI.FunctionComponent({ ) }) -export type LoadingCoverProps = Parameters[0] +export type LoadingCoverProps = UUIFunctionComponentProps diff --git a/src/components/Loading/LoadingSpinner.tsx b/src/components/Loading/LoadingSpinner.tsx index 6d96aaf..c1db8c5 100644 --- a/src/components/Loading/LoadingSpinner.tsx +++ b/src/components/Loading/LoadingSpinner.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Icons } from '../../icons/Icons'; export type LoadingSpinnerFeatureProps = { @@ -9,7 +9,7 @@ export type LoadingSpinnerFeatureProps = { animate?: boolean; } -export const LoadingSpinner = UUI.FunctionComponent({ +export const LoadingSpinner = UUIFunctionComponent({ name: 'LoadingSpinner', nodes: { Root: 'div', @@ -33,4 +33,4 @@ export const LoadingSpinner = UUI.FunctionComponent({ ) }) -export type LoadingSpinnerProps = Parameters[0] +export type LoadingSpinnerProps = UUIFunctionComponentProps diff --git a/src/components/Page/Page.tsx b/src/components/Page/Page.tsx index fa3f762..336a968 100644 --- a/src/components/Page/Page.tsx +++ b/src/components/Page/Page.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { PageAnnotatedSection } from './PageAnnotatedSection'; import { PageSection } from './PageSection'; import { createGroupedComponent } from '../../utils/createGroupedComponent'; @@ -16,7 +16,7 @@ export interface PageFeatureProps { children?: React.ReactNode; } -export const _Page = UUI.FunctionComponent({ +export const _Page = UUIFunctionComponent({ name: 'Page', nodes: { Root: 'div', @@ -73,7 +73,7 @@ export const _Page = UUI.FunctionComponent({ ) }) -export type PageProps = Parameters[0] +export type PageProps = UUIFunctionComponentProps const Page = createGroupedComponent(_Page, { Section: PageSection, diff --git a/src/components/Page/PageAnnotatedSection.tsx b/src/components/Page/PageAnnotatedSection.tsx index 290dd47..2b0a996 100644 --- a/src/components/Page/PageAnnotatedSection.tsx +++ b/src/components/Page/PageAnnotatedSection.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface PageAnnotatedSectionFeatureProps { title: string; @@ -10,7 +10,7 @@ export interface PageAnnotatedSectionFeatureProps { children?: React.ReactNode; } -export const PageAnnotatedSection = UUI.FunctionComponent({ +export const PageAnnotatedSection = UUIFunctionComponent({ name: 'PageAnnotatedSection', nodes: { Root: 'div', @@ -34,4 +34,4 @@ export const PageAnnotatedSection = UUI.FunctionComponent({ ) }) -export type PageAnnotatedSectionProps = Parameters[0] \ No newline at end of file +export type PageAnnotatedSectionProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Page/PageSection.tsx b/src/components/Page/PageSection.tsx index 6e73c4b..7fde7bf 100644 --- a/src/components/Page/PageSection.tsx +++ b/src/components/Page/PageSection.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface PageSectionFeatureProps { /** @@ -8,7 +8,7 @@ export interface PageSectionFeatureProps { children?: React.ReactNode; } -export const PageSection = UUI.FunctionComponent({ +export const PageSection = UUIFunctionComponent({ name: 'PageSection', nodes: { Root: 'div', @@ -22,4 +22,4 @@ export const PageSection = UUI.FunctionComponent({ ) }) -export type PageSectionProps = Parameters[0] \ No newline at end of file +export type PageSectionProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Pagination/PageInfo.tsx b/src/components/Pagination/PageInfo.tsx index 05c8e57..9add3aa 100644 --- a/src/components/Pagination/PageInfo.tsx +++ b/src/components/Pagination/PageInfo.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { PaginationContext } from './PaginationContext'; export interface PageInfoFeatureProps { @@ -9,7 +9,7 @@ export interface PageInfoFeatureProps { onRender?: (startItem: number, endItem: number, totalItem: number) => React.ReactNode; } -export const PageInfo = UUI.FunctionComponent({ +export const PageInfo = UUIFunctionComponent({ name: 'PageInfo', nodes: { Root: 'div', @@ -35,4 +35,4 @@ export const PageInfo = UUI.FunctionComponent({ ) }) -export type PageInfoProps = Parameters[0] \ No newline at end of file +export type PageInfoProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Pagination/PageJumper.tsx b/src/components/Pagination/PageJumper.tsx index cc363e9..c8bdb8c 100644 --- a/src/components/Pagination/PageJumper.tsx +++ b/src/components/Pagination/PageJumper.tsx @@ -1,6 +1,6 @@ import React, { useContext, useState, useEffect } from 'react'; import { NumberField as UUINumberField } from '../Input'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { PaginationContext } from './PaginationContext'; export interface PageJumperFeatureProps { @@ -10,7 +10,7 @@ export interface PageJumperFeatureProps { labelText?: string; } -export const PageJumper = UUI.FunctionComponent({ +export const PageJumper = UUIFunctionComponent({ name: 'PageJumper', nodes: { Root: 'div', @@ -57,4 +57,4 @@ export const PageJumper = UUI.FunctionComponent({ ) }) -export type PageJumperProps = Parameters[0] \ No newline at end of file +export type PageJumperProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Pagination/PageList.tsx b/src/components/Pagination/PageList.tsx index 4177333..017655d 100644 --- a/src/components/Pagination/PageList.tsx +++ b/src/components/Pagination/PageList.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useContext } from 'react'; import { range } from 'lodash-es'; import { Button as UUIButton } from '../Button'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { PaginationContext } from './PaginationContext'; import classNames from 'classnames'; @@ -10,7 +10,7 @@ export interface PageListFeatureProps { } -export const PageList = UUI.FunctionComponent({ +export const PageList = UUIFunctionComponent({ name: 'PageList', nodes: { Root: 'div', @@ -53,7 +53,7 @@ export const PageList = UUI.FunctionComponent({ ) }) -export type PageListProps = Parameters[0] +export type PageListProps = UUIFunctionComponentProps const getEllipsisPageData = (currentPage: number, pageCount: number) => { const delta = (() => { diff --git a/src/components/Pagination/PageNextButton.tsx b/src/components/Pagination/PageNextButton.tsx index 22a8fac..f2f1da9 100644 --- a/src/components/Pagination/PageNextButton.tsx +++ b/src/components/Pagination/PageNextButton.tsx @@ -1,13 +1,13 @@ import React, { useContext } from 'react'; import { Button as UUIButton } from '../Button'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Icons } from '../../icons/Icons'; import { PaginationContext } from './PaginationContext'; export interface PageNextButtonFeatureProps { } -export const PageNextButton = UUI.FunctionComponent({ +export const PageNextButton = UUIFunctionComponent({ name: 'PageNextButton', nodes: { Root: UUIButton, @@ -33,4 +33,4 @@ export const PageNextButton = UUI.FunctionComponent({ ) }) -export type PageNextButtonProps = Parameters[0] +export type PageNextButtonProps = UUIFunctionComponentProps diff --git a/src/components/Pagination/PagePrevButton.tsx b/src/components/Pagination/PagePrevButton.tsx index 6af8b10..61a367c 100644 --- a/src/components/Pagination/PagePrevButton.tsx +++ b/src/components/Pagination/PagePrevButton.tsx @@ -1,13 +1,13 @@ import React, { useContext } from 'react'; import { Button as UUIButton } from '../Button'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Icons } from '../../icons/Icons'; import { PaginationContext } from './PaginationContext'; export interface PagePrevButtonFeatureProps { } -export const PagePrevButton = UUI.FunctionComponent({ +export const PagePrevButton = UUIFunctionComponent({ name: 'PagePrevButton', nodes: { Root: UUIButton, @@ -33,4 +33,4 @@ export const PagePrevButton = UUI.FunctionComponent({ ) }) -export type PagePrevButtonProps = Parameters[0] +export type PagePrevButtonProps = UUIFunctionComponentProps diff --git a/src/components/Pagination/PageSelector.tsx b/src/components/Pagination/PageSelector.tsx index 2a06d50..0b088c5 100644 --- a/src/components/Pagination/PageSelector.tsx +++ b/src/components/Pagination/PageSelector.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { PaginationContext } from './PaginationContext'; import { HTMLSelect } from '../Select'; import { range } from 'lodash-es'; @@ -8,7 +8,7 @@ export interface PageSelectorFeatureProps { labelRender?: (currentPage: number, totalPage: number) => string; } -export const PageSelector = UUI.FunctionComponent({ +export const PageSelector = UUIFunctionComponent({ name: 'PageSelector', nodes: { Root: 'div', @@ -43,4 +43,4 @@ export const PageSelector = UUI.FunctionComponent({ ) }) -export type PageSelectorProps = Parameters[0] \ No newline at end of file +export type PageSelectorProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Pagination/PageSize.tsx b/src/components/Pagination/PageSize.tsx index d53f990..d1498e4 100644 --- a/src/components/Pagination/PageSize.tsx +++ b/src/components/Pagination/PageSize.tsx @@ -1,5 +1,5 @@ import React, { useContext } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { PaginationContext } from './PaginationContext'; import { HTMLSelect } from '../Select'; @@ -8,7 +8,7 @@ export interface PageSizeFeatureProps { labelRender?: (pageSize: number) => string; } -export const PageSize = UUI.FunctionComponent({ +export const PageSize = UUIFunctionComponent({ name: 'PageSize', nodes: { Root: 'div', @@ -41,4 +41,4 @@ export const PageSize = UUI.FunctionComponent({ ) }) -export type PageSizeProps = Parameters[0] \ No newline at end of file +export type PageSizeProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index ca6fdbd..f463160 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { usePagination, IPagination } from '../../hooks/usePagination'; import { PageSize } from './PageSize'; import { PageInfo } from './PageInfo'; @@ -23,7 +23,7 @@ export interface PaginationFeatureProps { children: React.ReactNode; } -export const _Pagination = UUI.FunctionComponent({ +export const _Pagination = UUIFunctionComponent({ name: 'Pagination', nodes: { Root: 'div' @@ -47,7 +47,7 @@ export const _Pagination = UUI.FunctionComponent({ ) }) -export type PaginationProps = Parameters[0] +export type PaginationProps = UUIFunctionComponentProps const Pagination = createGroupedComponent(_Pagination, { PageSize, diff --git a/src/components/Popover/Popover.tsx b/src/components/Popover/Popover.tsx index c96ba10..780c308 100644 --- a/src/components/Popover/Popover.tsx +++ b/src/components/Popover/Popover.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { usePopper } from 'react-popper'; import { useClickAway } from 'react-use'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import ReactHelper from '../../utils/ReactHelper'; export type PopoverPlacement = Exclude @@ -66,7 +66,7 @@ export interface PopoverFeatureProps { popperElement?: Element; } -export const Popover = UUI.FunctionComponent({ +export const Popover = UUIFunctionComponent({ name: 'Popover', nodes: { Root: 'div', @@ -173,4 +173,4 @@ export const Popover = UUI.FunctionComponent({ ) }) -export type PopoverProps = Parameters[0] +export type PopoverProps = UUIFunctionComponentProps diff --git a/src/components/ProgressBar/ProgressBar.tsx b/src/components/ProgressBar/ProgressBar.tsx index 71c4a67..e2d8bb6 100644 --- a/src/components/ProgressBar/ProgressBar.tsx +++ b/src/components/ProgressBar/ProgressBar.tsx @@ -1,7 +1,7 @@ import classNames from "classnames"; import React, { useMemo } from "react"; -import { UUI } from "../../core/uui"; import { clamp } from "lodash-es"; +import { UUIFunctionComponent, UUIFunctionComponentProps } from "../../core"; export interface ProgressBarFeatureProps { /** @@ -25,7 +25,7 @@ export interface ProgressBarFeatureProps { indeterminate?: boolean; } -export const ProgressBar = UUI.FunctionComponent( +export const ProgressBar = UUIFunctionComponent( { name: "ProgressBar", nodes: { @@ -117,6 +117,6 @@ export const ProgressBar = UUI.FunctionComponent( } ); -export type ProgressBarProps = Parameters[0]; +export type ProgressBarProps = UUIFunctionComponentProps; const toPercentage = (n: number) => `${(n * 100).toFixed(4)}%`; diff --git a/src/components/Radio/Radio.tsx b/src/components/Radio/Radio.tsx index d9728aa..f57a0d7 100644 --- a/src/components/Radio/Radio.tsx +++ b/src/components/Radio/Radio.tsx @@ -1,8 +1,8 @@ import React, { useContext, useMemo } from 'react'; import { omit } from 'lodash-es'; import classNames from 'classnames'; -import { UUI, UUIComponentProps } from '../../core/uui'; import { RadioGroupContext } from './RadioGroupContext'; +import { UUIFunctionComponent, UUIComponentProps, UUIFunctionComponentProps } from '../../core'; type InputHTMLAttributes = Pick< React.InputHTMLAttributes, @@ -33,7 +33,7 @@ const RadioNodes = { Label: 'span', } as const -const BaseRadio = UUI.FunctionComponent({ +const BaseRadio = UUIFunctionComponent({ name: "Radio", nodes: RadioNodes, }, (props: RadioFeatureProps, nodes) => { @@ -84,4 +84,4 @@ export function Radio(props: UUIComponentProps } Radio.displayName = ` [GenericComponent] Radio` -export type RadioProps = Parameters[0] +export type RadioProps = UUIFunctionComponentProps diff --git a/src/components/Radio/RadioGroup.tsx b/src/components/Radio/RadioGroup.tsx index 177abb1..76ecf45 100644 --- a/src/components/Radio/RadioGroup.tsx +++ b/src/components/Radio/RadioGroup.tsx @@ -1,9 +1,9 @@ import React, { useMemo, useState } from 'react'; import { RadioFeatureProps, Radio } from './Radio'; -import { UUI, UUIComponentProps } from '../../core/uui'; import { KeyCode } from '../../utils/keyboardHelper'; import { getValidTypeChildren } from '../../utils/componentHelper'; import { RadioGroupContext } from './RadioGroupContext'; +import { UUIFunctionComponent, UUIComponentProps, UUIFunctionComponentProps } from '../../core'; export interface RadioGroupFeatureProps { /** @@ -31,7 +31,7 @@ const RadioGroupNodes = { Root: 'div' } as const -const BaseRadioGroup = UUI.FunctionComponent({ +const BaseRadioGroup = UUIFunctionComponent({ name: "RadioGroup", nodes: RadioGroupNodes, }, (props: RadioGroupFeatureProps, nodes) => { @@ -97,4 +97,4 @@ export function RadioGroup(props: UUIComponentProps } RadioGroup.displayName = ` [GenericComponent] RadioGroup` -export type RadioGroupProps = Parameters[0] +export type RadioGroupProps = UUIFunctionComponentProps diff --git a/src/components/SegmentControl/SegmentControl.tsx b/src/components/SegmentControl/SegmentControl.tsx index 2ab1131..93f13fd 100644 --- a/src/components/SegmentControl/SegmentControl.tsx +++ b/src/components/SegmentControl/SegmentControl.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { UUI, UUIComponentProps } from '../../core/uui'; import classNames from 'classnames'; import { KeyCode } from '../../utils/keyboardHelper'; +import { UUIFunctionComponent, UUIComponentProps, UUIFunctionComponentProps } from '../../core'; export interface SegmentControlOption { label: React.ReactNode; @@ -32,7 +32,7 @@ const SegmentControlNodes = { Thumb: 'div', } as const -const BaseSegmentControl = UUI.FunctionComponent({ +const BaseSegmentControl = UUIFunctionComponent({ name: 'SegmentControl', nodes: SegmentControlNodes }, (props: SegmentControlFeatureProps, nodes) => { @@ -108,4 +108,4 @@ export function SegmentControl(props: UUIComponentPro return } SegmentControl.displayName = ` [GenericComponent] SegmentControl` -export type SegmentControlProps = Parameters[0] +export type SegmentControlProps = UUIFunctionComponentProps diff --git a/src/components/Select/HTMLSelect.tsx b/src/components/Select/HTMLSelect.tsx index ae2d9e4..6747909 100644 --- a/src/components/Select/HTMLSelect.tsx +++ b/src/components/Select/HTMLSelect.tsx @@ -1,8 +1,8 @@ import React from 'react'; -import { UUI, UUIComponentProps } from '../../core/uui'; import { isString } from 'lodash-es'; import { LoadingSpinner } from '../Loading/LoadingSpinner'; import classNames from 'classnames'; +import { UUIFunctionComponent, UUIComponentProps } from '../../core'; export interface HTMLSelectOption { label: string; @@ -47,7 +47,7 @@ const HTMLSelectNodes = { LoadingSpinner: LoadingSpinner, } as const -const BaseHTMLSelect = UUI.FunctionComponent({ +const BaseHTMLSelect = UUIFunctionComponent({ name: "HTMLSelect", nodes: HTMLSelectNodes, }, (props: HTMLSelectFeatureProps, nodes) => { diff --git a/src/components/Select/Select.tsx b/src/components/Select/Select.tsx index 646422a..863f47a 100644 --- a/src/components/Select/Select.tsx +++ b/src/components/Select/Select.tsx @@ -1,5 +1,4 @@ import React, { useState, useRef, useMemo, useCallback } from 'react'; -import { UUI, UUIComponentProps } from '../../core/uui'; import { Popover as UUIPopover, PopoverPlacement } from '../Popover'; import { Tag as UUITag } from '../Tag'; import { TextField as UUITextField } from '../Input'; @@ -9,6 +8,7 @@ import { Icons } from '../../icons/Icons'; import { LoadingSpinner } from '../Loading/LoadingSpinner'; import { KeyCode } from '../../utils/keyboardHelper'; import { ListBox as UUIListBox, ListBoxItem } from '../ListBox'; +import { UUIFunctionComponent, UUIComponentProps, UUIFunctionComponentProps } from '../../core'; export interface SelectOption { key: string; @@ -127,7 +127,7 @@ const SelectNodes = { } as const -export const BaseSelect = UUI.FunctionComponent({ +export const BaseSelect = UUIFunctionComponent({ name: 'Select', nodes: SelectNodes, }, (props: SelectFeatureProps, nodes) => { @@ -433,4 +433,4 @@ export function Select return <_BaseSelect {...props} /> } Select.displayName = ` [GenericComponent] Radio` -export type SelectProps = Parameters[0] +export type SelectProps = UUIFunctionComponentProps diff --git a/src/components/Skeleton/Paragraph.tsx b/src/components/Skeleton/Paragraph.tsx index 11a8a85..f8f1bfb 100644 --- a/src/components/Skeleton/Paragraph.tsx +++ b/src/components/Skeleton/Paragraph.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface SkeletonParagraphFeatureProps { lines?: number; } -export const SkeletonParagraph = UUI.FunctionComponent({ +export const SkeletonParagraph = UUIFunctionComponent({ name: 'SkeletonParagraph', nodes: { Root: 'div', @@ -25,4 +25,4 @@ export const SkeletonParagraph = UUI.FunctionComponent({ ) }) -export type SkeletonParagraphProps = Parameters[0] \ No newline at end of file +export type SkeletonParagraphProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Skeleton/Picture.tsx b/src/components/Skeleton/Picture.tsx index f6e3315..9bd92f3 100644 --- a/src/components/Skeleton/Picture.tsx +++ b/src/components/Skeleton/Picture.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface SkeletonPictureFeatureProps { } -export const SkeletonPicture = UUI.FunctionComponent({ +export const SkeletonPicture = UUIFunctionComponent({ name: 'SkeletonPicture', nodes: { Root: 'div' @@ -16,4 +16,4 @@ export const SkeletonPicture = UUI.FunctionComponent({ ) }) -export type SkeletonPictureProps = Parameters[0] \ No newline at end of file +export type SkeletonPictureProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Skeleton/Skeleton.tsx b/src/components/Skeleton/Skeleton.tsx index 610635b..3c690de 100644 --- a/src/components/Skeleton/Skeleton.tsx +++ b/src/components/Skeleton/Skeleton.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { SkeletonParagraph } from './Paragraph'; import { SkeletonTitle } from './Title'; import { SkeletonPicture } from './Picture'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent } from '../../core'; import { createGroupedComponent } from '../../utils/createGroupedComponent'; @@ -10,7 +10,7 @@ export interface SkeletonFeatureProps { children?: React.ReactNode | string; } -export const _Skeleton = UUI.FunctionComponent({ +export const _Skeleton = UUIFunctionComponent({ name: 'Skeleton', nodes: { Root: 'div' diff --git a/src/components/Skeleton/Title.tsx b/src/components/Skeleton/Title.tsx index 89b7fc9..e211855 100644 --- a/src/components/Skeleton/Title.tsx +++ b/src/components/Skeleton/Title.tsx @@ -1,10 +1,10 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface SkeletonTitleFeatureProps { } -export const SkeletonTitle = UUI.FunctionComponent({ +export const SkeletonTitle = UUIFunctionComponent({ name: 'SkeletonTitle', nodes: { Root: 'h3' @@ -16,4 +16,4 @@ export const SkeletonTitle = UUI.FunctionComponent({ ) }) -export type SkeletonTitleProps = Parameters[0] \ No newline at end of file +export type SkeletonTitleProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Slider/Slider.tsx b/src/components/Slider/Slider.tsx index 57cfb0b..3907df6 100644 --- a/src/components/Slider/Slider.tsx +++ b/src/components/Slider/Slider.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useRef, useCallback, useState, useEffect } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { useEvent } from 'react-use'; import { clamp, clone, inRange, isArray } from 'lodash-es'; import classNames from 'classnames'; @@ -49,7 +49,7 @@ export interface SliderFeatureProps { vertical?: boolean; } -export const Slider = UUI.FunctionComponent({ +export const Slider = UUIFunctionComponent({ name: 'Slider', nodes: { Root: 'div', @@ -347,6 +347,6 @@ export const Slider = UUI.FunctionComponent({ ) }) -export type SliderProps = Parameters[0] +export type SliderProps = UUIFunctionComponentProps const toPercentage = (n: number) => `${(n * 100).toFixed(4)}%` diff --git a/src/components/Stepper/Stepper.tsx b/src/components/Stepper/Stepper.tsx index 592c0b4..301a2c5 100644 --- a/src/components/Stepper/Stepper.tsx +++ b/src/components/Stepper/Stepper.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Button } from '../../components/Button'; import { NumberField } from '../Input'; import { limitRange } from '../../utils/numberHelper'; @@ -53,7 +53,7 @@ export interface StepperFeatureProps { placeholder?: string; } -export const Stepper = UUI.FunctionComponent({ +export const Stepper = UUIFunctionComponent({ name: 'Stepper', nodes: { Root: 'div', @@ -186,4 +186,4 @@ export const Stepper = UUI.FunctionComponent({ ) }) -export type StepperProps = Parameters[0] +export type StepperProps = UUIFunctionComponentProps diff --git a/src/components/Switch/Switch.tsx b/src/components/Switch/Switch.tsx index 95e4434..557d240 100644 --- a/src/components/Switch/Switch.tsx +++ b/src/components/Switch/Switch.tsx @@ -1,5 +1,5 @@ import React, { useRef } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { Button as UUIButton } from '../Button/Button'; import classNames from 'classnames'; import { LoadingSpinner } from '../Loading/LoadingSpinner'; @@ -28,7 +28,7 @@ export interface SwitchFeatureProps { onChange: (flag: boolean) => void; } -export const Switch = UUI.FunctionComponent({ +export const Switch = UUIFunctionComponent({ name: 'Switch', nodes: { Root: 'div', @@ -71,4 +71,4 @@ export const Switch = UUI.FunctionComponent({ ) }) -export type SwitchProps = Parameters[0] +export type SwitchProps = UUIFunctionComponentProps diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index 18529e8..1497492 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import React, { useCallback, useMemo } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { useArrayCacheRender } from '../../hooks/useCacheRender'; import { Checkbox as UUICheckbox } from '../Checkbox'; import { LoadingCover } from '../Loading'; @@ -78,7 +78,7 @@ export const TableNodes = { EmptyView: 'div', } as const -export const Table = UUI.FunctionComponent({ +export const Table = UUIFunctionComponent({ name: 'Table', nodes: TableNodes, }, (props: TableFeatureProps, nodes) => { @@ -258,4 +258,4 @@ export const Table = UUI.FunctionComponent({ ) }) -export type TableProps = Parameters[0] \ No newline at end of file +export type TableProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Tabs/Tab.tsx b/src/components/Tabs/Tab.tsx index 543b044..908a152 100644 --- a/src/components/Tabs/Tab.tsx +++ b/src/components/Tabs/Tab.tsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useRef } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { TabsContext } from './TabsContext'; import classNames from 'classnames'; @@ -18,7 +18,7 @@ export interface TabFeatureProps { children: React.ReactNode; } -export const Tab = UUI.FunctionComponent({ +export const Tab = UUIFunctionComponent({ name: 'Tab', nodes: { Root: 'div', @@ -61,4 +61,4 @@ export const Tab = UUI.FunctionComponent({ ) }) -export type TabProps = Parameters[0] +export type TabProps = UUIFunctionComponentProps diff --git a/src/components/Tabs/Tabs.tsx b/src/components/Tabs/Tabs.tsx index d695492..1621099 100644 --- a/src/components/Tabs/Tabs.tsx +++ b/src/components/Tabs/Tabs.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import React, { useMemo, useState } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; import { getValidTypeChildren } from '../../utils/componentHelper'; import { Tab } from './Tab'; import { TabsContext } from './TabsContext'; @@ -38,7 +38,7 @@ export interface TabsFeatureProps { toggleTabWhenFocusChange?: boolean; } -export const Tabs = UUI.FunctionComponent({ +export const Tabs = UUIFunctionComponent({ name: 'Tabs', nodes: { Root: 'div', @@ -145,4 +145,4 @@ export const Tabs = UUI.FunctionComponent({ ) }) -export type TabsProps = Parameters[0] +export type TabsProps = UUIFunctionComponentProps diff --git a/src/components/Tag/Tag.tsx b/src/components/Tag/Tag.tsx index 54b3902..feba628 100644 --- a/src/components/Tag/Tag.tsx +++ b/src/components/Tag/Tag.tsx @@ -1,6 +1,6 @@ import React from 'react'; import classNames from 'classnames'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface TagFeatureProps { /** @@ -18,7 +18,7 @@ export const TagNodes = { Content: 'span', } as const -export const Tag = UUI.FunctionComponent({ +export const Tag = UUIFunctionComponent({ name: 'Tag', nodes: TagNodes, }, (props: TagFeatureProps, nodes) => { @@ -37,4 +37,4 @@ export const Tag = UUI.FunctionComponent({ ) }) -export type TagProps = Parameters[0] \ No newline at end of file +export type TagProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Toast/Toast.tsx b/src/components/Toast/Toast.tsx index 63778b2..55f5b7b 100644 --- a/src/components/Toast/Toast.tsx +++ b/src/components/Toast/Toast.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef, useCallback } from 'react'; -import { UUI } from '../../core/uui'; +import { UUIFunctionComponent, UUIFunctionComponentProps } from '../../core'; export interface ToastFeatureProps { /** Message to display in the body of the toast. */ @@ -19,7 +19,7 @@ export interface ToastFeatureProps { } export type IToast = ToastFeatureProps & { id: string } -export const Toast = UUI.FunctionComponent({ +export const Toast = UUIFunctionComponent({ prefix: 'UUI', name: 'Toast', nodes: { @@ -63,4 +63,4 @@ export const Toast = UUI.FunctionComponent({ ) }) -export type ToastProps = Parameters[0] \ No newline at end of file +export type ToastProps = UUIFunctionComponentProps \ No newline at end of file diff --git a/src/components/Toast/Toaster.tsx b/src/components/Toast/Toaster.tsx index 1826227..b9fad73 100644 --- a/src/components/Toast/Toaster.tsx +++ b/src/components/Toast/Toaster.tsx @@ -2,10 +2,9 @@ import { IToast, Toast, ToastProps } from './Toast'; import React from 'react'; import ReactDOM from 'react-dom'; import { v4 as uuidv4 } from 'uuid'; - import classNames from 'classnames'; -import { UUI } from '../../core/uui'; import ReactHelper from '../../utils/ReactHelper'; +import { UUIClassComponent, UUIClassComponentProps } from '../../core'; export enum ToasterPosition { Top = "top", @@ -40,7 +39,7 @@ export interface ToasterState { const ToasterPortalClassName = "UUI-Toaster-Portal" -export class Toaster extends UUI.ClassComponent({ +export class Toaster extends UUIClassComponent({ prefix: 'UUI', name: 'Toaster', nodes: { @@ -128,3 +127,4 @@ export class Toaster extends UUI.ClassComponent({ ) } } +export type ToasterProps = UUIClassComponentProps \ No newline at end of file diff --git a/src/core/UUIComponent.tsx b/src/core/UUIComponent.tsx new file mode 100644 index 0000000..c943fa2 --- /dev/null +++ b/src/core/UUIComponent.tsx @@ -0,0 +1,180 @@ +import { IntrinsicNodeT, FunctionComponentNodeT, ClassComponentNodeT, IntrinsicNode, ComponentNode, UUIComponentNodes } from "./modules/UUICustomizeNode"; +import React, { useMemo } from "react"; +import { clone, isString, pickBy, mapKeys, isEmpty, mapValues } from "lodash-es"; +import classNames from "classnames"; +import { UUIComponentCustomizeProps, UUIConvenienceProps, UUIMetaProps } from "./modules/UUIComponentProps"; + +/** + * UUI Advanced Component for Function Component + * @param options setup options + * @param WrappedComponent wrapped function component + */ +export function UUIFunctionComponent< + /** + * Generic type P for target component props + */ + P, + /** + * Generic type N for component node name + */ + N extends string, + /** + * Generic type T for options.nodes value + */ + T extends keyof IntrinsicNodeT | FunctionComponentNodeT | ClassComponentNodeT, + /** + * Generic type X for WrappedComponent generated nodes + */ + X extends { [key in N]: T }, + /** + * Generic type Z for component props.customize + */ + Z extends UUIComponentCustomizeProps +>( + options: { + prefix?: string; + name: string; + separator?: string; + nodes: X; + }, + WrappedComponent: (props: P, nodes: UUIComponentNodes) => React.ReactElement, +) { + const component: React.FunctionComponent

= (props) => { + const { prefix, separator } = props; + // eslint-disable-next-line react-hooks/rules-of-hooks + const { finalOptions, nodes } = useMemo(() => { + const finalOptions = getFinalOptions(options, { prefix, separator }) + const nodes = compileNodes(finalOptions) + return { finalOptions, nodes } + }, [prefix, separator]) + const compiledProps = compileProps(props, finalOptions, undefined) + injectCustomizeProps(nodes, compiledProps); + return WrappedComponent(compiledProps, nodes) + } + component.displayName = ` [Component] ${options.name}` + return component +} + +/** + * UUI Advanced Component for Class Component + * @param options setup options + */ +export function UUIClassComponent< + /** + * Generic type N for component node name + */ + N extends string, + /** + * Generic type T for options.nodes value + */ + T extends keyof IntrinsicNodeT | FunctionComponentNodeT | ClassComponentNodeT, + /** + * Generic type X for WrappedComponent generated nodes + */ + X extends { [key in N]: T }, + /** + * Generic type Z for component props.customize + */ + Z extends UUIComponentCustomizeProps +>( + options: { + prefix?: string; + name: string; + separator?: string; + nodes: X; + }, +) { + return class WrappedComponent

extends React.Component

}, SS> { + static displayName = ` [Component] ${options.name}` + state: S & { nodes: UUIComponentNodes } + + componentDidUpdate(prevProps: P & UUIConvenienceProps & UUIMetaProps & Z) { + if ( + prevProps.prefix !== this.props.prefix || + prevProps.separator !== this.props.separator + ) { + const finalOptions = getFinalOptions(options, this.props) + this.setState({ nodes: compileNodes(finalOptions) }) + const compiledProps = compileProps(this.props, finalOptions, (this.props as any).innerRef || undefined) + injectCustomizeProps(this.state.nodes, compiledProps) + } + } + + constructor(props: P & UUIConvenienceProps & Z) { + super(props) + const finalOptions = getFinalOptions(options, props) + this.state = { nodes: compileNodes(finalOptions) } as any + this.state.nodes = compileNodes(finalOptions) + const compiledProps = compileProps(props, finalOptions, (props as any).innerRef || undefined) + injectCustomizeProps(this.state.nodes, compiledProps) + } + } +} + +function getFinalOptions(options: any, props: any) { + return { + nodes: options.nodes, + name: options.name, + prefix: props.prefix || options.prefix || 'UUI', + separator: props.separator || options.separator || '-', + } +} + +function compileProps(props: any, options: any, ref: any): any { + const compiledProps = clone(props) + if (!compiledProps.customize) { + compiledProps.customize = {} + } + + // Generally, UUI component should contain a Root node. + if ( + (options.nodes as any)['Root'] && + isString((options.nodes as any)['Root']) + ) { + const rootCustomizeProps: any = (compiledProps.customize as any)['Root'] || {}; + /** + * Convenience props: className, style + * className will be injected into customize.Root { extendClassName: ... } + * style will be injected into customize.Root { extendStyle: ... } + * id will be injected into customize.Root { id: ... } + */ + if (compiledProps.className) rootCustomizeProps.extendClassName = classNames(compiledProps.className, rootCustomizeProps.extendClassName); + if (compiledProps.style) rootCustomizeProps.extendStyle = Object.assign(compiledProps.style, rootCustomizeProps.extendStyle); + if (compiledProps.id) rootCustomizeProps.id = compiledProps.id; + + let dataAttributes = pickBy(compiledProps, (v, k) => k.startsWith('data-')) + dataAttributes = mapKeys(dataAttributes, (v, k) => k.replace('data-', '')) + if (!isEmpty(dataAttributes)) { + rootCustomizeProps.dataAttributes = Object.assign(dataAttributes, rootCustomizeProps.dataAttributes); + } + + + let ariaAttributes = pickBy(compiledProps, (v, k) => k.startsWith('aria-')) + ariaAttributes = mapKeys(ariaAttributes, (v, k) => k.replace('aria-', '')) + if (!isEmpty(ariaAttributes)) { + rootCustomizeProps.ariaAttributes = Object.assign(ariaAttributes, rootCustomizeProps.ariaAttributes); + } + + (compiledProps.customize as any)['Root'] = rootCustomizeProps; + } + compiledProps.ref = ref + + return compiledProps +} + +function compileNodes(options: any): any { + return mapValues(options.nodes, (nodeElement, nodeName) => { + if (isString(nodeElement)) { + return IntrinsicNode(nodeElement as any, nodeName, options) + } else { + return ComponentNode(nodeElement as any, nodeName, options) + } + }) +} + +function injectCustomizeProps(nodes: any, props: any) { + for (const nodeName of Object.keys(nodes)) { + const customizeProps = props.customize && (props.customize as any)[nodeName] + nodes[nodeName]['CustomizeProps'] = { customize: customizeProps } + } +} diff --git a/src/core/index.ts b/src/core/index.ts new file mode 100644 index 0000000..464d099 --- /dev/null +++ b/src/core/index.ts @@ -0,0 +1,3 @@ +export { UUIFunctionComponent, UUIClassComponent } from './UUIComponent'; + +export type { UUIComponentProps, UUIFunctionComponentProps, UUIClassComponentProps } from './modules/UUIComponentProps'; \ No newline at end of file diff --git a/src/core/modules/UUIComponentProps.ts b/src/core/modules/UUIComponentProps.ts new file mode 100644 index 0000000..1d853eb --- /dev/null +++ b/src/core/modules/UUIComponentProps.ts @@ -0,0 +1,55 @@ +import { IntrinsicNodeT, FunctionComponentNodeT, ClassComponentNodeT, NodeCustomizeProps } from "./UUICustomizeNode" + +export type UUIConvenienceProps = { + /** + * Convenience id props, + * this props will be applied to id of component Root node. + */ + id?: string; + /** + * Convenience className props, + * this props will be applied to append to extendClassName of component Root node customize props. + * @default none + */ + className?: string; + /** + * Convenience style props, + * this props will be applied to merge to extendStyle of component Root node customize props. + * @default none + */ + style?: React.CSSProperties; + + /** + * React natively support data-* attributes type, + * dont need to redeclare again convenience data attributes. + */ +} +export type UUIMetaProps = { + prefix?: string; + separator?: string; +} +export type UUIComponentProps = P & UUIConvenienceProps & UUIComponentCustomizeProps +export type UUIFunctionComponentProps any> = Parameters[0] +export type UUIClassComponentProps> = React.ComponentProps + +export type UUIComponentCustomizeProps< + X extends { [key in string]?: keyof IntrinsicNodeT | FunctionComponentNodeT | ClassComponentNodeT }, +> = { + /** + * Customize component nodes + * @default none + */ + customize?: { + [key in keyof X]?: X[key] extends keyof IntrinsicNodeT + ? NodeCustomizeProps & Partial + : ( + X[key] extends FunctionComponentNodeT + ? NonNullable[0]['customize']> + : ( + X[key] extends ClassComponentNodeT + ? React.ComponentProps['customize'] + : never + ) + ) + }; +} \ No newline at end of file diff --git a/src/core/types/UUICustomizeAriaAttributes.ts b/src/core/modules/UUICustomizeAriaAttributes.ts similarity index 100% rename from src/core/types/UUICustomizeAriaAttributes.ts rename to src/core/modules/UUICustomizeAriaAttributes.ts diff --git a/src/core/modules/UUICustomizeNode.tsx b/src/core/modules/UUICustomizeNode.tsx new file mode 100644 index 0000000..b9ba9ed --- /dev/null +++ b/src/core/modules/UUICustomizeNode.tsx @@ -0,0 +1,194 @@ +import React from "react"; +import { mergeRefs } from "../utils/mergeRefs"; +import { mapKeys, uniq, omit, merge, isEmpty, pickBy } from "lodash-es"; +import { UUIConvenienceProps } from "./UUIComponentProps"; +import { mergeCustomize } from "../utils/mergeCustomize"; +import classNames from "classnames"; +import { TypeWithArgs } from "../utils/typeHelper"; +import { UUICustomizeAriaAttributes } from "./UUICustomizeAriaAttributes"; + +export interface NodeCustomizeClassNameProps { + overrideClassName?: string; + extendClassName?: string; +} +export interface NodeCustomizeStyleProps { + overrideStyle?: React.CSSProperties; + extendStyle?: React.CSSProperties; +} +export interface NodeCustomizeChildrenProps { + children?: React.ReactNode; + overrideChildren?: React.ReactNode; + extendChildrenBefore?: React.ReactNode; + extendChildrenAfter?: React.ReactNode; +} +export interface NodeCustomizeDataAttributesProps { + dataAttributes?: { + [key: string]: any; + }; +} +export interface NodeCustomizeAriaAttributesProps { + ariaAttributes?: UUICustomizeAriaAttributes; +} +export type NodeCustomizeProps = + & NodeCustomizeClassNameProps + & NodeCustomizeStyleProps + & NodeCustomizeChildrenProps + & NodeCustomizeDataAttributesProps + & NodeCustomizeAriaAttributesProps + & React.RefAttributes + +export type NodeCustomizeOptions = { + name: string; + prefix: string; + separator: string; +} + +export type IntrinsicNodeCustomizeProps = NodeCustomizeProps +export type IntrinsicNodeT = JSX.IntrinsicElements +export type IntrinsicNode = (tagName: T, nodeName: N, options: NodeCustomizeOptions) => (props: JSX.IntrinsicElements[T]) => JSX.Element +export function IntrinsicNode(tagName: T, nodeName: N, options: NodeCustomizeOptions) { + const nodeClassName = [options.prefix, options.name, nodeName].join(options.separator) + + const Node = React.forwardRef((innerProps: JSX.IntrinsicElements[T], _ref) => { + const { customize } = (Node as any)['CustomizeProps'] as Readonly<{ customize?: IntrinsicNodeCustomizeProps }> + const id = (customize as any | undefined)?.id || innerProps.id + const className = (() => { + if (customize?.overrideClassName) return customize.overrideClassName + const innerClassName = innerProps.className + const finalClassName = classNames(nodeClassName, innerClassName, customize?.extendClassName) + return finalClassName + })() + const style = (() => { + if (customize?.overrideStyle) return customize.overrideStyle + const innerStyle = innerProps.style + const finalStyle = merge(innerStyle, customize?.extendStyle) + return isEmpty(finalStyle) ? undefined : finalStyle + })() + const dataAttributes = (() => { + if (!customize?.dataAttributes) return {} + /** + * @reference https://www.w3.org/TR/2008/REC-xml-20081126/#NT-Name + * TODO: // fix regex for supporting unicode + */ + const validDataAttributesCharactersRegex = /^([A-Za-z0-9-])*$/ + let result = customize.dataAttributes + result = pickBy(result, (v, k) => validDataAttributesCharactersRegex.test(k)) + result = mapKeys(result, (v, k) => `data-${k}`) + return result + })() + const children = (() => { + /** + *