Skip to content
Merged

Dev #55

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 32 additions & 21 deletions library/src/components/form/DynamicForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
type UseFormWatch,
} from "react-hook-form"
import { twMerge } from "tailwind-merge"
import { Button } from "../Button"
import { Button, ButtonGroup } from "../Button"

export interface FormProps<T extends FieldValues> {
control: Control<T>
Expand Down Expand Up @@ -44,6 +44,9 @@ export interface DynamicFormProps<T extends FieldValues>
horizontal?: boolean
hideReset?: boolean
hideSave?: boolean

customSubmitButton?: (props: { disabled: boolean }) => React.ReactNode
customResetButton?: (props: { disabled: boolean }) => React.ReactNode
}

export function DynamicForm<T extends FieldValues>({
Expand All @@ -55,6 +58,8 @@ export function DynamicForm<T extends FieldValues>({
className,
hideSave,
hideReset,
customResetButton,
customSubmitButton,
...props
}: DynamicFormProps<T>) {
const {
Expand Down Expand Up @@ -95,26 +100,32 @@ export function DynamicForm<T extends FieldValues>({

<hr className="border border-border" />

<div className="flex flex-row items-end w-full justify-end mt-4">
{!hideReset && (
<Button
type="reset"
appearance="subtle"
disabled={!isDirty}
>
Reset
</Button>
)}
{!hideSave && (
<Button
appearance="primary"
type="submit"
disabled={!isValid}
>
Save
</Button>
)}
</div>
<ButtonGroup className="justify-end">
{!hideReset &&
(customResetButton ? (
customResetButton({ disabled: !isDirty })
) : (
<Button
type="reset"
appearance="subtle"
disabled={!isDirty}
>
Reset
</Button>
))}
{!hideSave &&
(customSubmitButton ? (
customSubmitButton({ disabled: !isValid })
) : (
<Button
appearance="primary"
type="submit"
disabled={!isValid}
>
Save
</Button>
))}
</ButtonGroup>
</form>
)
}
14 changes: 9 additions & 5 deletions library/src/components/timetable/TimeTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ import {
initAndUpdateTimeTableSelectionStore,
type onTimeRangeSelectedType,
} from "./TimeTableSelectionStore"
import { getStartAndEndSlot, useGroupRows } from "./useGoupRows"
import { useGroupRows } from "./useGoupRows"
import { twMerge } from "tailwind-merge"
import { getStartAndEndSlot } from "./timeTableUtils"

export interface TimeSlotBooking {
title: string
Expand Down Expand Up @@ -189,8 +190,8 @@ export interface LPTimeTableProps<

/** custom header row */
customHeaderRow?: {
timeSlot: (props: CustomHeaderRowTimeSlotProps) => JSX.Element
header: (props: CustomHeaderRowHeaderProps) => JSX.Element
timeSlot: (props: CustomHeaderRowTimeSlotProps<G, I>) => JSX.Element
header: (props: CustomHeaderRowHeaderProps<G, I>) => JSX.Element
}
}

Expand Down Expand Up @@ -257,6 +258,7 @@ const LPTimeTableImpl = <G extends TimeTableGroup, I extends TimeSlotBooking>({
)

// change on viewType
// biome-ignore lint/correctness/useExhaustiveDependencies: just remove the message is props change
useEffect(() => {
setMessage?.(undefined) // clear the message on time frame change
}, [viewType, startDate, endDate, setMessage, timeStepsMinutes])
Expand Down Expand Up @@ -490,8 +492,9 @@ const LPTimeTableImpl = <G extends TimeTableGroup, I extends TimeSlotBooking>({
}
ref={tableRef}
>
<LPTimeTableHeader
<LPTimeTableHeader<G, I>
slotsArray={slotsArray}
timeSlotMinutes={timeSlotMinutes}
columnWidth={columnWidth}
groupHeaderColumnWidth={groupHeaderColumnWidth}
startDate={startDate}
Expand All @@ -508,7 +511,8 @@ const LPTimeTableImpl = <G extends TimeTableGroup, I extends TimeSlotBooking>({
weekStartsOnSunday={weekStartsOnSunday}
locale={locale}
customHeaderRow={customHeaderRow}
ref={tableHeaderRef}
entries={entries}
tableHeaderRef={tableHeaderRef}
/>
<tbody ref={tableBodyRef} className="table-fixed">
<TimeTableRows<G, I>
Expand Down
158 changes: 118 additions & 40 deletions library/src/components/timetable/TimeTableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dayjs.extend(weekOfYear)
dayjs.extend(weekYear)
dayjs.extend(localeData)
import type React from "react"
import { Fragment, forwardRef } from "react"
import { Fragment, type RefObject, useRef } from "react"

// if more locales then english and germans are needed, we need to enable them first here
import "dayjs/locale/de"
Expand All @@ -15,7 +15,12 @@ import "dayjs/locale/de"
//import "dayjs/locale/it"
//import "dayjs/locale/nl"

import type { TimeTableViewType } from "./TimeTable"
import type {
TimeSlotBooking,
TimeTableEntry,
TimeTableGroup,
TimeTableViewType,
} from "./TimeTable"
import type { TimeFrameDay } from "./TimeTableConfigStore"

const headerTimeSlotFormat: { [viewType in TimeTableViewType]: string } = {
Expand Down Expand Up @@ -52,22 +57,36 @@ export function headerText(
}
}

export type CustomHeaderRowTimeSlotProps = {
export type CustomHeaderRowTimeSlotProps<
G extends TimeTableGroup,
I extends TimeSlotBooking,
> = {
timeSlot: Dayjs
timeSlotMinutes: number
isLastOfDay: boolean
viewType: TimeTableViewType
timeFrameOfDay: TimeFrameDay
entries: TimeTableEntry<G, I>[]
slotsArray: readonly Dayjs[]
tableCellRef: RefObject<HTMLTableCellElement>
}

export type CustomHeaderRowHeaderProps = {
export type CustomHeaderRowHeaderProps<
G extends TimeTableGroup,
I extends TimeSlotBooking,
> = {
slotsArray: readonly Dayjs[]
viewType: TimeTableViewType
timeFrameOfDay: TimeFrameDay
entries: TimeTableEntry<G, I>[]
}

type TimeTableHeaderProps = {
type TimeTableHeaderProps<
G extends TimeTableGroup,
I extends TimeSlotBooking,
> = {
slotsArray: readonly Dayjs[]
timeSlotMinutes: number
groupHeaderColumnWidth: number | string
columnWidth: number | string
startDate: Dayjs
Expand All @@ -80,29 +99,38 @@ type TimeTableHeaderProps = {
weekStartsOnSunday: boolean
locale?: "en" | "de"

entries: TimeTableEntry<G, I>[]
customHeaderRow?: {
timeSlot: (props: CustomHeaderRowTimeSlotProps) => JSX.Element
header: (props: CustomHeaderRowHeaderProps) => JSX.Element
timeSlot: (props: CustomHeaderRowTimeSlotProps<G, I>) => JSX.Element
header: (props: CustomHeaderRowHeaderProps<G, I>) => JSX.Element
}

tableHeaderRef: React.Ref<HTMLTableSectionElement>
}

export const LPTimeTableHeader = forwardRef(function TimeTableHeader(
{
slotsArray,
groupHeaderColumnWidth,
columnWidth,
startDate,
endDate,
viewType,
showTimeSlotHeader,
timeFrameDay,
dateHeaderTextFormat,
weekStartsOnSunday,
locale,
customHeaderRow,
}: TimeTableHeaderProps,
tableHeaderRef: React.Ref<HTMLTableSectionElement>,
) {
const headerCellBaseClassname =
"bg-surface border-transparent border-b-border after:border-border relative select-none border-0 border-b-2 border-solid p-0 pl-1 font-bold after:absolute after:bottom-[1px] after:right-0 after:top-0 after:h-full after:border-solid"

export const LPTimeTableHeader = function TimeTableHeader<
G extends TimeTableGroup,
I extends TimeSlotBooking,
>({
slotsArray,
timeSlotMinutes,
groupHeaderColumnWidth,
columnWidth,
startDate,
endDate,
viewType,
showTimeSlotHeader,
timeFrameDay,
dateHeaderTextFormat,
weekStartsOnSunday,
locale,
customHeaderRow,
entries,
tableHeaderRef,
}: TimeTableHeaderProps<G, I>) {
const currentLocale = dayjs.locale()
if (locale && locale !== currentLocale) {
dayjs.locale(locale)
Expand Down Expand Up @@ -254,7 +282,7 @@ export const LPTimeTableHeader = forwardRef(function TimeTableHeader(
<th
key={`timeheader${slot.unix()}`}
colSpan={2}
className={`bg-surface border-transparent border-b-border after:border-border relative select-none border-0 border-b-2 border-solid p-0 pl-1 font-bold after:absolute after:bottom-[1px] after:right-0 after:top-0 after:h-full after:border-solid ${
className={`${headerCellBaseClassname} ${
isLastOfDay ? "after:border-l-2" : ""
} ${showTimeSlotHeader ? "pt-1" : ""}`}
>
Expand All @@ -278,33 +306,83 @@ export const LPTimeTableHeader = forwardRef(function TimeTableHeader(
slotsArray,
timeFrameOfDay: timeFrameDay,
viewType,
entries,
})}
</th>
{slotsArray.map((slot, i) => {
const isLastOfDay =
i === slotsArray.length - 1 ||
!slotsArray[i + 1].isSame(slot, "day")
return (
<th
<CustomHeaderRowCell
customHeaderRow={customHeaderRow}
timeSlot={slot}
timeSlotMinutes={timeSlotMinutes}
timeFrameOfDay={timeFrameDay}
slotsArray={slotsArray}
entries={entries}
showTimeSlotHeader={showTimeSlotHeader}
viewType={viewType}
isLastOfDay={isLastOfDay}
key={`timeheader${slot.unix()}`}
colSpan={2}
className={`bg-surface border-transparent border-b-border after:border-border relative select-none border-0 border-b-2 border-solid p-0 pl-1 font-bold after:absolute after:bottom-[1px] after:right-0 after:top-0 after:h-full after:border-solid ${
isLastOfDay ? "after:border-l-2" : ""
} ${showTimeSlotHeader ? "pt-1" : ""}`}
>
{customHeaderRow.timeSlot({
timeSlot: slot,
timeSlotMinutes: 60,
isLastOfDay,
timeFrameOfDay: timeFrameDay,
viewType,
})}
</th>
/>
)
})}
</tr>
)}
</thead>
</>
)
})
}

function CustomHeaderRowCell<
G extends TimeTableGroup,
I extends TimeSlotBooking,
>({
timeSlot,
timeSlotMinutes,
isLastOfDay,
viewType,
timeFrameOfDay,
entries,
slotsArray,
showTimeSlotHeader,
customHeaderRow,
}: {
timeSlot: Dayjs
timeSlotMinutes: number
isLastOfDay: boolean
viewType: TimeTableViewType
timeFrameOfDay: TimeFrameDay
entries: TimeTableEntry<G, I>[]
slotsArray: readonly Dayjs[]
showTimeSlotHeader: boolean
customHeaderRow: {
timeSlot: (props: CustomHeaderRowTimeSlotProps<G, I>) => JSX.Element
header: (props: CustomHeaderRowHeaderProps<G, I>) => JSX.Element
}
}) {
const ref = useRef<HTMLTableCellElement>(null)

return (
<th
key={`timeheader${timeSlot.unix()}`}
colSpan={2}
className={`${headerCellBaseClassname} ${
isLastOfDay ? "after:border-l-2" : ""
} ${showTimeSlotHeader ? "pt-1" : ""}`}
ref={ref}
>
{customHeaderRow.timeSlot({
timeSlot,
timeSlotMinutes,
isLastOfDay,
timeFrameOfDay,
viewType,
entries,
slotsArray,
tableCellRef: ref,
})}
</th>
)
}
21 changes: 19 additions & 2 deletions library/src/components/timetable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ export type { TimeTableItemProps } from "./ItemWrapper"
export type { TimeTablePlaceholderItemProps } from "./PlaceholderItem"
import type { TimeFrameDay as _TimeFrameDay } from "./TimeTableConfigStore"

import {
getLeftAndWidth,
getStartAndEndSlot,
itemsOutsideOfDayRangeORSameStartAndEnd,
} from "./timeTableUtils"
export const timeTableUtils = {
getLeftAndWidth,
getStartAndEndSlot,
itemsOutsideOfDayRangeORSameStartAndEnd,
}

//const memoized = React.memo(TimeTable) as typeof TimeTable

//export { memoized as TimeTable }
Expand All @@ -27,8 +38,14 @@ export namespace TimeTableTypes {
export type TimeSlotBooking = _TimeSlotBooking
export type TimeFrameDay = _TimeFrameDay
export type TimeTableGroup = _TimeTableGroup
export type CustomHeaderRowHeaderProps = _CustomHeaderRowHeaderProps
export type CustomHeadeRowTimeSlotProps = _CustomHeaderRowTimeSlotProps
export type CustomHeaderRowHeaderProps<
G extends TimeTableGroup,
I extends TimeSlotBooking,
> = _CustomHeaderRowHeaderProps<G, I>
export type CustomHeadeRowTimeSlotProps<
G extends TimeTableGroup,
I extends TimeSlotBooking,
> = _CustomHeaderRowTimeSlotProps<G, I>

export type TimeTableEntry<
G extends TimeTableGroup,
Expand Down
Loading