wini-web-components 2.8.2 → 2.8.4

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.
Files changed (65) hide show
  1. package/package.json +6 -2
  2. package/src/component/button/button.module.css +210 -0
  3. package/src/component/button/button.tsx +57 -0
  4. package/src/component/calendar/calendar.module.css +153 -0
  5. package/src/component/calendar/calendar.tsx +389 -0
  6. package/src/component/carousel/carousel.css +622 -0
  7. package/src/component/carousel/carousel.tsx +91 -0
  8. package/src/component/checkbox/checkbox.module.css +48 -0
  9. package/src/component/checkbox/checkbox.tsx +80 -0
  10. package/src/component/ck-editor/ck-editor.css +206 -0
  11. package/src/component/ck-editor/ckeditor.tsx +522 -0
  12. package/src/component/component-status.tsx +53 -0
  13. package/src/component/date-time-picker/date-time-picker.module.css +94 -0
  14. package/src/component/date-time-picker/date-time-picker.tsx +663 -0
  15. package/src/component/dialog/dialog.module.css +111 -0
  16. package/src/component/dialog/dialog.tsx +109 -0
  17. package/src/component/import-file/import-file.module.css +83 -0
  18. package/src/component/import-file/import-file.tsx +174 -0
  19. package/src/component/infinite-scroll/infinite-scroll.module.css +34 -0
  20. package/src/component/infinite-scroll/infinite-scroll.tsx +35 -0
  21. package/src/component/input-multi-select/input-multi-select.module.css +121 -0
  22. package/src/component/input-multi-select/input-multi-select.tsx +263 -0
  23. package/src/component/input-otp/input-otp.module.css +41 -0
  24. package/src/component/input-otp/input-otp.tsx +110 -0
  25. package/src/component/number-picker/number-picker.module.css +137 -0
  26. package/src/component/number-picker/number-picker.tsx +107 -0
  27. package/src/component/pagination/pagination.module.css +48 -0
  28. package/src/component/pagination/pagination.tsx +88 -0
  29. package/src/component/popup/popup.css +136 -0
  30. package/src/component/popup/popup.tsx +125 -0
  31. package/src/component/progress-bar/progress-bar.module.css +42 -0
  32. package/src/component/progress-bar/progress-bar.tsx +33 -0
  33. package/src/component/progress-circle/progress-circle.css +0 -0
  34. package/src/component/progress-circle/progress-circle.tsx +25 -0
  35. package/src/component/radio-button/radio-button.module.css +51 -0
  36. package/src/component/radio-button/radio-button.tsx +60 -0
  37. package/src/component/rating/rating.module.css +11 -0
  38. package/src/component/rating/rating.tsx +65 -0
  39. package/src/component/select1/select1.module.css +108 -0
  40. package/src/component/select1/select1.tsx +271 -0
  41. package/src/component/switch/switch.module.css +53 -0
  42. package/src/component/switch/switch.tsx +68 -0
  43. package/src/component/table/table.css +74 -0
  44. package/src/component/table/table.tsx +108 -0
  45. package/src/component/tag/tag.module.css +108 -0
  46. package/src/component/tag/tag.tsx +31 -0
  47. package/src/component/text/text.css +27 -0
  48. package/src/component/text/text.tsx +24 -0
  49. package/src/component/text-area/text-area.module.css +57 -0
  50. package/src/component/text-area/text-area.tsx +65 -0
  51. package/src/component/text-field/text-field.module.css +71 -0
  52. package/src/component/text-field/text-field.tsx +102 -0
  53. package/src/component/toast-noti/toast-noti.css +866 -0
  54. package/src/component/toast-noti/toast-noti.tsx +22 -0
  55. package/src/component/wini-icon/winicon.module.css +110 -0
  56. package/src/component/wini-icon/winicon.tsx +9424 -0
  57. package/src/form/login/view.module.css +80 -0
  58. package/src/form/login/view.tsx +138 -0
  59. package/src/global.d.ts +5 -0
  60. package/src/index.tsx +66 -0
  61. package/src/language/i18n.tsx +143 -0
  62. package/src/skin/layout.css +649 -0
  63. package/src/skin/root.css +294 -0
  64. package/src/skin/typography.css +314 -0
  65. package/src/vite-env.d.ts +1 -0
@@ -0,0 +1,48 @@
1
+ .custom-pagination {
2
+ width: 100%;
3
+ height: 100%;
4
+ justify-content: space-between;
5
+ gap: 1.6rem;
6
+ }
7
+
8
+ .custom-pagination>.row {
9
+ gap: 1.6rem;
10
+ height: 100%;
11
+ }
12
+
13
+ .items-per-page-container {
14
+ gap: 8px;
15
+ height: 100%;
16
+ }
17
+
18
+ .pagination {
19
+ gap: 0.8rem;
20
+ }
21
+
22
+ .pagination>li {
23
+ width: 2.4rem;
24
+ height: 2.4rem;
25
+ color: #161C2499;
26
+ justify-content: center;
27
+ border-radius: 0.4rem;
28
+ font-weight: 600;
29
+ }
30
+
31
+ .pagination>li * {
32
+ color: inherit;
33
+ font: inherit;
34
+ font-size: inherit;
35
+ font-family: inherit;
36
+ line-height: inherit;
37
+ font-weight: inherit;
38
+ text-align: inherit;
39
+ letter-spacing: inherit;
40
+ }
41
+
42
+ .pagination>li.active {
43
+ background-color: var(--infor-background);
44
+ }
45
+
46
+ .pagination>li.active {
47
+ color: var(--infor-main-color);
48
+ }
@@ -0,0 +1,88 @@
1
+ import { CSSProperties, useEffect, useRef } from "react";
2
+ import ReactPaginate from "react-paginate";
3
+ import styles from './pagination.module.css';
4
+ import { Select1 } from "../select1/select1";
5
+ import { Text } from "../text/text";
6
+ import { TextField } from "../text-field/text-field";
7
+ import { Winicon } from "../wini-icon/winicon";
8
+ import { useTranslation } from "react-i18next";
9
+
10
+ interface Props {
11
+ id?: string,
12
+ currentPage: number,
13
+ itemPerPage: number,
14
+ totalItem: number,
15
+ onChangePage: Function,
16
+ hideGoToPage?: boolean,
17
+ hidePageSize?: boolean,
18
+ style: CSSProperties,
19
+ }
20
+
21
+ export function Pagination({ id, currentPage, itemPerPage, totalItem, onChangePage, hidePageSize = false, hideGoToPage = false, style }: Props) {
22
+ const goToPageRef = useRef<TextField>(null)
23
+ const { t } = useTranslation()
24
+
25
+ useEffect(() => {
26
+ if (goToPageRef.current) {
27
+ const _inputPage = goToPageRef.current.getInput()
28
+ if (_inputPage) _inputPage.value = currentPage.toString()
29
+ }
30
+ }, [currentPage])
31
+
32
+ if (currentPage > 1 && (totalItem === 0 || (Math.floor(totalItem / itemPerPage) + (totalItem % itemPerPage === 0 ? 0 : 1)) < currentPage)) {
33
+ onChangePage(1, itemPerPage);
34
+ return <div></div>;
35
+ } else if (totalItem > 0) {
36
+ return <div id={id} className={`${styles['custom-pagination']} row`} style={style}>
37
+ {hidePageSize ? null : <div className="row" style={{ gap: '0.8rem' }}>
38
+ <Select1
39
+ readOnly
40
+ placeholder={itemPerPage.toString()}
41
+ options={[10, 20, 50, 80, 100, 150, 200].map((item, _) => { return { id: item, name: item } })}
42
+ style={{ borderRadius: '0.4rem', width: '5.6rem', padding: '0 0.8rem', height: '2.4rem' }}
43
+ onChange={(ev: any) => {
44
+ onChangePage(currentPage, isNaN(parseInt(ev.id)) ? itemPerPage : parseInt(ev.id));
45
+ }}
46
+ />
47
+ <Text className="body-3">{t("ofItems", { totalItem: totalItem })}</Text>
48
+ </div>}
49
+ <div style={{ flex: 1 }} />
50
+ <ReactPaginate
51
+ onPageChange={(ev) => {
52
+ onChangePage(ev.selected + 1, itemPerPage);
53
+ }}
54
+ forcePage={currentPage - 1}
55
+ breakClassName="row button-text-3"
56
+ breakLabel="..."
57
+ pageCount={Math.ceil(totalItem / itemPerPage)}
58
+ previousClassName="row"
59
+ previousLabel={<Winicon src={"fill/arrows/left-arrow"} size={"1.4rem"} />}
60
+ nextClassName="row"
61
+ nextLabel={<Winicon src={"fill/arrows/right-arrow"} size={"1.4rem"} />}
62
+ containerClassName={`${styles['pagination']} row`}
63
+ pageClassName="row button-text-3"
64
+ activeClassName={styles['active']}
65
+ hrefBuilder={(pageIndex: number) =>
66
+ pageIndex >= 1 && pageIndex <= Math.ceil(totalItem / itemPerPage) ? `/page/${pageIndex}` : '#'
67
+ }
68
+ renderOnZeroPageCount={null}
69
+ />
70
+ {hideGoToPage ? null : <>
71
+ <div style={{ height: '1.6rem', backgroundColor: "var(--neutral-bolder-border-color)", width: 1 }} />
72
+ <Text className="label-3">{t("go")} {t("page").toLowerCase()}</Text>
73
+ <TextField
74
+ ref={goToPageRef as any}
75
+ style={{ width: '4.8rem', textAlign: "center", padding: 0, height: '2.4rem', borderRadius: '0.4rem' }}
76
+ className="body-3"
77
+ type="number"
78
+ onBlur={(ev) => {
79
+ const _tmp = ev.target.value.trim().length ? parseInt(ev.target.value.trim()) : undefined
80
+ if (_tmp && !isNaN(_tmp) && _tmp > 0 && _tmp <= Math.ceil(totalItem / itemPerPage)) {
81
+ onChangePage(_tmp, itemPerPage)
82
+ } else ev.target.value = ""
83
+ }}
84
+ />
85
+ </>}
86
+ </div>
87
+ } else return <div id={id} />
88
+ }
@@ -0,0 +1,136 @@
1
+ .popup-overlay {
2
+ position: fixed;
3
+ top: 0 !important;
4
+ left: 0 !important;
5
+ right: 0 !important;
6
+ bottom: 0 !important;
7
+ width: 100dvw !important;
8
+ height: 100dvh !important;
9
+ background-color: #000000B2;
10
+ display: flex !important;
11
+ justify-content: center;
12
+ align-items: center;
13
+ z-index: 99;
14
+ }
15
+
16
+ .popup-container {
17
+ position: relative;
18
+ background-color: var(--neutral-absolute-background-color);
19
+ border-radius: 0.8rem;
20
+ border: var(--neutral-main-border);
21
+ overflow: visible;
22
+ max-width: 86%;
23
+ max-height: 80%;
24
+ }
25
+
26
+ .popup-header {
27
+ align-items: center;
28
+ justify-content: start;
29
+ padding: 1.6rem 2.4rem;
30
+ border-bottom: var(--neutral-main-border);
31
+ }
32
+
33
+ .popup-footer {
34
+ column-gap: 0.8rem;
35
+ justify-content: end;
36
+ padding: 1.2rem 2.4rem;
37
+ border-top: var(--neutral-main-border);
38
+ }
39
+
40
+ .popup-container>.popup-close-btn {
41
+ position: absolute;
42
+ right: 0;
43
+ top: 0;
44
+ transform: translate(50%, -50%);
45
+ width: 3.2rem;
46
+ height: 3.2rem;
47
+ padding: 0.6rem;
48
+ border-radius: 50%;
49
+ align-items: center;
50
+ justify-content: center;
51
+ background-color: var(--neutral-absolute-background-color);
52
+ box-shadow: 0px 15px 50px 0px rgba(27, 32, 50, .1);
53
+ }
54
+
55
+ .popup-overlay.hidden-overlay {
56
+ background-color: transparent;
57
+ width: fit-content !important;
58
+ height: fit-content !important;
59
+ }
60
+
61
+ .popup-overlay.hidden-overlay>* {
62
+ position: fixed !important;
63
+ z-index: 99;
64
+ border-radius: 0.8rem;
65
+ background-color: var(--neutral-absolute-background-color);
66
+ }
67
+
68
+ .popup-overlay>* {
69
+ transition: width 0.8s ease-in-out, max-width 0.8s ease-in-out, height 0.8s ease-in-out, max-height 0.8s ease-in-out, border-radius 0.8s ease-in-out;
70
+ box-shadow: 2px 0px 16px 0px hsla(0, 0%, 0%, 0.04);
71
+ }
72
+
73
+ .popup-overlay.hidden-overlay>.popup-container {
74
+ position: absolute;
75
+ width: max-content;
76
+ height: max-content;
77
+ background-color: transparent;
78
+ }
79
+
80
+ .popup-overlay.hidden-overlay>.popup-container .popup-close-btn {
81
+ display: none !important;
82
+ }
83
+
84
+ @keyframes open-drawer-right-to-left {
85
+ 0% {
86
+ transform: translateX(100%);
87
+ }
88
+
89
+ 100% {
90
+ transform: translateX(0);
91
+ }
92
+ }
93
+
94
+ .right-drawer {
95
+ top: 0;
96
+ right: 0;
97
+ height: 100dvh;
98
+ animation: open-drawer-right-to-left 0.6s ease-in-out;
99
+ animation-fill-mode: forwards;
100
+ }
101
+
102
+ @keyframes open-drawer-left-to-right {
103
+ 0% {
104
+ transform: translateX(-100%);
105
+ }
106
+
107
+ 100% {
108
+ transform: translateX(0);
109
+ }
110
+ }
111
+
112
+ .left-drawer {
113
+ top: 0;
114
+ left: 0;
115
+ height: 100dvh;
116
+ animation: open-drawer-left-to-right 0.6s ease-in-out;
117
+ animation-fill-mode: forwards;
118
+ }
119
+
120
+ @keyframes show-dropdown-popup {
121
+ 0% {
122
+ opacity: 0;
123
+ height: 0;
124
+ width: 0;
125
+ transform: scale(0);
126
+ }
127
+
128
+ 100% {
129
+ opacity: 1;
130
+ transform: scale(1);
131
+ }
132
+ }
133
+
134
+ .dropdown-popup {
135
+ animation: show-dropdown-popup 0.4s ease-in-out;
136
+ }
@@ -0,0 +1,125 @@
1
+ import React, { CSSProperties, ReactNode, useEffect, useRef } from 'react'
2
+ import './popup.css'
3
+
4
+ interface PopupState {
5
+ readonly open?: boolean,
6
+ heading?: ReactNode,
7
+ body?: ReactNode,
8
+ content?: ReactNode,
9
+ footer?: ReactNode,
10
+ clickOverlayClosePopup?: boolean,
11
+ style?: CSSProperties,
12
+ className?: string,
13
+ hideButtonClose?: boolean,
14
+ }
15
+
16
+ export const showPopup = (props: {
17
+ ref: React.RefObject<Popup | undefined>,
18
+ heading?: ReactNode,
19
+ content?: ReactNode,
20
+ body?: ReactNode,
21
+ footer?: ReactNode,
22
+ clickOverlayClosePopup?: boolean,
23
+ style?: CSSProperties,
24
+ className?: string,
25
+ hideButtonClose?: boolean
26
+ }) => {
27
+ props.ref?.current?.onOpen({
28
+ heading: props.heading,
29
+ content: props.content,
30
+ body: props.body,
31
+ footer: props.footer,
32
+ clickOverlayClosePopup: props.clickOverlayClosePopup,
33
+ style: props.style,
34
+ className: props.className,
35
+ hideButtonClose: props.hideButtonClose
36
+ })
37
+ }
38
+
39
+ export const closePopup = (ref: React.RefObject<Popup>) => {
40
+ ref.current.onClose()
41
+ }
42
+
43
+ export class Popup extends React.Component<Object, PopupState> {
44
+ constructor(props: Object | Readonly<Object>) {
45
+ super(props);
46
+ }
47
+ state: Readonly<PopupState> = {
48
+ open: false,
49
+ }
50
+
51
+ onOpen(data: PopupState) {
52
+ this.setState({ open: true, ...data })
53
+ }
54
+
55
+ onClose() {
56
+ this.setState({ open: false })
57
+ }
58
+
59
+ render() {
60
+ return (
61
+ <>
62
+ {this.state.open &&
63
+ <PopupOverlay className={this.state.clickOverlayClosePopup ? 'hidden-overlay' : ''} onClose={this.state.clickOverlayClosePopup ? () => { this.onClose() } : undefined}>
64
+ {this.state.content ?? <div className={`popup-container col ${this.state.className ?? ""}`} onClick={e => e.stopPropagation()} style={this.state.style} >
65
+ {this.state.heading}
66
+ {this.state.body}
67
+ {this.state.footer}
68
+ {this.state.hideButtonClose ? null : <button type='button' onClick={() => this.onClose()} className='popup-close-btn row' >
69
+ <svg width='100%' height='100%' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg' style={{ width: '2rem', height: '2rem' }} >
70
+ <path fillRule='evenodd' clipRule='evenodd' d='M16.4223 4.7559C16.7477 4.43047 16.7477 3.90283 16.4223 3.57739C16.0968 3.25195 15.5692 3.25195 15.2438 3.57739L9.99967 8.82147L4.7556 3.57739C4.43016 3.25195 3.90252 3.25195 3.57709 3.57739C3.25165 3.90283 3.25165 4.43047 3.57709 4.7559L8.82116 9.99998L3.57709 15.2441C3.25165 15.5695 3.25165 16.0971 3.57709 16.4226C3.90252 16.748 4.43016 16.748 4.7556 16.4226L9.99967 11.1785L15.2438 16.4226C15.5692 16.748 16.0968 16.748 16.4223 16.4226C16.7477 16.0971 16.7477 15.5695 16.4223 15.2441L11.1782 9.99998L16.4223 4.7559Z' fill='#00204D' fillOpacity={0.6} />
71
+ </svg>
72
+ </button>}
73
+ </div>}
74
+ </PopupOverlay>}
75
+ </>
76
+ )
77
+ }
78
+ }
79
+
80
+ export function PopupOverlay({ children, onClose, className, style, onOpen }: { children?: ReactNode, className?: string, onClose?: (ev: MouseEvent) => void, style?: CSSProperties, onOpen?: (ev: HTMLDivElement) => void }) {
81
+ const overlayRef = useRef<HTMLDivElement>(null)
82
+
83
+ useEffect(() => {
84
+ if (overlayRef.current && onClose) {
85
+ const onClickDropDown = (ev: any) => {
86
+ if (ev.target === overlayRef.current || !overlayRef.current!.contains(ev.target)) onClose(ev)
87
+ }
88
+ window.document.body.addEventListener("mousedown", onClickDropDown)
89
+ return () => {
90
+ window.document.body.removeEventListener("mousedown", onClickDropDown)
91
+ }
92
+ }
93
+ }, [overlayRef.current])
94
+
95
+ useEffect(() => {
96
+ if (overlayRef.current && onOpen) onOpen(overlayRef.current)
97
+ }, [overlayRef.current, onOpen])
98
+
99
+ useEffect(() => {
100
+ if (overlayRef.current && overlayRef.current.firstChild) {
101
+ const popupContent = overlayRef.current.firstChild as HTMLElement
102
+ const rect = popupContent.getBoundingClientRect()
103
+ if (rect.x < 0) {
104
+ popupContent.style.left = "0px"
105
+ popupContent.style.right = "unset"
106
+ } else if (rect.right > document.body.offsetWidth) {
107
+ popupContent.style.right = "0px"
108
+ popupContent.style.left = "unset"
109
+ }
110
+ if (rect.y < 0) {
111
+ popupContent.style.top = "0px"
112
+ popupContent.style.bottom = "unset"
113
+ } else if (rect.bottom > document.body.offsetHeight) {
114
+ popupContent.style.bottom = "0px"
115
+ popupContent.style.top = "unset"
116
+ }
117
+ }
118
+ }, [overlayRef])
119
+
120
+ return <div
121
+ ref={overlayRef}
122
+ className={`popup-overlay ${className ?? ""}`}
123
+ style={style}
124
+ >{children}</div>
125
+ }
@@ -0,0 +1,42 @@
1
+ .progress-bar-container {
2
+ row-gap: 1.2rem;
3
+ width: 38.2rem;
4
+ }
5
+
6
+ .progress-bar-container>.progress-bar-title {
7
+ justify-content: space-between;
8
+ width: 100%;
9
+ }
10
+
11
+ .progress-bar-container>.progress-bar-tile {
12
+ column-gap: 1.6rem;
13
+ width: 100%;
14
+ height: 2.2rem;
15
+ }
16
+
17
+ .progress-bar-container>.progress-bar-tile>.progress-bar-value {
18
+ border-radius: 10rem;
19
+ width: 100%;
20
+ flex: 1;
21
+ height: 0.6rem;
22
+ position: relative;
23
+ overflow: hidden;
24
+ background-color: var(--full-color);
25
+ }
26
+
27
+ .progress-bar-container>.progress-bar-tile>.progress-bar-value::after {
28
+ content: '';
29
+ position: absolute;
30
+ top: 0;
31
+ left: 0;
32
+ width: var(--percent);
33
+ height: 0.6rem;
34
+ border-radius: 10rem;
35
+ background-color: var(--percent-color);
36
+ transition: width ease-in-out 1s;
37
+ }
38
+
39
+ .status-icon>div {
40
+ width: 1.6rem !important;
41
+ height: 1.6rem !important;
42
+ }
@@ -0,0 +1,33 @@
1
+ import { CSSProperties, ReactNode, useState } from 'react'
2
+ import styles from './progress-bar.module.css'
3
+ import React from 'react'
4
+ import { ComponentStatus, getStatusIcon, Winicon } from '../../index'
5
+
6
+ export function ProgressBar({ id, status = ComponentStatus.INFOR, percent = 100, titleText, title, hideTitle = false, progressBarOnly = false, fullColor = 'var(--neutral-main-background-color)', percentColor = 'var(--primary-main-color)', style, progressBarStyle }: {
7
+ id?: string,
8
+ percent: number,
9
+ titleText?: string,
10
+ title?: ReactNode,
11
+ hideTitle: boolean,
12
+ progressBarOnly: boolean,
13
+ fullColor: string,
14
+ percentColor: string,
15
+ style?: CSSProperties,
16
+ status: ComponentStatus,
17
+ progressBarStyle?: CSSProperties
18
+ }) {
19
+ const [openDetails, setOpenDetails] = useState(true)
20
+
21
+ return <div id={id} className={`col ${styles["progress-bar-container"]}`} style={style ? { padding: progressBarOnly ? '0' : '1.6rem 2.4rem', ...style } : { padding: progressBarOnly ? '0' : '1.6rem 2.4rem' }}>
22
+ {(hideTitle || progressBarOnly) ? null : (title ?? <div className={`row ${styles["progress-bar-title"]}`}>
23
+ <div className="heading-8">{titleText}</div>
24
+ <Winicon src={openDetails ? "fill/arrows/down-arrow" : "fill/arrows/up-arrow"} onClick={() => { setOpenDetails(!openDetails) }} />
25
+ </div>)}
26
+ {openDetails ? <div className={`row ${styles["progress-bar-tile"]}`} >
27
+ <div className={styles["progress-bar-value"]} style={{ '--percent-color': percentColor, '--full-color': fullColor, '--percent': `${percent}%`, ...(progressBarStyle ?? {}) } as CSSProperties}></div>
28
+ {progressBarOnly || status === ComponentStatus.INFOR ? null : <div className={`${styles["status-icon"]}`}>{getStatusIcon(status)}</div>}
29
+ {progressBarOnly ? null : <div className='label-4'>{percent}/100</div>}
30
+ </div> : null}
31
+
32
+ </div>
33
+ }
@@ -0,0 +1,25 @@
1
+ import { CSSProperties } from 'react'
2
+ import './progress-circle.css'
3
+ import React from 'react'
4
+
5
+ export function ProgressCircle(props: {
6
+ id?: string,
7
+ /** value: 0 - 100 (%)*/
8
+ percent?: number,
9
+ style?: CSSProperties,
10
+ fillColor?: string,
11
+ percentColor?: string,
12
+ strokeWidth?: number,
13
+ strokeColor?: string,
14
+ textStyle?: CSSProperties
15
+ }) {
16
+ const radius = 30 - (props.strokeWidth ?? 4)
17
+ const diameter = Math.PI * 2 * radius;
18
+ const strokeOffset = (1 - ((props.percent ?? 0) / 100)) * diameter;
19
+ return <svg id={props.id} width="100%" height="100%" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ width: '6rem', height: '6rem', ...(props.style ?? {}) }} >
20
+ <path d={`M 30,30 m 0,-${radius} a ${radius},${radius} 0 1 1 0,${2 * radius} a ${radius},${radius} 0 1 1 0,-${2 * radius}`} style={{ fill: "none", stroke: props.strokeColor ?? "var(--neutral-main-background-color)", strokeWidth: props.strokeWidth ?? '4px', }} />
21
+ <path d={`M 30,30 m 0,-${radius} a ${radius},${radius} 0 1 1 0,${2 * radius} a ${radius},${radius} 0 1 1 0,-${2 * radius}`} style={{ fill: props.fillColor ?? "none", stroke: props.percentColor ?? "var(--primary-main-color)", strokeWidth: props.strokeWidth ?? '4px', strokeLinecap: 'round', strokeDasharray: `${diameter}px ${diameter}px`, strokeDashoffset: `${strokeOffset}px` }} />
22
+ <text x="50%" y="50%" dy=".3em" textAnchor="middle" fontSize={"1.6rem"} fontWeight={'600'} style={{ fill: "var(neutral-text-title-color)", ...(props.textStyle ?? {}) }}>{props.percent ?? 0}%</text>
23
+ </svg>
24
+ }
25
+
@@ -0,0 +1,51 @@
1
+ .radio-btn-container {
2
+ height: max(var(--size), 14px);
3
+ width: max(var(--size), 14px);
4
+ aspect-ratio: 1 / 1 !important;
5
+ border-radius: 50%;
6
+ box-sizing: border-box;
7
+ border: 2px solid var(--off-color);
8
+ background-color: transparent !important;
9
+ transition: border-color ease-in-out 0.5s;
10
+ justify-content: center;
11
+ cursor: pointer;
12
+ }
13
+
14
+ .radio-btn-container>input {
15
+ display: none;
16
+ opacity: 0;
17
+ cursor: pointer;
18
+ }
19
+
20
+ .radio-btn-container>.checkmark {
21
+ height: inherit;
22
+ width: inherit;
23
+ scale: 0.5;
24
+ transform-origin: center center;
25
+ border-radius: 50%;
26
+ aspect-ratio: 1 / 1;
27
+ visibility: hidden;
28
+ opacity: 0;
29
+ transition: visibility, opacity ease-in-out 0.5s;
30
+ }
31
+
32
+ .radio-btn-container>input:checked~.checkmark {
33
+ visibility: visible;
34
+ opacity: 1;
35
+ background-color: var(--active-color);
36
+ }
37
+
38
+ .radio-btn-container:has(> input:checked) {
39
+ border-color: var(--active-color) !important;
40
+ }
41
+
42
+ .radio-btn-container:has(input:disabled) {
43
+ pointer-events: none !important;
44
+ border-color: var(--off-color) !important;
45
+ background-color: var(--neutral-disable-background-color) !important;
46
+ }
47
+
48
+ .radio-btn-container>input:disabled~.checkmark {
49
+ display: block !important;
50
+ background-color: var(--neutral-text-disabled-color);
51
+ }
@@ -0,0 +1,60 @@
1
+ import React, { CSSProperties } from 'react';
2
+ import styles from './radio-button.module.css';
3
+ import { UseFormRegister } from 'react-hook-form';
4
+
5
+ interface RadioButtonProps {
6
+ id?: string,
7
+ onChange?: React.ChangeEventHandler<HTMLInputElement>,
8
+ value?: string | number | readonly string[],
9
+ disabled?: boolean,
10
+ style?: CSSProperties,
11
+ size?: number | string,
12
+ defaultChecked?: boolean,
13
+ name?: string,
14
+ activeColor?: string,
15
+ offColor?: string,
16
+ className?: string,
17
+ register?: UseFormRegister<{}>,
18
+ }
19
+
20
+
21
+ export class RadioButton extends React.Component<RadioButtonProps> {
22
+ render(): React.ReactNode {
23
+ let convertStyle: CSSProperties = {
24
+ '--off-color': this.props.offColor ?? 'var(--neutral-bolder-border-color)',
25
+ '--active-color': this.props.activeColor ?? 'var(--primary-main-color)',
26
+ '--size': this.props.size ? (typeof this.props.size === 'number') ? `${this.props.size}px` : this.props.size : '20px'
27
+ } as CSSProperties
28
+ if (this.props.style) {
29
+ delete this.props.style.width
30
+ delete this.props.style.minWidth
31
+ delete this.props.style.maxWidth
32
+ delete this.props.style.height
33
+ delete this.props.style.minHeight
34
+ delete this.props.style.maxHeight
35
+ convertStyle = {
36
+ ...this.props.style,
37
+ ...convertStyle,
38
+ }
39
+ }
40
+
41
+ return <label id={this.props.id} className={`row ${styles['radio-btn-container']} ${this.props.className ?? ''}`} style={convertStyle} >
42
+ {this.props.register ?
43
+ <input
44
+ {...this.props.register}
45
+ type="radio"
46
+ value={this.props.value}
47
+ disabled={this.props.disabled}
48
+ /> :
49
+ <input type="radio"
50
+ name={this.props.name}
51
+ value={this.props.value}
52
+ defaultChecked={this.props.defaultChecked}
53
+ disabled={this.props.disabled}
54
+ onChange={this.props.onChange}
55
+ />
56
+ }
57
+ <span className={styles['checkmark']}></span>
58
+ </label>
59
+ }
60
+ }
@@ -0,0 +1,11 @@
1
+ .rating-container {
2
+ gap: 0.8rem;
3
+ }
4
+
5
+ .rating-container svg {
6
+ cursor: pointer;
7
+ }
8
+
9
+ .rating-container svg > * {
10
+ pointer-events: none !important;
11
+ }