@ippon-ui/ui 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/component-library-create-from-pattern-library.agent.md +37 -0
- package/.agents/agents/patten-library-create-component.agent.md +30 -0
- package/.agents/skills/component-library/SKILL.md +169 -0
- package/.agents/skills/pattern-library/SKILL.md +277 -0
- package/.github/workflows/build.yml +34 -0
- package/.gitlab-ci.yml +12 -0
- package/.prettierignore +6 -0
- package/AGENTS.md +50 -0
- package/LICENSE +202 -0
- package/ci/build.yml +15 -0
- package/ci/common.yml +42 -0
- package/ci/deploy.yml +20 -0
- package/icons/LICENCE +202 -0
- package/icons/index.ts +69 -0
- package/icons/package.json +25 -0
- package/icons/tsconfig.json +11 -0
- package/lefthook.yml +10 -0
- package/mise.toml +55 -0
- package/package.json +26 -0
- package/pnpm-workspace.yaml +9 -0
- package/prettier.config.mts +8 -0
- package/react/LICENCE +202 -0
- package/react/README.md +75 -0
- package/react/eslint.config.js +22 -0
- package/react/package.json +63 -0
- package/react/src/CAP.ts +14 -0
- package/react/src/Card.ts +2 -0
- package/react/src/DataSelectable.ts +7 -0
- package/react/src/Grid.ts +33 -0
- package/react/src/IpponBadge.tsx +62 -0
- package/react/src/IpponButton.tsx +93 -0
- package/react/src/IpponButtonCard.tsx +34 -0
- package/react/src/IpponCard.tsx +30 -0
- package/react/src/IpponContainer.tsx +15 -0
- package/react/src/IpponGrid.tsx +56 -0
- package/react/src/IpponHSpace.tsx +56 -0
- package/react/src/IpponIcon.tsx +15 -0
- package/react/src/IpponImportFile.tsx +128 -0
- package/react/src/IpponIon.tsx +45 -0
- package/react/src/IpponMeter.tsx +43 -0
- package/react/src/IpponProgress.tsx +45 -0
- package/react/src/IpponText.tsx +56 -0
- package/react/src/IpponTitle.tsx +45 -0
- package/react/src/IpponVSpace.tsx +43 -0
- package/react/src/Optional.ts +177 -0
- package/react/src/Tokens.ts +36 -0
- package/react/src/index.ts +16 -0
- package/react/test/File.fixture.ts +13 -0
- package/react/test/IpponBadge.spec.tsx +245 -0
- package/react/test/IpponButton.spec.tsx +666 -0
- package/react/test/IpponButtonCard.spec.tsx +162 -0
- package/react/test/IpponCard.spec.tsx +133 -0
- package/react/test/IpponContainer.spec.tsx +56 -0
- package/react/test/IpponGrid.spec.tsx +140 -0
- package/react/test/IpponHSpace.spec.tsx +107 -0
- package/react/test/IpponIcon.spec.tsx +37 -0
- package/react/test/IpponImportFile.spec.tsx +431 -0
- package/react/test/IpponIon.spec.tsx +52 -0
- package/react/test/IpponMeter.spec.tsx +59 -0
- package/react/test/IpponProgress.spec.tsx +68 -0
- package/react/test/IpponText.spec.tsx +149 -0
- package/react/test/IpponTitle.spec.tsx +242 -0
- package/react/test/IpponVSpace.spec.tsx +91 -0
- package/react/tsconfig.app.json +24 -0
- package/react/tsconfig.json +4 -0
- package/react/tsconfig.node.json +23 -0
- package/react/vite.config.ts +30 -0
- package/react/vitest.config.ts +21 -0
- package/styles/.editorconfig +12 -0
- package/styles/.stylelintrc.json +75 -0
- package/styles/LICENCE +202 -0
- package/styles/README.md +107 -0
- package/styles/logo.svg +26 -0
- package/styles/package.json +67 -0
- package/styles/src/atom/_atom.scss +9 -0
- package/styles/src/atom/atom.pug +34 -0
- package/styles/src/atom/badge/_badge.scss +108 -0
- package/styles/src/atom/badge/badge.code.pug +29 -0
- package/styles/src/atom/badge/badge.md +1 -0
- package/styles/src/atom/badge/badge.mixin.pug +24 -0
- package/styles/src/atom/badge/badge.render.pug +7 -0
- package/styles/src/atom/button/_button.scss +242 -0
- package/styles/src/atom/button/button.code.pug +38 -0
- package/styles/src/atom/button/button.md +31 -0
- package/styles/src/atom/button/button.mixin.pug +30 -0
- package/styles/src/atom/button/button.render.pug +13 -0
- package/styles/src/atom/icon/_icon.scss +8 -0
- package/styles/src/atom/icon/icon.code.pug +5 -0
- package/styles/src/atom/icon/icon.md +11 -0
- package/styles/src/atom/icon/icon.mixin.pug +8 -0
- package/styles/src/atom/icon/icon.render.pug +7 -0
- package/styles/src/atom/ion/ion.code.pug +8 -0
- package/styles/src/atom/ion/ion.md +11 -0
- package/styles/src/atom/ion/ion.mixin.pug +8 -0
- package/styles/src/atom/ion/ion.render.pug +7 -0
- package/styles/src/atom/meter/_meter.scss +23 -0
- package/styles/src/atom/meter/meter.code.pug +8 -0
- package/styles/src/atom/meter/meter.md +7 -0
- package/styles/src/atom/meter/meter.mixin.pug +12 -0
- package/styles/src/atom/meter/meter.render.pug +5 -0
- package/styles/src/atom/progress/_progress.scss +23 -0
- package/styles/src/atom/progress/progress.code.pug +8 -0
- package/styles/src/atom/progress/progress.md +7 -0
- package/styles/src/atom/progress/progress.mixin.pug +14 -0
- package/styles/src/atom/progress/progress.render.pug +5 -0
- package/styles/src/atom/tab/_tab.scss +48 -0
- package/styles/src/atom/tab/tab.code.pug +5 -0
- package/styles/src/atom/tab/tab.md +1 -0
- package/styles/src/atom/tab/tab.mixin.pug +14 -0
- package/styles/src/atom/tab/tab.render.pug +4 -0
- package/styles/src/atom/text/_text.scss +74 -0
- package/styles/src/atom/text/text.code.pug +19 -0
- package/styles/src/atom/text/text.md +5 -0
- package/styles/src/atom/text/text.mixin.pug +12 -0
- package/styles/src/atom/text/text.render.pug +7 -0
- package/styles/src/atom/title/_title.scss +68 -0
- package/styles/src/atom/title/title.code.pug +25 -0
- package/styles/src/atom/title/title.md +9 -0
- package/styles/src/atom/title/title.mixin.pug +12 -0
- package/styles/src/atom/title/title.render.pug +5 -0
- package/styles/src/atom/title-display/_title-display.scss +26 -0
- package/styles/src/atom/title-display/title-display.code.pug +9 -0
- package/styles/src/atom/title-display/title-display.md +5 -0
- package/styles/src/atom/title-display/title-display.mixin.pug +6 -0
- package/styles/src/atom/title-display/title-display.render.pug +4 -0
- package/styles/src/doc.scss +2 -0
- package/styles/src/favicon.ico +0 -0
- package/styles/src/function/_conversion.scss +9 -0
- package/styles/src/index.pug +59 -0
- package/styles/src/layout-documentation.pug +14 -0
- package/styles/src/layout.pug +17 -0
- package/styles/src/molecule/_molecule.scss +2 -0
- package/styles/src/molecule/import-file/_import-file.scss +38 -0
- package/styles/src/molecule/import-file/import-file.code.pug +4 -0
- package/styles/src/molecule/import-file/import-file.md +1 -0
- package/styles/src/molecule/import-file/import-file.mixin.pug +15 -0
- package/styles/src/molecule/import-file/import-file.render.pug +5 -0
- package/styles/src/molecule/molecule.pug +20 -0
- package/styles/src/molecule/tabs/_tabs.scss +4 -0
- package/styles/src/molecule/tabs/tabs.code.pug +9 -0
- package/styles/src/molecule/tabs/tabs.md +1 -0
- package/styles/src/molecule/tabs/tabs.mixin.pug +4 -0
- package/styles/src/molecule/tabs/tabs.render.pug +4 -0
- package/styles/src/molecule/toggle/_toggle.scss +68 -0
- package/styles/src/molecule/toggle/toggle.code.pug +26 -0
- package/styles/src/molecule/toggle/toggle.md +1 -0
- package/styles/src/molecule/toggle/toggle.mixin.pug +36 -0
- package/styles/src/molecule/toggle/toggle.render.pug +5 -0
- package/styles/src/organism/_abstract-card.scss +36 -0
- package/styles/src/organism/_docorganism.scss +1 -0
- package/styles/src/organism/_organism.scss +8 -0
- package/styles/src/organism/button-card/_button-card.scss +22 -0
- package/styles/src/organism/button-card/button-card.code.pug +31 -0
- package/styles/src/organism/button-card/button-card.md +28 -0
- package/styles/src/organism/button-card/button-card.mixin.pug +8 -0
- package/styles/src/organism/button-card/button-card.render.pug +7 -0
- package/styles/src/organism/card/_card.scss +6 -0
- package/styles/src/organism/card/card.code.pug +9 -0
- package/styles/src/organism/card/card.md +23 -0
- package/styles/src/organism/card/card.mixin.pug +7 -0
- package/styles/src/organism/card/card.render.pug +7 -0
- package/styles/src/organism/container/_container.scss +3 -0
- package/styles/src/organism/container/container.code.pug +13 -0
- package/styles/src/organism/container/container.md +5 -0
- package/styles/src/organism/container/container.mixin.pug +3 -0
- package/styles/src/organism/container/container.render.pug +4 -0
- package/styles/src/organism/grid/_docgrid.scss +11 -0
- package/styles/src/organism/grid/_grid.scss +84 -0
- package/styles/src/organism/grid/grid.code.pug +25 -0
- package/styles/src/organism/grid/grid.md +1 -0
- package/styles/src/organism/grid/grid.mixin.pug +7 -0
- package/styles/src/organism/grid/grid.render.pug +5 -0
- package/styles/src/organism/h-space/_h-space.scss +49 -0
- package/styles/src/organism/h-space/h-space.code.pug +56 -0
- package/styles/src/organism/h-space/h-space.md +22 -0
- package/styles/src/organism/h-space/h-space.mixin.pug +14 -0
- package/styles/src/organism/h-space/h-space.render.pug +5 -0
- package/styles/src/organism/header/_header.scss +8 -0
- package/styles/src/organism/header/header.code.pug +14 -0
- package/styles/src/organism/header/header.md +1 -0
- package/styles/src/organism/header/header.mixin.pug +7 -0
- package/styles/src/organism/header/header.render.pug +4 -0
- package/styles/src/organism/modal/_modal.scss +58 -0
- package/styles/src/organism/modal/modal.code.pug +68 -0
- package/styles/src/organism/modal/modal.md +1 -0
- package/styles/src/organism/modal/modal.mixin.pug +25 -0
- package/styles/src/organism/modal/modal.render.pug +4 -0
- package/styles/src/organism/organism.pug +30 -0
- package/styles/src/organism/v-space/_v-space.scss +45 -0
- package/styles/src/organism/v-space/v-space.code.pug +41 -0
- package/styles/src/organism/v-space/v-space.md +20 -0
- package/styles/src/organism/v-space/v-space.mixin.pug +7 -0
- package/styles/src/organism/v-space/v-space.render.pug +5 -0
- package/styles/src/quark/_breakpoint.scss +12 -0
- package/styles/src/quark/_font.scss +38 -0
- package/styles/src/quark/_gap.scss +34 -0
- package/styles/src/quark/_placeholder.scss +27 -0
- package/styles/src/quark/_shadow.scss +13 -0
- package/styles/src/quark/_typography.scss +146 -0
- package/styles/src/template/_template.scss +1 -0
- package/styles/src/template/layout/_layout.scss +20 -0
- package/styles/src/template/layout/layout.code.pug +11 -0
- package/styles/src/template/layout/layout.md +1 -0
- package/styles/src/template/layout/layout.mixin.pug +11 -0
- package/styles/src/template/layout/layout.render.pug +4 -0
- package/styles/src/template/template.pug +16 -0
- package/styles/src/tikui.scss +5 -0
- package/styles/src/token/_doctable.scss +14 -0
- package/styles/src/token/_doctoken.scss +1 -0
- package/styles/src/token/_size.scss +9 -0
- package/styles/src/token/_token.scss +5 -0
- package/styles/src/token/color/_color.scss +9 -0
- package/styles/src/token/color/color/_base.scss +65 -0
- package/styles/src/token/color/color/_brand.scss +13 -0
- package/styles/src/token/color/color/_error.scss +13 -0
- package/styles/src/token/color/color/_information-2.scss +13 -0
- package/styles/src/token/color/color/_information.scss +13 -0
- package/styles/src/token/color/color/_neutral.scss +20 -0
- package/styles/src/token/color/color/_semantic.scss +69 -0
- package/styles/src/token/color/color/_success.scss +13 -0
- package/styles/src/token/color/color/_warning.scss +13 -0
- package/styles/src/token/color/color.js +31 -0
- package/styles/src/token/color/color.mixin.pug +19 -0
- package/styles/src/token/color/color.pug +9 -0
- package/styles/src/token/color/color.render.pug +13 -0
- package/styles/src/token/radius/_radius.scss +8 -0
- package/styles/src/token/radius/radius.js +54 -0
- package/styles/src/token/radius/radius.mixin.pug +14 -0
- package/styles/src/token/radius/radius.pug +9 -0
- package/styles/src/token/radius/radius.render.pug +11 -0
- package/styles/src/token/shadow/_shadow.scss +22 -0
- package/styles/src/token/shadow/shadow.js +45 -0
- package/styles/src/token/shadow/shadow.mixin.pug +13 -0
- package/styles/src/token/shadow/shadow.pug +9 -0
- package/styles/src/token/shadow/shadow.render.pug +9 -0
- package/styles/src/token/token.js +38 -0
- package/styles/src/token/token.pug +25 -0
- package/styles/src/token/typography/_typography.scss +103 -0
- package/styles/src/token/typography/typography.js +32 -0
- package/styles/src/token/typography/typography.mixin.pug +17 -0
- package/styles/src/token/typography/typography.pug +9 -0
- package/styles/src/token/typography/typography.render.pug +19 -0
- package/styles/test/function/conversion.test.scss +20 -0
- package/styles/test/function/sass.spec.ts +6 -0
- package/styles/tikuiconfig.json +14 -0
- package/styles/tsconfig.json +10 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import {
|
|
4
|
+
optionalToPrefixedAlternativeClass,
|
|
5
|
+
optionalToAlternativeClass,
|
|
6
|
+
toAlternativeClass,
|
|
7
|
+
} from './CAP.ts';
|
|
8
|
+
import type { IpponCardShadowLevel, IpponCardSize } from './Card.ts';
|
|
9
|
+
|
|
10
|
+
type IpponCardProps = DataSelectableWithChildren<{
|
|
11
|
+
shadow?: IpponCardShadowLevel;
|
|
12
|
+
border?: boolean;
|
|
13
|
+
size?: IpponCardSize;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
export const IpponCard = (props: IpponCardProps) => (
|
|
17
|
+
<div
|
|
18
|
+
className={clsx(
|
|
19
|
+
'ippon-card',
|
|
20
|
+
optionalToPrefixedAlternativeClass('shadow')(props.shadow),
|
|
21
|
+
{
|
|
22
|
+
[toAlternativeClass('border')]: props.border,
|
|
23
|
+
},
|
|
24
|
+
optionalToAlternativeClass(props.size),
|
|
25
|
+
)}
|
|
26
|
+
data-selector={props.dataSelector}
|
|
27
|
+
>
|
|
28
|
+
{props.children}
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
2
|
+
import type { JSX } from 'react';
|
|
3
|
+
|
|
4
|
+
type IpponContainerProps = DataSelectableWithChildren<{
|
|
5
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
6
|
+
}>;
|
|
7
|
+
|
|
8
|
+
export const IpponContainer = (props: IpponContainerProps) => {
|
|
9
|
+
const CustomTag: keyof JSX.IntrinsicElements = props.tag || 'div';
|
|
10
|
+
return (
|
|
11
|
+
<CustomTag className="ippon-container" data-selector={props.dataSelector}>
|
|
12
|
+
{props.children}
|
|
13
|
+
</CustomTag>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
3
|
+
import type { IpponGridCol, IpponGridContainer, IpponGridGap, IpponGridMedia } from './Grid.ts';
|
|
4
|
+
import { clsx } from 'clsx';
|
|
5
|
+
import { toAlternativeClass } from './CAP.ts';
|
|
6
|
+
import { Optional } from './Optional.ts';
|
|
7
|
+
|
|
8
|
+
const toResponsiveClasses = (
|
|
9
|
+
prefix: string,
|
|
10
|
+
value: string | number | (string | number)[],
|
|
11
|
+
): string =>
|
|
12
|
+
clsx(
|
|
13
|
+
Array.isArray(value)
|
|
14
|
+
? value.map((v) => toAlternativeClass(`${prefix}-${v}`))
|
|
15
|
+
: toAlternativeClass(`${prefix}-${value}`),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const optionalToResponsiveClasses =
|
|
19
|
+
(prefix: string) =>
|
|
20
|
+
(value?: string | number | (string | number)[]): string | undefined =>
|
|
21
|
+
Optional.ofFalsifiable(value)
|
|
22
|
+
.map((v) => toResponsiveClasses(prefix, v))
|
|
23
|
+
.orUndefined();
|
|
24
|
+
|
|
25
|
+
type IpponGridProps = DataSelectableWithChildren<{
|
|
26
|
+
media?: IpponGridMedia;
|
|
27
|
+
container?: IpponGridContainer;
|
|
28
|
+
gap?: IpponGridGap;
|
|
29
|
+
}>;
|
|
30
|
+
|
|
31
|
+
export const IpponGrid = (props: IpponGridProps): ReactNode => (
|
|
32
|
+
<div
|
|
33
|
+
className={clsx(
|
|
34
|
+
'ippon-grid',
|
|
35
|
+
optionalToResponsiveClasses('media')(props.media),
|
|
36
|
+
optionalToResponsiveClasses('container')(props.container),
|
|
37
|
+
optionalToResponsiveClasses('gap')(props.gap),
|
|
38
|
+
)}
|
|
39
|
+
data-selector={props.dataSelector}
|
|
40
|
+
>
|
|
41
|
+
{props.children}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
type IpponGridSlotProps = DataSelectableWithChildren<{
|
|
46
|
+
col?: IpponGridCol;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
export const IpponGridSlot = (props: IpponGridSlotProps): ReactNode => (
|
|
50
|
+
<div
|
|
51
|
+
className={clsx('ippon-grid--slot', optionalToResponsiveClasses('col')(props.col))}
|
|
52
|
+
data-selector={props.dataSelector}
|
|
53
|
+
>
|
|
54
|
+
{props.children}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ReactNode, JSX } from 'react';
|
|
2
|
+
import type { IpponTokenSize } from './Tokens.ts';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
5
|
+
import { Optional } from './Optional.ts';
|
|
6
|
+
import { optionalToPrefixedAlternativeClass, toAlternativeClass } from './CAP.ts';
|
|
7
|
+
|
|
8
|
+
type Align = 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom' | 'space-between';
|
|
9
|
+
|
|
10
|
+
type IpponHSpaceProps = DataSelectableWithChildren<{
|
|
11
|
+
gap?: IpponTokenSize;
|
|
12
|
+
align?: Align | Align[];
|
|
13
|
+
className?: string;
|
|
14
|
+
wrap?: boolean;
|
|
15
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
const alignSetToClass = (alignSet: Set<Align>): string =>
|
|
19
|
+
clsx([...alignSet].map(toAlternativeClass));
|
|
20
|
+
|
|
21
|
+
const alignToClass = (align: Align | Align[]): string => {
|
|
22
|
+
if (typeof align === 'string') {
|
|
23
|
+
return toAlternativeClass(align);
|
|
24
|
+
}
|
|
25
|
+
return alignSetToClass(new Set<Align>(align));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const IpponHSpace = (props: IpponHSpaceProps): ReactNode => {
|
|
29
|
+
const CustomTag: keyof JSX.IntrinsicElements = props.tag || 'div';
|
|
30
|
+
const className = clsx(
|
|
31
|
+
'ippon-h-space',
|
|
32
|
+
optionalToPrefixedAlternativeClass('gap')(props.gap),
|
|
33
|
+
Optional.ofFalsifiable(props.align).map(alignToClass).orUndefined(),
|
|
34
|
+
props.className,
|
|
35
|
+
{ '-wrap': props.wrap },
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<CustomTag className={className} data-selector={props.dataSelector}>
|
|
40
|
+
{props.children}
|
|
41
|
+
</CustomTag>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type IpponHSpaceSlotProps = DataSelectableWithChildren<{
|
|
46
|
+
expand?: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
|
|
49
|
+
export const IpponHSpaceSlot = (props: IpponHSpaceSlotProps): ReactNode => (
|
|
50
|
+
<div
|
|
51
|
+
className={clsx('ippon-h-space--slot', { '-expand': props.expand })}
|
|
52
|
+
data-selector={props.dataSelector}
|
|
53
|
+
>
|
|
54
|
+
{props.children}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { IpponIon, type IpponIonProps } from './IpponIon.tsx';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import type { IpponTokenTextColor, IpponTokenSize } from './Tokens.ts';
|
|
4
|
+
|
|
5
|
+
export type IpponIconProps = IpponIonProps & {
|
|
6
|
+
color?: IpponTokenTextColor;
|
|
7
|
+
size?: IpponTokenSize;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const IpponIcon = (props: IpponIconProps) => {
|
|
11
|
+
const colorClass = props.color ? `-color-${props.color}` : null;
|
|
12
|
+
const sizeClass = props.size ? `-size-${props.size}` : null;
|
|
13
|
+
const className = clsx('ippon-icon', colorClass, sizeClass, props.className);
|
|
14
|
+
return <IpponIon {...props} className={className} />;
|
|
15
|
+
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import type { ChangeEvent, DragEvent } from 'react';
|
|
3
|
+
import { useRef, useState } from 'react';
|
|
4
|
+
import { Optional } from './Optional.ts';
|
|
5
|
+
import type { DataSelectable } from './DataSelectable.ts';
|
|
6
|
+
import { IpponIcon } from './IpponIcon.tsx';
|
|
7
|
+
import { IpponText } from './IpponText.tsx';
|
|
8
|
+
import { IpponVSpace } from './IpponVSpace.tsx';
|
|
9
|
+
import { toAlternativeClass } from './CAP.ts';
|
|
10
|
+
|
|
11
|
+
type IpponImportFileCommonProps = {
|
|
12
|
+
accept?: string;
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type IpponImportFileSingleProps = IpponImportFileCommonProps & {
|
|
18
|
+
multiple?: false;
|
|
19
|
+
onChange?: (file: File) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type IpponImportFileMultipleProps = IpponImportFileCommonProps & {
|
|
23
|
+
multiple: true;
|
|
24
|
+
onChange?: (files: File[]) => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type IpponImportFileVanillaProps = IpponImportFileSingleProps | IpponImportFileMultipleProps;
|
|
28
|
+
|
|
29
|
+
type IpponImportFileProps = DataSelectable<IpponImportFileVanillaProps>;
|
|
30
|
+
|
|
31
|
+
const getFileForOnChange = (files: FileList): File => files[0];
|
|
32
|
+
|
|
33
|
+
const getFilesForOnChange = (files: FileList): File[] => Array.from(files);
|
|
34
|
+
|
|
35
|
+
const importOnChange = (props: IpponImportFileProps) => (files: FileList) => {
|
|
36
|
+
if (!props.onChange) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (props.multiple) {
|
|
40
|
+
props.onChange(getFilesForOnChange(files));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
props.onChange(getFileForOnChange(files));
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const IpponImportFile = (props: IpponImportFileProps) => {
|
|
47
|
+
const [isDragover, setIsDragover] = useState(false);
|
|
48
|
+
const dragCounterRef = useRef(0);
|
|
49
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
50
|
+
|
|
51
|
+
const resetFile = () => {
|
|
52
|
+
if (inputRef.current) {
|
|
53
|
+
inputRef.current.value = '';
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const importFiles = (files: FileList) => {
|
|
58
|
+
importOnChange(props)(files);
|
|
59
|
+
resetFile();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
|
63
|
+
Optional.ofNullable(event.target.files)
|
|
64
|
+
.filter((files) => files.length > 0)
|
|
65
|
+
.ifPresent(importFiles);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const handleDragEnter = () => {
|
|
69
|
+
dragCounterRef.current += 1;
|
|
70
|
+
setIsDragover(true);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handleDragLeave = () => {
|
|
74
|
+
dragCounterRef.current -= 1;
|
|
75
|
+
if (dragCounterRef.current === 0) {
|
|
76
|
+
setIsDragover(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const handleDragOver = (event: DragEvent<HTMLLabelElement>) => {
|
|
81
|
+
event.preventDefault();
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const handleDrop = (event: DragEvent<HTMLLabelElement>) => {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
dragCounterRef.current = 0;
|
|
87
|
+
setIsDragover(false);
|
|
88
|
+
Optional.ofFalsifiable(event.dataTransfer?.files)
|
|
89
|
+
.filter((files) => files.length > 0)
|
|
90
|
+
.ifPresent(importFiles);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<label
|
|
95
|
+
className={clsx('ippon-import-file', {
|
|
96
|
+
[toAlternativeClass('dragover')]: isDragover,
|
|
97
|
+
})}
|
|
98
|
+
data-selector={props.dataSelector}
|
|
99
|
+
onDragEnter={handleDragEnter}
|
|
100
|
+
onDragLeave={handleDragLeave}
|
|
101
|
+
onDragOver={handleDragOver}
|
|
102
|
+
onDrop={handleDrop}
|
|
103
|
+
>
|
|
104
|
+
<IpponVSpace gap={8} align="center">
|
|
105
|
+
<div className="ippon-import-file--icon">
|
|
106
|
+
<IpponIcon name="cloud-upload" size={24} />
|
|
107
|
+
</div>
|
|
108
|
+
<IpponVSpace gap={4} align="center">
|
|
109
|
+
<IpponText variant="body" weight="bold">
|
|
110
|
+
{props.title}
|
|
111
|
+
</IpponText>
|
|
112
|
+
<IpponText variant="body" size="small" color="neutral-tertiary-inversed">
|
|
113
|
+
{props.description}
|
|
114
|
+
</IpponText>
|
|
115
|
+
</IpponVSpace>
|
|
116
|
+
</IpponVSpace>
|
|
117
|
+
<input
|
|
118
|
+
ref={inputRef}
|
|
119
|
+
type="file"
|
|
120
|
+
className="ippon-import-file--input"
|
|
121
|
+
multiple={props.multiple}
|
|
122
|
+
accept={props.accept}
|
|
123
|
+
onChange={handleChange}
|
|
124
|
+
data-selector={props.dataSelector ? `${props.dataSelector}.input` : undefined}
|
|
125
|
+
/>
|
|
126
|
+
</label>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { IconClassic, IconLogo, IconVariant } from '@ippon-ui/icons';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import type { DataSelectable } from './DataSelectable.ts';
|
|
4
|
+
|
|
5
|
+
type IpponIconClassic = {
|
|
6
|
+
name: IconClassic;
|
|
7
|
+
variant?: IconVariant;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type IpponIconLogo = {
|
|
11
|
+
name: IconLogo;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type IpponIconBase = {
|
|
15
|
+
className?: string;
|
|
16
|
+
onClick?: () => void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type IpponIonProps = DataSelectable<(IpponIconClassic | IpponIconLogo) & IpponIconBase>;
|
|
20
|
+
|
|
21
|
+
type IpponIconInfo = {
|
|
22
|
+
name: string;
|
|
23
|
+
variant?: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const toClassName = (props: IpponIconInfo): string => {
|
|
27
|
+
const { name, variant } = props;
|
|
28
|
+
const prefix = 'ippon-ion';
|
|
29
|
+
const variantSuffix = variant ? `-${variant}` : '';
|
|
30
|
+
return `${prefix}-${name}${variantSuffix}`;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const IpponIon = (props: IpponIonProps) => {
|
|
34
|
+
const CustomTag = props.onClick ? 'button' : 'span';
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
/* NOSONAR */ <CustomTag
|
|
38
|
+
role={props.onClick ? undefined : 'presentation'}
|
|
39
|
+
type={props.onClick ? 'button' : undefined}
|
|
40
|
+
className={clsx(toClassName(props), props.className)}
|
|
41
|
+
data-selector={props.dataSelector}
|
|
42
|
+
onClick={props.onClick}
|
|
43
|
+
></CustomTag>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import type { DataSelectable } from './DataSelectable.ts';
|
|
3
|
+
|
|
4
|
+
declare module 'react' {
|
|
5
|
+
interface CSSProperties {
|
|
6
|
+
'--ippon-meter-percentage'?: string;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type IpponVanillaMeterProps = {
|
|
11
|
+
value: number;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type IpponMeterProps = DataSelectable<IpponVanillaMeterProps>;
|
|
18
|
+
|
|
19
|
+
const toPercentage = (props: IpponVanillaMeterProps) => {
|
|
20
|
+
const range = props.max - props.min;
|
|
21
|
+
if (range === 0) {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
return ((props.value - props.min) / range) * 100;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const IpponMeter = (props: IpponMeterProps) => {
|
|
28
|
+
const percentage = toPercentage(props);
|
|
29
|
+
const clampedPercentage = Math.min(Math.max(percentage, 0), 100);
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
/* NOSONAR */ <div
|
|
33
|
+
className={clsx('ippon-meter')}
|
|
34
|
+
role="meter"
|
|
35
|
+
aria-valuenow={props.value}
|
|
36
|
+
aria-valuemin={props.min}
|
|
37
|
+
aria-valuemax={props.max}
|
|
38
|
+
aria-label={props.label}
|
|
39
|
+
data-selector={props.dataSelector}
|
|
40
|
+
style={{ '--ippon-meter-percentage': `${clampedPercentage}%` }}
|
|
41
|
+
></div>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import type { DataSelectable } from './DataSelectable.ts';
|
|
3
|
+
|
|
4
|
+
declare module 'react' {
|
|
5
|
+
interface CSSProperties {
|
|
6
|
+
'--ippon-progress-percentage'?: string;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type IpponVanillaProgressProps = {
|
|
11
|
+
value: number;
|
|
12
|
+
min: number;
|
|
13
|
+
max: number;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type IpponProgressProps = DataSelectable<IpponVanillaProgressProps>;
|
|
18
|
+
|
|
19
|
+
const toPercentage = (props: IpponVanillaProgressProps) => {
|
|
20
|
+
const range = props.max - props.min;
|
|
21
|
+
if (range === 0) {
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
return ((props.value - props.min) / range) * 100;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const IpponProgress = (props: IpponProgressProps) => {
|
|
28
|
+
const percentage = toPercentage(props);
|
|
29
|
+
const clampedPercentage = Math.min(Math.max(percentage, 0), 100);
|
|
30
|
+
const busy = props.value < props.max;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
/* NOSONAR */ <div
|
|
34
|
+
className={clsx('ippon-progress')}
|
|
35
|
+
role="progressbar"
|
|
36
|
+
aria-valuenow={props.value}
|
|
37
|
+
aria-valuemin={props.min}
|
|
38
|
+
aria-valuemax={props.max}
|
|
39
|
+
aria-label={props.label}
|
|
40
|
+
aria-busy={busy}
|
|
41
|
+
data-selector={props.dataSelector}
|
|
42
|
+
style={{ '--ippon-progress-percentage': `${clampedPercentage}%` }}
|
|
43
|
+
></div>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { IpponTokenTextColor } from './Tokens.ts';
|
|
2
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { optionalToAlternativeClass, optionalToPrefixedAlternativeClass } from './CAP.ts';
|
|
5
|
+
import type { JSX } from 'react';
|
|
6
|
+
|
|
7
|
+
type IpponTextBodyProps = {
|
|
8
|
+
variant: 'body';
|
|
9
|
+
weight?: 'bold';
|
|
10
|
+
size?: 'small' | 'large';
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type IpponTextLabelProps = {
|
|
14
|
+
variant: 'label';
|
|
15
|
+
size?: 'small' | 'large';
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type IpponVariantProps = IpponTextBodyProps | IpponTextLabelProps;
|
|
19
|
+
|
|
20
|
+
type IpponVanillaTextProps = {
|
|
21
|
+
color?: IpponTokenTextColor;
|
|
22
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
23
|
+
placeholder?: boolean;
|
|
24
|
+
} & IpponVariantProps;
|
|
25
|
+
|
|
26
|
+
type IpponTextProps = DataSelectableWithChildren<IpponVanillaTextProps>;
|
|
27
|
+
|
|
28
|
+
const toBodyVariantClasses = (props: IpponTextBodyProps) =>
|
|
29
|
+
clsx('-body', optionalToAlternativeClass(props.weight), optionalToAlternativeClass(props.size));
|
|
30
|
+
|
|
31
|
+
const toLabelVariantClasses = (props: IpponTextLabelProps) =>
|
|
32
|
+
clsx('-label', optionalToAlternativeClass(props.size));
|
|
33
|
+
|
|
34
|
+
const toVariantClasses = (props: IpponVariantProps) => {
|
|
35
|
+
if (props.variant === 'body') {
|
|
36
|
+
return toBodyVariantClasses(props);
|
|
37
|
+
}
|
|
38
|
+
return toLabelVariantClasses(props);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const IpponText = (props: IpponTextProps) => {
|
|
42
|
+
const CustomTag: keyof JSX.IntrinsicElements = props.tag || 'span';
|
|
43
|
+
return (
|
|
44
|
+
<CustomTag
|
|
45
|
+
className={clsx(
|
|
46
|
+
'ippon-text',
|
|
47
|
+
toVariantClasses(props),
|
|
48
|
+
optionalToPrefixedAlternativeClass('color')(props.color),
|
|
49
|
+
props.placeholder && '-placeholder',
|
|
50
|
+
)}
|
|
51
|
+
data-selector={props.dataSelector}
|
|
52
|
+
>
|
|
53
|
+
{props.children}
|
|
54
|
+
</CustomTag>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import type { JSX } from 'react';
|
|
4
|
+
import { Optional } from './Optional.ts';
|
|
5
|
+
|
|
6
|
+
type Level = 1 | 2 | 3 | 4 | 5;
|
|
7
|
+
|
|
8
|
+
type HeadingTag = `h${Level}`;
|
|
9
|
+
|
|
10
|
+
type IpponTitleWithHeadingProps = DataSelectableWithChildren<{
|
|
11
|
+
tag: HeadingTag;
|
|
12
|
+
level?: Level;
|
|
13
|
+
placeholder?: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
type IpponTitleWithNonHeadingProps = DataSelectableWithChildren<{
|
|
17
|
+
tag?: Exclude<keyof JSX.IntrinsicElements, HeadingTag>;
|
|
18
|
+
level: Level;
|
|
19
|
+
placeholder?: boolean;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
type IpponTitleProps = IpponTitleWithHeadingProps | IpponTitleWithNonHeadingProps;
|
|
23
|
+
|
|
24
|
+
const levelToClass = (level?: Level): string | undefined =>
|
|
25
|
+
Optional.ofFalsifiable(level)
|
|
26
|
+
.map((l) => `-l${l}`)
|
|
27
|
+
.orUndefined();
|
|
28
|
+
|
|
29
|
+
export const IpponTitle = (props: IpponTitleProps) => {
|
|
30
|
+
const CustomTag: keyof JSX.IntrinsicElements = (props.tag ||
|
|
31
|
+
'div') as keyof JSX.IntrinsicElements;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<CustomTag
|
|
35
|
+
className={clsx(
|
|
36
|
+
'ippon-title',
|
|
37
|
+
levelToClass(props.level),
|
|
38
|
+
props.placeholder && '-placeholder',
|
|
39
|
+
)}
|
|
40
|
+
data-selector={props.dataSelector}
|
|
41
|
+
>
|
|
42
|
+
{props.children}
|
|
43
|
+
</CustomTag>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { JSX, ReactNode } from 'react';
|
|
2
|
+
import type { IpponTokenSize } from './Tokens.ts';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import type { DataSelectableWithChildren } from './DataSelectable.ts';
|
|
5
|
+
import { optionalToAlternativeClass, optionalToPrefixedAlternativeClass } from './CAP.ts';
|
|
6
|
+
|
|
7
|
+
type Align = 'left' | 'center' | 'justify' | 'right';
|
|
8
|
+
|
|
9
|
+
type IpponVSpaceProps = DataSelectableWithChildren<{
|
|
10
|
+
gap?: IpponTokenSize;
|
|
11
|
+
align?: Align;
|
|
12
|
+
className?: string;
|
|
13
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
export const IpponVSpace = (props: IpponVSpaceProps): ReactNode => {
|
|
17
|
+
const CustomTag: keyof JSX.IntrinsicElements = props.tag || 'div';
|
|
18
|
+
const className = clsx(
|
|
19
|
+
'ippon-v-space',
|
|
20
|
+
optionalToPrefixedAlternativeClass('gap')(props.gap),
|
|
21
|
+
optionalToAlternativeClass(props.align),
|
|
22
|
+
props.className,
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<CustomTag className={className} data-selector={props.dataSelector}>
|
|
27
|
+
{props.children}
|
|
28
|
+
</CustomTag>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type IpponVSpaceSlotProps = DataSelectableWithChildren<{
|
|
33
|
+
align?: Align;
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export const IpponVSpaceSlot = (props: IpponVSpaceSlotProps): ReactNode => (
|
|
37
|
+
<div
|
|
38
|
+
className={clsx('ippon-v-space--slot', optionalToAlternativeClass(props.align))}
|
|
39
|
+
data-selector={props.dataSelector}
|
|
40
|
+
>
|
|
41
|
+
{props.children}
|
|
42
|
+
</div>
|
|
43
|
+
);
|