Onboarding Flow (#503)
* wip * wip * db: schedule schema * fix adding time goes into new day fix adding new time not keeping updating ranges fix updating ranges not maintaining changed values * remove photo upload * remove unused code * remove more unused code * undo time lib * didnt actually change this * dont show onboardi ng flow for users created before sept 1 2021 * use more consistent max-widths * align all inputs in scheduler component * allow overriding of default styles * match figma designs implement goto previous step * add more types, match figma Co-authored-by: Bailey Pumfleet <pumfleet@hey.com>
This commit is contained in:
co-authored by
Bailey Pumfleet
parent
f63c9311e5
commit
2d23a8b7db
@@ -0,0 +1,337 @@
|
||||
import React from "react";
|
||||
import Text from "@components/ui/Text";
|
||||
import { PlusIcon, TrashIcon } from "@heroicons/react/outline";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import classnames from "classnames";
|
||||
export const SCHEDULE_FORM_ID = "SCHEDULE_FORM_ID";
|
||||
export const toCalendsoAvailabilityFormat = (schedule: Schedule) => {
|
||||
return schedule;
|
||||
};
|
||||
|
||||
export const AM_PM_TIME_FORMAT = `h:mm:ss a`;
|
||||
export const _24_HOUR_TIME_FORMAT = `HH:mm:ss`;
|
||||
|
||||
const DEFAULT_START_TIME = "09:00:00";
|
||||
const DEFAULT_END_TIME = "17:00:00";
|
||||
|
||||
/** Begin Time Increments For Select */
|
||||
const increment = 15;
|
||||
|
||||
/**
|
||||
* Creates an array of times on a 15 minute interval from
|
||||
* 00:00:00 (Start of day) to
|
||||
* 23:45:00 (End of day with enough time for 15 min booking)
|
||||
*/
|
||||
const TIMES = (() => {
|
||||
const starting_time = dayjs().startOf("day");
|
||||
const ending_time = dayjs().endOf("day");
|
||||
|
||||
const times = [];
|
||||
let t: Dayjs = starting_time;
|
||||
|
||||
while (t.isBefore(ending_time)) {
|
||||
times.push(t.format(_24_HOUR_TIME_FORMAT));
|
||||
t = t.add(increment, "minutes");
|
||||
}
|
||||
return times;
|
||||
})();
|
||||
/** End Time Increments For Select */
|
||||
|
||||
const DEFAULT_SCHEDULE: Schedule = {
|
||||
monday: [{ start: "09:00:00", end: "17:00:00" }],
|
||||
tuesday: [{ start: "09:00:00", end: "17:00:00" }],
|
||||
wednesday: [{ start: "09:00:00", end: "17:00:00" }],
|
||||
thursday: [{ start: "09:00:00", end: "17:00:00" }],
|
||||
friday: [{ start: "09:00:00", end: "17:00:00" }],
|
||||
saturday: null,
|
||||
sunday: null,
|
||||
};
|
||||
|
||||
type DayOfWeek = "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday";
|
||||
export type TimeRange = {
|
||||
start: string;
|
||||
end: string;
|
||||
};
|
||||
|
||||
export type FreeBusyTime = TimeRange[];
|
||||
|
||||
export type Schedule = {
|
||||
monday?: FreeBusyTime | null;
|
||||
tuesday?: FreeBusyTime | null;
|
||||
wednesday?: FreeBusyTime | null;
|
||||
thursday?: FreeBusyTime | null;
|
||||
friday?: FreeBusyTime | null;
|
||||
saturday?: FreeBusyTime | null;
|
||||
sunday?: FreeBusyTime | null;
|
||||
};
|
||||
|
||||
type ScheduleBlockProps = {
|
||||
day: DayOfWeek;
|
||||
ranges?: FreeBusyTime | null;
|
||||
selected?: boolean;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
schedule?: Schedule;
|
||||
onChange?: (data: Schedule) => void;
|
||||
onSubmit: (data: Schedule) => void;
|
||||
};
|
||||
|
||||
const SchedulerForm = ({ schedule = DEFAULT_SCHEDULE, onSubmit }: Props) => {
|
||||
const ref = React.useRef<HTMLFormElement>(null);
|
||||
|
||||
const transformElementsToSchedule = (elements: HTMLFormControlsCollection): Schedule => {
|
||||
const schedule: Schedule = {};
|
||||
const formElements = Array.from(elements)
|
||||
.map((element) => {
|
||||
return element.id;
|
||||
})
|
||||
.filter((value) => value);
|
||||
|
||||
/**
|
||||
* elementId either {day} or {day.N.start} or {day.N.end}
|
||||
* If elementId in DAYS_ARRAY add elementId to scheduleObj
|
||||
* then element is the checkbox and can be ignored
|
||||
*
|
||||
* If elementId starts with a day in DAYS_ARRAY
|
||||
* the elementId should be split by "." resulting in array length 3
|
||||
* [day, rangeIndex, "start" | "end"]
|
||||
*/
|
||||
formElements.forEach((elementId) => {
|
||||
const [day, rangeIndex, rangeId] = elementId.split(".");
|
||||
if (rangeIndex && rangeId) {
|
||||
if (!schedule[day]) {
|
||||
schedule[day] = [];
|
||||
}
|
||||
|
||||
if (!schedule[day][parseInt(rangeIndex)]) {
|
||||
schedule[day][parseInt(rangeIndex)] = {};
|
||||
}
|
||||
|
||||
schedule[day][parseInt(rangeIndex)][rangeId] = elements[elementId].value;
|
||||
}
|
||||
});
|
||||
|
||||
return schedule;
|
||||
};
|
||||
|
||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
const elements = ref.current?.elements;
|
||||
if (elements) {
|
||||
const schedule = transformElementsToSchedule(elements);
|
||||
onSubmit && typeof onSubmit === "function" && onSubmit(schedule);
|
||||
}
|
||||
};
|
||||
|
||||
const ScheduleBlock = ({ day, ranges: defaultRanges, selected: defaultSelected }: ScheduleBlockProps) => {
|
||||
const [ranges, setRanges] = React.useState(defaultRanges);
|
||||
const [selected, setSelected] = React.useState(defaultSelected);
|
||||
React.useEffect(() => {
|
||||
if (!ranges || ranges.length === 0) {
|
||||
setSelected(false);
|
||||
} else {
|
||||
setSelected(true);
|
||||
}
|
||||
}, [ranges]);
|
||||
|
||||
const handleSelectedChange = () => {
|
||||
if (!selected && (!ranges || ranges.length === 0)) {
|
||||
setRanges([
|
||||
{
|
||||
start: "09:00:00",
|
||||
end: "17:00:00",
|
||||
},
|
||||
]);
|
||||
}
|
||||
setSelected(!selected);
|
||||
};
|
||||
|
||||
const handleAddRange = () => {
|
||||
let rangeToAdd;
|
||||
if (!ranges || ranges?.length === 0) {
|
||||
rangeToAdd = {
|
||||
start: DEFAULT_START_TIME,
|
||||
end: DEFAULT_END_TIME,
|
||||
};
|
||||
setRanges([rangeToAdd]);
|
||||
} else {
|
||||
const lastRange = ranges[ranges.length - 1];
|
||||
|
||||
const [hour, minute, second] = lastRange.end.split(":");
|
||||
const date = dayjs()
|
||||
.set("hour", parseInt(hour))
|
||||
.set("minute", parseInt(minute))
|
||||
.set("second", parseInt(second));
|
||||
const nextStartTime = date.add(1, "hour");
|
||||
const nextEndTime = date.add(2, "hour");
|
||||
|
||||
/**
|
||||
* If next range goes over into "tomorrow"
|
||||
* i.e. time greater that last value in Times
|
||||
* return
|
||||
*/
|
||||
if (nextStartTime.isAfter(date.endOf("day"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
rangeToAdd = {
|
||||
start: nextStartTime.format(_24_HOUR_TIME_FORMAT),
|
||||
end: nextEndTime.format(_24_HOUR_TIME_FORMAT),
|
||||
};
|
||||
setRanges([...ranges, rangeToAdd]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteRange = (range: TimeRange) => {
|
||||
if (ranges && ranges.length > 0) {
|
||||
setRanges(
|
||||
ranges.filter((r: TimeRange) => {
|
||||
return r.start != range.start;
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Should update ranges values
|
||||
*/
|
||||
const handleSelectRangeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const [day, rangeIndex, rangeId] = event.currentTarget.name.split(".");
|
||||
|
||||
if (day && ranges) {
|
||||
const newRanges = ranges.map((range, index) => {
|
||||
const newRange = {
|
||||
...range,
|
||||
[rangeId]: event.currentTarget.value,
|
||||
};
|
||||
return index === parseInt(rangeIndex) ? newRange : range;
|
||||
});
|
||||
|
||||
setRanges(newRanges);
|
||||
}
|
||||
};
|
||||
|
||||
const TimeRangeField = ({ range, day, index }: { range: TimeRange; day: DayOfWeek; index: number }) => {
|
||||
return (
|
||||
<div key={`${day}-range-${index}`} className="flex items-center justify-between space-x-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<select
|
||||
id={`${day}.${index}.start`}
|
||||
name={`${day}.${index}.start`}
|
||||
defaultValue={range?.start || DEFAULT_START_TIME}
|
||||
onChange={handleSelectRangeChange}
|
||||
className="block px-4 pr-8 py-2 text-base border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-sm">
|
||||
{TIMES.map((time) => {
|
||||
return (
|
||||
<option key={`${day}.${index}.start.${time}`} value={time}>
|
||||
{time}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
<Text>-</Text>
|
||||
<select
|
||||
id={`${day}.${index}.end`}
|
||||
name={`${day}.${index}.end`}
|
||||
defaultValue={range?.end || DEFAULT_END_TIME}
|
||||
onChange={handleSelectRangeChange}
|
||||
className=" block px-4 pr-8 py-2 text-base border-gray-300 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-sm">
|
||||
{TIMES.map((time) => {
|
||||
return (
|
||||
<option key={`${day}.${index}.end.${time}`} value={time}>
|
||||
{time}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
<div className="">
|
||||
<DeleteAction range={range} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Actions = () => {
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
<button type="button" onClick={() => handleAddRange()}>
|
||||
<PlusIcon className="h-5 w-5 text-neutral-400 hover:text-neutral-500" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const DeleteAction = ({ range }: { range: TimeRange }) => {
|
||||
return (
|
||||
<button type="button" onClick={() => handleDeleteRange(range)}>
|
||||
<TrashIcon className="h-5 w-5 text-neutral-400 hover:text-neutral-500" />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<fieldset className=" py-6">
|
||||
<section
|
||||
className={classnames(
|
||||
"flex flex-col space-y-6 sm:space-y-0 sm:flex-row sm:justify-between",
|
||||
ranges && ranges?.length > 1 ? "sm:items-start" : "sm:items-center"
|
||||
)}>
|
||||
<div style={{ minWidth: "33%" }} className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-2 ">
|
||||
<input
|
||||
id={day}
|
||||
name={day}
|
||||
checked={selected}
|
||||
onChange={handleSelectedChange}
|
||||
type="checkbox"
|
||||
className="focus:ring-neutral-500 h-4 w-4 text-neutral-900 border-gray-300 rounded-sm"
|
||||
/>
|
||||
<Text variant="overline">{day}</Text>
|
||||
</div>
|
||||
<div className="sm:hidden justify-self-end self-end">
|
||||
<Actions />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 w-full">
|
||||
{selected && ranges && ranges.length != 0 ? (
|
||||
ranges.map((range, index) => (
|
||||
<TimeRangeField key={`${day}-range-${index}`} range={range} index={index} day={day} />
|
||||
))
|
||||
) : (
|
||||
<Text key={`${day}`} variant="caption">
|
||||
Unavailable
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="hidden sm:block px-2">
|
||||
<Actions />
|
||||
</div>
|
||||
</section>
|
||||
</fieldset>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form id={SCHEDULE_FORM_ID} onSubmit={handleSubmit} ref={ref} className="divide-y divide-gray-200">
|
||||
{Object.keys(schedule).map((day) => {
|
||||
const selected = schedule[day as DayOfWeek] != null;
|
||||
return (
|
||||
<ScheduleBlock
|
||||
key={`${day}`}
|
||||
day={day as DayOfWeek}
|
||||
ranges={schedule[day as DayOfWeek]}
|
||||
selected={selected}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SchedulerForm;
|
||||
@@ -15,6 +15,7 @@ type Props = {
|
||||
timeZone: string;
|
||||
availability: Availability[];
|
||||
setTimeZone: unknown;
|
||||
setAvailability: unknown;
|
||||
};
|
||||
|
||||
export const Scheduler = ({
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Body: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-lg leading-relaxed text-gray-900 dark:text-white");
|
||||
const classes = classnames("text-lg leading-relaxed text-gray-900 dark:text-white", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Body;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Caption: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-sm text-gray-500 dark:text-white leading-tight");
|
||||
const classes = classnames("text-sm text-gray-500 dark:text-white leading-tight", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Caption;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Caption2: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-xs italic text-gray-500 dark:text-white leading-tight");
|
||||
const classes = classnames("text-xs italic text-gray-500 dark:text-white leading-tight", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Caption2;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Footnote: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-base font-normal text-gray-900 dark:text-white");
|
||||
const classes = classnames("text-xs font-medium text-gray-500 dark:text-white", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Footnote;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Headline: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-xl font-bold text-gray-900 dark:text-white");
|
||||
const classes = classnames("text-xl font-bold text-gray-900 dark:text-white", props?.className);
|
||||
|
||||
return <h1 className={classes}>{props.children}</h1>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Headline;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Largetitle: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-2xl font-normal text-gray-900 dark:text-white");
|
||||
const classes = classnames("text-3xl font-extrabold text-gray-900 dark:text-white", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Largetitle;
|
||||
|
||||
@@ -4,10 +4,11 @@ import { TextProps } from "../Text";
|
||||
|
||||
const Overline: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames(
|
||||
"text-sm uppercase font-semibold leading-snug tracking-wide text-gray-900 dark:text-white"
|
||||
"text-sm uppercase font-semibold leading-snug tracking-wide text-gray-900 dark:text-white",
|
||||
props?.className
|
||||
);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Overline;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Subheadline: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-xl text-gray-500 dark:text-white leading-relaxed");
|
||||
const classes = classnames("text-xl text-gray-500 dark:text-white leading-relaxed", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Subheadline;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Subtitle: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("ext-sm text-neutral-500 dark:text-white");
|
||||
const classes = classnames("text-sm font-normal text-neutral-500 dark:text-white", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Subtitle;
|
||||
|
||||
+13
-19
@@ -12,8 +12,6 @@ import Title from "./Title";
|
||||
import Title2 from "./Title2";
|
||||
import Title3 from "./Title3";
|
||||
|
||||
import classnames from "classnames";
|
||||
|
||||
type Props = {
|
||||
variant?:
|
||||
| "overline"
|
||||
@@ -32,14 +30,12 @@ type Props = {
|
||||
text?: string;
|
||||
tx?: string;
|
||||
className?: string;
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export type TextProps = {
|
||||
children: any;
|
||||
text?: string;
|
||||
tx?: string;
|
||||
color?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
@@ -79,84 +75,82 @@ export type TextProps = {
|
||||
*/
|
||||
|
||||
const Text: React.FunctionComponent<Props> = (props: Props) => {
|
||||
const classes = classnames(props?.className, props?.color);
|
||||
|
||||
switch (props?.variant) {
|
||||
case "overline":
|
||||
return (
|
||||
<Overline className={classes} {...props}>
|
||||
<Overline text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Overline>
|
||||
);
|
||||
case "body":
|
||||
return (
|
||||
<Body className={classes} {...props}>
|
||||
<Body text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Body>
|
||||
);
|
||||
case "caption":
|
||||
return (
|
||||
<Caption className={classes} {...props}>
|
||||
<Caption text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Caption>
|
||||
);
|
||||
case "caption2":
|
||||
return (
|
||||
<Caption2 className={classes} {...props}>
|
||||
<Caption2 text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Caption2>
|
||||
);
|
||||
case "footnote":
|
||||
return (
|
||||
<Footnote className={classes} {...props}>
|
||||
<Footnote text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Footnote>
|
||||
);
|
||||
case "headline":
|
||||
return (
|
||||
<Headline className={classes} {...props}>
|
||||
<Headline text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Headline>
|
||||
);
|
||||
case "largetitle":
|
||||
return (
|
||||
<Largetitle className={classes} {...props}>
|
||||
<Largetitle text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Largetitle>
|
||||
);
|
||||
case "subheadline":
|
||||
return (
|
||||
<Subheadline className={classes} {...props}>
|
||||
<Subheadline text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Subheadline>
|
||||
);
|
||||
case "subtitle":
|
||||
return (
|
||||
<Subtitle className={classes} {...props}>
|
||||
<Subtitle text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Subtitle>
|
||||
);
|
||||
case "title":
|
||||
return (
|
||||
<Title className={classes} {...props}>
|
||||
<Title text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Title>
|
||||
);
|
||||
case "title2":
|
||||
return (
|
||||
<Title2 className={classes} {...props}>
|
||||
<Title2 text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Title2>
|
||||
);
|
||||
case "title3":
|
||||
return (
|
||||
<Title3 className={classes} {...props}>
|
||||
<Title3 text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Title3>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Body className={classes} {...props}>
|
||||
<Body text={props?.text} tx={props?.tx} className={props?.className}>
|
||||
{props.children}
|
||||
</Body>
|
||||
);
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Title: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("font-medium text-neutral-900 dark:text-white");
|
||||
const classes = classnames("font-medium text-neutral-900 dark:text-white", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Title;
|
||||
|
||||
@@ -3,9 +3,9 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Title2: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-base font-normal text-gray-900 dark:text-white");
|
||||
const classes = classnames("text-base font-normal text-gray-900 dark:text-white", props?.className);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Title2;
|
||||
|
||||
@@ -3,9 +3,12 @@ import classnames from "classnames";
|
||||
import { TextProps } from "../Text";
|
||||
|
||||
const Title3: React.FunctionComponent<TextProps> = (props: TextProps) => {
|
||||
const classes = classnames("text-xs font-semibold leading-tight text-gray-900 dark:text-white");
|
||||
const classes = classnames(
|
||||
"text-xs font-semibold leading-tight text-gray-900 dark:text-white",
|
||||
props?.className
|
||||
);
|
||||
|
||||
return <p className={classes}>{props.children}</p>;
|
||||
return <p className={classes}>{props?.text || props.children}</p>;
|
||||
};
|
||||
|
||||
export default Title3;
|
||||
|
||||
Reference in New Issue
Block a user