From 17ba979e41fb188f463a67fceb070d8f9edfe4df Mon Sep 17 00:00:00 2001 From: it-is-not-alright Date: Tue, 22 Oct 2024 23:11:03 +0400 Subject: [PATCH] [front]: gitignore fix --- .gitignore | 1 - .../ripple/parts/ripple-wave/component.tsx | 39 +++++++++ .../ripple/parts/ripple-wave/index.ts | 1 + .../parts/ripple-wave/style.module.scss | 33 +++++++ .../ripple/parts/ripple-wave/types.ts | 6 ++ .../parts/calendar-days/component.tsx | 81 ++++++++++++++++++ .../calendar/parts/calendar-days/constants.ts | 24 ++++++ .../ui/calendar/parts/calendar-days/index.ts | 2 + .../parts/calendar-days/styles.module.scss | 85 +++++++++++++++++++ .../ui/calendar/parts/calendar-days/types.ts | 26 ++++++ .../ui/calendar/parts/calendar-days/utils.ts | 52 ++++++++++++ .../src/components/ui/calendar/parts/index.ts | 1 + .../parts/windmill-table-row/component.tsx | 68 +++++++++++++++ .../parts/windmill-table-row/index.tsx | 1 + .../windmill-table-row/style.module.scss | 16 ++++ .../parts/windmill-table-row/types.ts | 8 ++ 16 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 front/src/components/ui/animation/ripple/parts/ripple-wave/component.tsx create mode 100644 front/src/components/ui/animation/ripple/parts/ripple-wave/index.ts create mode 100644 front/src/components/ui/animation/ripple/parts/ripple-wave/style.module.scss create mode 100644 front/src/components/ui/animation/ripple/parts/ripple-wave/types.ts create mode 100644 front/src/components/ui/calendar/parts/calendar-days/component.tsx create mode 100644 front/src/components/ui/calendar/parts/calendar-days/constants.ts create mode 100644 front/src/components/ui/calendar/parts/calendar-days/index.ts create mode 100644 front/src/components/ui/calendar/parts/calendar-days/styles.module.scss create mode 100644 front/src/components/ui/calendar/parts/calendar-days/types.ts create mode 100644 front/src/components/ui/calendar/parts/calendar-days/utils.ts create mode 100644 front/src/components/ui/calendar/parts/index.ts create mode 100644 front/src/components/ux/windmill-table/parts/windmill-table-row/component.tsx create mode 100644 front/src/components/ux/windmill-table/parts/windmill-table-row/index.tsx create mode 100644 front/src/components/ux/windmill-table/parts/windmill-table-row/style.module.scss create mode 100644 front/src/components/ux/windmill-table/parts/windmill-table-row/types.ts diff --git a/.gitignore b/.gitignore index 1dd9dcb..04585e3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ eggs/ .eggs/ lib/ lib64/ -parts/ sdist/ var/ wheels/ diff --git a/front/src/components/ui/animation/ripple/parts/ripple-wave/component.tsx b/front/src/components/ui/animation/ripple/parts/ripple-wave/component.tsx new file mode 100644 index 0000000..1227520 --- /dev/null +++ b/front/src/components/ui/animation/ripple/parts/ripple-wave/component.tsx @@ -0,0 +1,39 @@ +import clsx from 'clsx'; +import React, { useEffect, useState } from 'react'; + +import styles from './style.module.scss'; +import { RippleWaveProps } from './types'; + +export function RippleWave({ style, onDone }: RippleWaveProps) { + const [isMouseUp, setIsMouseUp] = useState(false); + const [isAnimationEnd, setIsAnimationEnd] = useState(false); + + useEffect(() => { + const mouseUpListener = () => setIsMouseUp(true); + document.addEventListener('mouseup', mouseUpListener, { once: true }); + document.addEventListener('touchend', mouseUpListener, { once: true }); + }, []); + + const visible = !isMouseUp || !isAnimationEnd; + + const className = clsx( + styles.wave, + visible ? styles.visible : styles.invisible, + ); + + const handleAnimationEnd = (event: React.AnimationEvent) => { + if (event.animationName === styles.fadein) { + setIsAnimationEnd(true); + } else { + onDone(); + } + }; + + return ( +
+ ); +} diff --git a/front/src/components/ui/animation/ripple/parts/ripple-wave/index.ts b/front/src/components/ui/animation/ripple/parts/ripple-wave/index.ts new file mode 100644 index 0000000..74e0c72 --- /dev/null +++ b/front/src/components/ui/animation/ripple/parts/ripple-wave/index.ts @@ -0,0 +1 @@ +export { RippleWave } from './component'; diff --git a/front/src/components/ui/animation/ripple/parts/ripple-wave/style.module.scss b/front/src/components/ui/animation/ripple/parts/ripple-wave/style.module.scss new file mode 100644 index 0000000..a9319f5 --- /dev/null +++ b/front/src/components/ui/animation/ripple/parts/ripple-wave/style.module.scss @@ -0,0 +1,33 @@ +.wave { + position: absolute; + border-radius: 100%; + background-color: var(--clr-ripple); +} + +.visible { + animation: fadein 0.3s linear; +} + +.invisible { + animation: fadeout 0.3s linear forwards; +} + +@keyframes fadein { + from { + opacity: 0; + scale: 0; + } + to { + opacity: 1; + scale: 1; + } +} + +@keyframes fadeout { + from { + opacity: 1; + } + to { + opacity: 0; + } +} diff --git a/front/src/components/ui/animation/ripple/parts/ripple-wave/types.ts b/front/src/components/ui/animation/ripple/parts/ripple-wave/types.ts new file mode 100644 index 0000000..3588e4d --- /dev/null +++ b/front/src/components/ui/animation/ripple/parts/ripple-wave/types.ts @@ -0,0 +1,6 @@ +import { CSSProperties } from 'react'; + +export type RippleWaveProps = { + style: CSSProperties; + onDone: () => void; +}; diff --git a/front/src/components/ui/calendar/parts/calendar-days/component.tsx b/front/src/components/ui/calendar/parts/calendar-days/component.tsx new file mode 100644 index 0000000..da2bc54 --- /dev/null +++ b/front/src/components/ui/calendar/parts/calendar-days/component.tsx @@ -0,0 +1,81 @@ +import { IconButton } from '@components/ui/icon-button'; +import { RawButton } from '@components/ui/raw'; +import { Span } from '@components/ui/span'; +import ArrowDownIcon from '@public/images/svg/arrow-down.svg'; +import ArrowUpIcon from '@public/images/svg/arrow-up.svg'; +import clsx from 'clsx'; +import React, { useMemo } from 'react'; + +import { DAYS_OF_THE_WEEK, MONTHS } from './constants'; +import styles from './styles.module.scss'; +import { CalendarDaysProps } from './types'; +import { getCalendarDays } from './utils'; + +export function CalendarDays({ + value, + onChange, + min, + max, + date, + onMonthChange, +}: CalendarDaysProps) { + const today = useMemo(() => new Date(), []); + + const days = useMemo(() => { + return getCalendarDays({ + year: date.getFullYear(), + monthIndex: date.getMonth(), + today, + selectedDateStr: value, + min, + max, + }); + }, [date, min, max]); + + const handleChange = (newValue: string) => { + onChange?.(newValue); + }; + + return ( +
+
+ + {MONTHS[date.getMonth()]} {date.getFullYear()} + + onMonthChange(-1)} + > + + + onMonthChange(1)} + > + + +
+
+ {DAYS_OF_THE_WEEK.map((day) => ( + + {day} + + ))} + {days.map((day, index) => ( + handleChange(day.string)} + > + {day.number} + {day.isToday &&
} + + ))} +
+
+ ); +} diff --git a/front/src/components/ui/calendar/parts/calendar-days/constants.ts b/front/src/components/ui/calendar/parts/calendar-days/constants.ts new file mode 100644 index 0000000..6e53f1a --- /dev/null +++ b/front/src/components/ui/calendar/parts/calendar-days/constants.ts @@ -0,0 +1,24 @@ +export const DAYS_OF_THE_WEEK = [ + 'Mon', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat', + 'Sun', +]; + +export const MONTHS = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', +]; diff --git a/front/src/components/ui/calendar/parts/calendar-days/index.ts b/front/src/components/ui/calendar/parts/calendar-days/index.ts new file mode 100644 index 0000000..637fb4a --- /dev/null +++ b/front/src/components/ui/calendar/parts/calendar-days/index.ts @@ -0,0 +1,2 @@ +export { CalendarDays } from './component'; +export { type CalendarDaysProps } from './types'; diff --git a/front/src/components/ui/calendar/parts/calendar-days/styles.module.scss b/front/src/components/ui/calendar/parts/calendar-days/styles.module.scss new file mode 100644 index 0000000..46dd70c --- /dev/null +++ b/front/src/components/ui/calendar/parts/calendar-days/styles.module.scss @@ -0,0 +1,85 @@ +.header { + display: flex; + align-items: center; + margin-bottom: 10px; + gap: 10px; +} + +.title { + flex: 1; + padding-left: 5px; + font-size: 18px; + font-weight: 500; +} + +.turnButton { + padding: 10px; +} + +.daysGrid { + display: grid; + gap: 5px; + grid-template-columns: repeat(7, auto); +} + +.dayOfTheWeek { + display: flex; + width: 36px; + height: 36px; + align-items: center; + justify-content: center; + font-size: 14px; +} + +.day { + position: relative; + display: flex; + width: 36px; + height: 36px; + align-items: center; + justify-content: center; + border-radius: 10px; + color: var(--clr-text-100); + transition: all var(--td-100) ease-in-out; + + &:not(:disabled) { + cursor: pointer; + + &:hover { + background-color: var(--clr-layer-300-hover); + } + } + + &:disabled { + color: var(--clr-text-100); + } +} + +.currentMonthDay { + color: var(--clr-text-300); +} + +.selectedDay { + background-color: var(--clr-primary); + box-shadow: 0px 2px 2px var(--clr-shadow-200); + color: var(--clr-on-primary); + + &:hover { + background-color: var(--clr-primary-hover); + } + + .todayIndicator { + background-color: var(--clr-on-primary); + } +} + +.todayIndicator { + position: absolute; + bottom: 12%; + left: 50%; + width: 4px; + height: 4px; + border-radius: 50%; + background-color: var(--clr-text-300); + transform: translateX(-50%); +} diff --git a/front/src/components/ui/calendar/parts/calendar-days/types.ts b/front/src/components/ui/calendar/parts/calendar-days/types.ts new file mode 100644 index 0000000..ccd3598 --- /dev/null +++ b/front/src/components/ui/calendar/parts/calendar-days/types.ts @@ -0,0 +1,26 @@ +export type CalendarDay = { + number: number; + isDisabled: boolean; + isSelected: boolean; + isToday: boolean; + string: string; + isCurrentMonth: boolean; +}; + +export type CalendarDaysProps = { + value?: string; + onChange?: (value: string) => void; + min: Date | null; + max: Date | null; + date: Date; + onMonthChange: (delta: number) => void; +}; + +export type GetCalendarDaysParams = { + year: number; + monthIndex: number; + today: Date; + selectedDateStr?: string; + min: Date | null; + max: Date | null; +}; diff --git a/front/src/components/ui/calendar/parts/calendar-days/utils.ts b/front/src/components/ui/calendar/parts/calendar-days/utils.ts new file mode 100644 index 0000000..7acda21 --- /dev/null +++ b/front/src/components/ui/calendar/parts/calendar-days/utils.ts @@ -0,0 +1,52 @@ +import { dateToInputString } from '@utils/date'; + +import { CalendarDay, GetCalendarDaysParams } from './types'; + +const addDays = (date: Date, days: number) => { + date.setDate(date.getDate() + days); +}; + +const daysAreEqual = (date1: Date, date2: Date) => { + return ( + date1.getDate() === date2.getDate() && + date1.getMonth() === date2.getMonth() && + date1.getFullYear() === date2.getFullYear() + ); +}; + +export const getCalendarDays = ({ + year, + monthIndex, + today, + selectedDateStr, + min, + max, +}: GetCalendarDaysParams) => { + const selectedDate = new Date(selectedDateStr); + + const firstDayOfMonth = new Date(year, monthIndex, 1); + const daysFromPrevMonth = (firstDayOfMonth.getDay() || 7) - 1; + + const date = new Date(year, monthIndex, 1); + addDays(date, -daysFromPrevMonth); + const days: CalendarDay[] = []; + + for (let i = 0; i < 42; i += 1) { + const number = date.getDate(); + const isDisabled = (min && date < min) || (max && date > max); + const isSelected = daysAreEqual(date, selectedDate); + const isToday = daysAreEqual(date, today); + const string = dateToInputString(date); + const isCurrentMonth = date.getMonth() === monthIndex; + days.push({ + number, + isDisabled, + isSelected, + isToday, + string, + isCurrentMonth, + }); + addDays(date, 1); + } + return days; +}; diff --git a/front/src/components/ui/calendar/parts/index.ts b/front/src/components/ui/calendar/parts/index.ts new file mode 100644 index 0000000..1b1adc3 --- /dev/null +++ b/front/src/components/ui/calendar/parts/index.ts @@ -0,0 +1 @@ +export * from './calendar-days'; diff --git a/front/src/components/ux/windmill-table/parts/windmill-table-row/component.tsx b/front/src/components/ux/windmill-table/parts/windmill-table-row/component.tsx new file mode 100644 index 0000000..a9941b7 --- /dev/null +++ b/front/src/components/ux/windmill-table/parts/windmill-table-row/component.tsx @@ -0,0 +1,68 @@ +import { Checkbox } from '@components/ui'; +import { Input } from '@components/ui/input'; +import { WindmillConfig } from '@components/ux/windmill-form'; +import React from 'react'; + +import styles from './style.module.scss'; +import { WindmillTableRowProps } from './types'; + +export function WindmillTableRow({ + value, + onChange, + onSelect, + selected, +}: WindmillTableRowProps) { + const extractNumber = (event: React.ChangeEvent) => { + const { value } = event.target; + if (!value) { + return ''; + } + const regex = /^[-+]?\d*\.?\d*$/; + return event.target.value.match(regex)?.[0] ?? null; + }; + + const handleChange = ( + event: React.ChangeEvent, + min: number, + max: number, + key: keyof WindmillConfig, + ) => { + const num = extractNumber(event); + if (num === null) { + return; + } + const float = parseFloat(num); + if (Number.isNaN(float)) { + onChange({ ...value, [key]: num }); + return; + } + if (float >= min && float <= max) { + onChange({ ...value, [key]: num }); + } + }; + + return ( +
+ + handleChange(event, -90, 90, 'x')} + /> + handleChange(event, -180, 180, 'y')} + /> + handleChange(event, 0, 360, 'angle')} + /> +
+ ); +} diff --git a/front/src/components/ux/windmill-table/parts/windmill-table-row/index.tsx b/front/src/components/ux/windmill-table/parts/windmill-table-row/index.tsx new file mode 100644 index 0000000..ae57d36 --- /dev/null +++ b/front/src/components/ux/windmill-table/parts/windmill-table-row/index.tsx @@ -0,0 +1 @@ +export { WindmillTableRow } from './component'; diff --git a/front/src/components/ux/windmill-table/parts/windmill-table-row/style.module.scss b/front/src/components/ux/windmill-table/parts/windmill-table-row/style.module.scss new file mode 100644 index 0000000..be7c298 --- /dev/null +++ b/front/src/components/ux/windmill-table/parts/windmill-table-row/style.module.scss @@ -0,0 +1,16 @@ +.row { + display: grid; + grid-template-columns: auto 1fr 1fr 1fr; +} + +.checkboxLabel { + width: 46px; + justify-content: center; + border: 1px solid var(--clr-border-200); +} + +.textInput { + border-radius: 0; + background-color: var(--clr-layer-200); + box-shadow: none; +} diff --git a/front/src/components/ux/windmill-table/parts/windmill-table-row/types.ts b/front/src/components/ux/windmill-table/parts/windmill-table-row/types.ts new file mode 100644 index 0000000..7d212a7 --- /dev/null +++ b/front/src/components/ux/windmill-table/parts/windmill-table-row/types.ts @@ -0,0 +1,8 @@ +import { WindmillConfig } from '@components/ux/windmill-form'; + +export type WindmillTableRowProps = { + value: WindmillConfig; + onChange: (value: WindmillConfig) => void; + onSelect: () => void; + selected: boolean; +};