feat(components): add the DatePicker component
This commit is contained in:
152
src/components/calendar/component.rs
Normal file
152
src/components/calendar/component.rs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_primitives::calendar::{
|
||||||
|
self, CalendarDayProps, CalendarGridProps, CalendarHeaderProps, CalendarMonthTitleProps,
|
||||||
|
CalendarNavigationProps, CalendarProps, CalendarSelectMonthProps, CalendarSelectYearProps,
|
||||||
|
RangeCalendarProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Calendar(props: CalendarProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
document::Link { rel: "stylesheet", href: asset!("./style.css") }
|
||||||
|
calendar::Calendar {
|
||||||
|
class: "calendar",
|
||||||
|
selected_date: props.selected_date,
|
||||||
|
on_date_change: props.on_date_change,
|
||||||
|
on_format_weekday: props.on_format_weekday,
|
||||||
|
on_format_month: props.on_format_month,
|
||||||
|
view_date: props.view_date,
|
||||||
|
today: props.today,
|
||||||
|
on_view_change: props.on_view_change,
|
||||||
|
disabled: props.disabled,
|
||||||
|
first_day_of_week: props.first_day_of_week,
|
||||||
|
min_date: props.min_date,
|
||||||
|
max_date: props.max_date,
|
||||||
|
month_count: props.month_count,
|
||||||
|
disabled_ranges: props.disabled_ranges,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn RangeCalendar(props: RangeCalendarProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
document::Link { rel: "stylesheet", href: asset!("./style.css") }
|
||||||
|
calendar::RangeCalendar {
|
||||||
|
class: "calendar",
|
||||||
|
selected_range: props.selected_range,
|
||||||
|
on_range_change: props.on_range_change,
|
||||||
|
on_format_weekday: props.on_format_weekday,
|
||||||
|
on_format_month: props.on_format_month,
|
||||||
|
view_date: props.view_date,
|
||||||
|
today: props.today,
|
||||||
|
on_view_change: props.on_view_change,
|
||||||
|
disabled: props.disabled,
|
||||||
|
first_day_of_week: props.first_day_of_week,
|
||||||
|
min_date: props.min_date,
|
||||||
|
max_date: props.max_date,
|
||||||
|
month_count: props.month_count,
|
||||||
|
disabled_ranges: props.disabled_ranges,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarView(
|
||||||
|
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
|
||||||
|
children: Element,
|
||||||
|
) -> Element {
|
||||||
|
rsx! {
|
||||||
|
div {
|
||||||
|
class: "calendar-view",
|
||||||
|
..attributes,
|
||||||
|
{children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarHeader(props: CalendarHeaderProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarHeader { id: props.id, attributes: props.attributes, {props.children} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarNavigation(props: CalendarNavigationProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarNavigation { attributes: props.attributes, {props.children} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarPreviousMonthButton(
|
||||||
|
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
|
||||||
|
) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarPreviousMonthButton { attributes,
|
||||||
|
svg {
|
||||||
|
class: "calendar-previous-month-icon",
|
||||||
|
view_box: "0 0 24 24",
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
polyline { points: "15 6 9 12 15 18" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarNextMonthButton(
|
||||||
|
#[props(extends = GlobalAttributes)] attributes: Vec<Attribute>,
|
||||||
|
) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarNextMonthButton { attributes,
|
||||||
|
svg {
|
||||||
|
class: "calendar-next-month-icon",
|
||||||
|
view_box: "0 0 24 24",
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
polyline { points: "9 18 15 12 9 6" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarSelectMonth(props: CalendarSelectMonthProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarSelectMonth { class: "calendar-month-select", attributes: props.attributes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarSelectYear(props: CalendarSelectYearProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarSelectYear { class: "calendar-year-select", attributes: props.attributes }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarGrid(props: CalendarGridProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
calendar::CalendarGrid {
|
||||||
|
id: props.id,
|
||||||
|
show_week_numbers: props.show_week_numbers,
|
||||||
|
render_day: props.render_day,
|
||||||
|
attributes: props.attributes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarMonthTitle(props: CalendarMonthTitleProps) -> Element {
|
||||||
|
calendar::CalendarMonthTitle(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn CalendarDay(props: CalendarDayProps) -> Element {
|
||||||
|
calendar::CalendarDay(props)
|
||||||
|
}
|
||||||
2
src/components/calendar/mod.rs
Normal file
2
src/components/calendar/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mod component;
|
||||||
|
pub use component::*;
|
||||||
297
src/components/calendar/style.css
Normal file
297
src/components/calendar/style.css
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
/* Calendar Container */
|
||||||
|
.calendar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
border: 1px solid var(--primary-color-6);
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: var(--primary-color-2);
|
||||||
|
box-shadow: 0 2px 10px rgb(0 0 0 / 10%);
|
||||||
|
font-family:
|
||||||
|
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial,
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar Navigation */
|
||||||
|
.calendar-navigation {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.75rem;
|
||||||
|
padding: 0.75rem calc(0.75rem + 1.75rem + 0.5rem) 0.25rem;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-title {
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-prev,
|
||||||
|
.calendar-nav-next {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1px solid var(--primary-color-6);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--light, transparent)
|
||||||
|
var(--dark, var(--primary-color-3));
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-prev {
|
||||||
|
left: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-next {
|
||||||
|
right: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-prev:hover,
|
||||||
|
.calendar-nav-next:hover {
|
||||||
|
border-color: var(--primary-color-7);
|
||||||
|
background-color: var(--primary-color-4);
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-prev:focus-visible,
|
||||||
|
.calendar-nav-next:focus-visible {
|
||||||
|
box-shadow: 0 0 0 2px var(--focused-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-nav-prev:disabled,
|
||||||
|
.calendar-nav-next:disabled {
|
||||||
|
border-color: var(--primary-color-5);
|
||||||
|
background-color: var(--primary-color-2);
|
||||||
|
color: var(--secondary-color-3);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month-title {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 1.75rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar Grid */
|
||||||
|
.calendar-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-day-header {
|
||||||
|
flex: 1;
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 300;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-body {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell {
|
||||||
|
width: 2rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background: none;
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"]:not([data-disabled="true"]):hover {
|
||||||
|
background-color: var(--primary-color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"]:focus-visible {
|
||||||
|
outline: 2px solid var(--focused-border-color);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="last"],
|
||||||
|
.calendar-grid-cell[data-month="next"],
|
||||||
|
.calendar-grid-cell[data-disabled="true"] {
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="last"][data-selected="true"],
|
||||||
|
.calendar-grid-cell[data-month="next"][data-selected="true"] {
|
||||||
|
background-color: var(--secondary-color-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"][data-selected="true"] {
|
||||||
|
background-color: var(--secondary-color-2);
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"][data-unavailable="true"] {
|
||||||
|
color: var(--secondary-color-6);
|
||||||
|
cursor: not-allowed;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-week td {
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-week td:first-child .calendar-grid-cell {
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
border-top-left-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-week td:last-child .calendar-grid-cell {
|
||||||
|
border-bottom-right-radius: 0.5rem;
|
||||||
|
border-top-right-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="last"][data-selection-between="true"],
|
||||||
|
.calendar-grid-cell[data-month="next"][data-selection-between="true"] {
|
||||||
|
border-radius: 0;
|
||||||
|
background-color: var(--primary-color-5);
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"][data-selection-between="true"] {
|
||||||
|
border-radius: 0;
|
||||||
|
background-color: var(--primary-color-5);
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
td:has(.calendar-grid-cell[data-selection-start="true"]) {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
background-color: var(--primary-color-5);
|
||||||
|
border-bottom-left-radius: 0.5rem;
|
||||||
|
border-top-left-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
td:has(.calendar-grid-cell[data-selection-end="true"]) {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
background-color: var(--primary-color-5);
|
||||||
|
border-bottom-right-radius: 0.5rem;
|
||||||
|
border-top-right-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"][data-selected="true"]:hover {
|
||||||
|
background-color: var(--light, var(--secondary-color-2))
|
||||||
|
var(--dark, var(--primary-color-5));
|
||||||
|
color: var(--light, var(--primary-color))
|
||||||
|
var(--dark, var(--secondary-color-1));
|
||||||
|
font-weight: var(--light, 550) var(--dark, inherit);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-cell[data-month="current"][data-today="true"]:not(
|
||||||
|
[data-selected="true"]
|
||||||
|
) {
|
||||||
|
background-color: var(--primary-color-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-grid-weeknum {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar with week numbers */
|
||||||
|
.calendar-grid-week {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calendar states */
|
||||||
|
.calendar[data-disabled="true"] {
|
||||||
|
opacity: 0.6;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-next-month-icon,
|
||||||
|
.calendar-previous-month-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: none;
|
||||||
|
stroke: currentcolor;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month-select-container,
|
||||||
|
.calendar-year-select-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month-select-container:has(:focus-visible),
|
||||||
|
.calendar-year-select-container:has(:focus-visible) {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
outline: 2px solid var(--focused-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month-select,
|
||||||
|
.calendar-year-select {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0.25rem;
|
||||||
|
margin: 0;
|
||||||
|
inset: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.calendar-month-select-value,
|
||||||
|
.calendar-year-select-value {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.25rem;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition:
|
||||||
|
background-color 0.2s ease,
|
||||||
|
color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-expand-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--secondary-color-4);
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
147
src/components/date_picker/component.rs
Normal file
147
src/components/date_picker/component.rs
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
use dioxus_primitives::{
|
||||||
|
date_picker::{self, DatePickerInputProps, DatePickerProps, DateRangePickerProps},
|
||||||
|
popover::{PopoverContentProps, PopoverTriggerProps},
|
||||||
|
ContentAlign,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::calendar::*;
|
||||||
|
use super::super::popover::*;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DatePicker(props: DatePickerProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
document::Link { rel: "stylesheet", href: asset!("./style.css") }
|
||||||
|
div {
|
||||||
|
date_picker::DatePicker {
|
||||||
|
class: "date-picker",
|
||||||
|
on_value_change: props.on_value_change,
|
||||||
|
selected_date: props.selected_date,
|
||||||
|
disabled: props.disabled,
|
||||||
|
read_only: props.read_only,
|
||||||
|
min_date: props.min_date,
|
||||||
|
max_date: props.max_date,
|
||||||
|
month_count: props.month_count,
|
||||||
|
disabled_ranges: props.disabled_ranges,
|
||||||
|
roving_loop: props.roving_loop,
|
||||||
|
attributes: props.attributes,
|
||||||
|
date_picker::DatePickerPopover {
|
||||||
|
popover_root: PopoverRoot,
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DateRangePicker(props: DateRangePickerProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
document::Link { rel: "stylesheet", href: asset!("./style.css") }
|
||||||
|
div {
|
||||||
|
date_picker::DateRangePicker {
|
||||||
|
class: "date-picker",
|
||||||
|
on_range_change: props.on_range_change,
|
||||||
|
selected_range: props.selected_range,
|
||||||
|
disabled: props.disabled,
|
||||||
|
read_only: props.read_only,
|
||||||
|
min_date: props.min_date,
|
||||||
|
max_date: props.max_date,
|
||||||
|
month_count: props.month_count,
|
||||||
|
disabled_ranges: props.disabled_ranges,
|
||||||
|
roving_loop: props.roving_loop,
|
||||||
|
attributes: props.attributes,
|
||||||
|
date_picker::DatePickerPopover { popover_root: PopoverRoot, {props.children} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DatePickerInput(props: DatePickerInputProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
date_picker::DatePickerInput {
|
||||||
|
on_format_day_placeholder: props.on_format_day_placeholder,
|
||||||
|
on_format_month_placeholder: props.on_format_month_placeholder,
|
||||||
|
on_format_year_placeholder: props.on_format_year_placeholder,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
DatePickerPopoverTrigger {}
|
||||||
|
DatePickerPopoverContent { align: ContentAlign::Center,
|
||||||
|
date_picker::DatePickerCalendar { calendar: Calendar,
|
||||||
|
CalendarView {
|
||||||
|
CalendarHeader {
|
||||||
|
CalendarNavigation {
|
||||||
|
CalendarPreviousMonthButton {}
|
||||||
|
CalendarSelectMonth {}
|
||||||
|
CalendarSelectYear {}
|
||||||
|
CalendarNextMonthButton {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CalendarGrid {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DateRangePickerInput(props: DatePickerInputProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
date_picker::DateRangePickerInput {
|
||||||
|
on_format_day_placeholder: props.on_format_day_placeholder,
|
||||||
|
on_format_month_placeholder: props.on_format_month_placeholder,
|
||||||
|
on_format_year_placeholder: props.on_format_year_placeholder,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
DatePickerPopoverTrigger {}
|
||||||
|
DatePickerPopoverContent {
|
||||||
|
align: ContentAlign::Center,
|
||||||
|
date_picker::DateRangePickerCalendar {
|
||||||
|
calendar: RangeCalendar,
|
||||||
|
CalendarView {
|
||||||
|
CalendarHeader {
|
||||||
|
CalendarNavigation {
|
||||||
|
CalendarPreviousMonthButton {}
|
||||||
|
CalendarSelectMonth {}
|
||||||
|
CalendarSelectYear {}
|
||||||
|
CalendarNextMonthButton {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CalendarGrid {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DatePickerPopoverTrigger(props: PopoverTriggerProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
PopoverTrigger { aria_label: "Show Calendar", attributes: props.attributes,
|
||||||
|
svg {
|
||||||
|
class: "date-picker-expand-icon",
|
||||||
|
view_box: "0 0 24 24",
|
||||||
|
xmlns: "http://www.w3.org/2000/svg",
|
||||||
|
polyline { points: "6 9 12 15 18 9" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DatePickerPopoverContent(props: PopoverContentProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
PopoverContent {
|
||||||
|
class: "popover-content",
|
||||||
|
id: props.id,
|
||||||
|
side: props.side,
|
||||||
|
align: props.align,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/components/date_picker/mod.rs
Normal file
2
src/components/date_picker/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mod component;
|
||||||
|
pub use component::*;
|
||||||
79
src/components/date_picker/style.css
Normal file
79
src/components/date_picker/style.css
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
.date-picker {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-group .popover-trigger {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: rotate 150ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover[data-state="open"] div .date-picker-trigger {
|
||||||
|
rotate: 180deg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-expand-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: none;
|
||||||
|
stroke: var(--primary-color-7);
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-disabled="true"] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-group {
|
||||||
|
display: flex;
|
||||||
|
width: fit-content;
|
||||||
|
min-width: 150px;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: none;
|
||||||
|
background: var(--light, var(--primary-color))
|
||||||
|
var(--dark, var(--primary-color-3));
|
||||||
|
box-shadow: inset 0 0 0 1px var(--light, var(--primary-color-6))
|
||||||
|
var(--dark, var(--primary-color-7));
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
gap: 0.25rem;
|
||||||
|
transition: background-color 100ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-picker-group .popover-content {
|
||||||
|
max-width: unset;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-segment {
|
||||||
|
caret-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-segment[no-date="true"] {
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-segment[is-separator="true"] {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-segment:focus-visible {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background: var(--secondary-color-3);
|
||||||
|
color: var(--primary-color);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
@@ -10,3 +10,7 @@ pub use input::Input;
|
|||||||
|
|
||||||
mod echo;
|
mod echo;
|
||||||
pub use echo::Echo;
|
pub use echo::Echo;
|
||||||
|
|
||||||
|
pub mod calendar;
|
||||||
|
pub mod date_picker;
|
||||||
|
pub mod popover;
|
||||||
|
|||||||
41
src/components/popover/component.rs
Normal file
41
src/components/popover/component.rs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
use dioxus::prelude::*;
|
||||||
|
use dioxus_primitives::popover::{
|
||||||
|
self, PopoverContentProps, PopoverRootProps, PopoverTriggerProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn PopoverRoot(props: PopoverRootProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
document::Link { rel: "stylesheet", href: asset!("./style.css") }
|
||||||
|
popover::PopoverRoot {
|
||||||
|
class: "popover",
|
||||||
|
is_modal: props.is_modal,
|
||||||
|
open: props.open,
|
||||||
|
default_open: props.default_open,
|
||||||
|
on_open_change: props.on_open_change,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn PopoverTrigger(props: PopoverTriggerProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
popover::PopoverTrigger { class: "popover-trigger", attributes: props.attributes, {props.children} }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn PopoverContent(props: PopoverContentProps) -> Element {
|
||||||
|
rsx! {
|
||||||
|
popover::PopoverContent {
|
||||||
|
class: "popover-content",
|
||||||
|
id: props.id,
|
||||||
|
side: props.side,
|
||||||
|
align: props.align,
|
||||||
|
attributes: props.attributes,
|
||||||
|
{props.children}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/components/popover/mod.rs
Normal file
2
src/components/popover/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
mod component;
|
||||||
|
pub use component::*;
|
||||||
228
src/components/popover/style.css
Normal file
228
src/components/popover/style.css
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
.popover {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: calc(100% - 2rem);
|
||||||
|
box-sizing: border-box;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: .25rem;
|
||||||
|
border-radius: .5rem;
|
||||||
|
margin-top: .5rem;
|
||||||
|
background: var(--light, var(--primary-color))
|
||||||
|
var(--dark, var(--primary-color-5));
|
||||||
|
box-shadow: inset 0 0 0 1px var(--light, var(--primary-color-6)) var(--dark, var(--primary-color-7));
|
||||||
|
text-align: center;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
transform-origin: top;
|
||||||
|
will-change: transform, opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-state="closed"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-state="open"] {
|
||||||
|
display: flex;
|
||||||
|
animation: popover-fade-in .2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes popover-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Positioning based on side */
|
||||||
|
.popover-content[data-side="top"] {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="top"]::after {
|
||||||
|
top: calc(100% - 0.25rem);
|
||||||
|
left: 50%;
|
||||||
|
border-color: var(--secondary-color-4);
|
||||||
|
border-radius: 0 0 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="right"] {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 100%;
|
||||||
|
margin-left: 8px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="right"]::after {
|
||||||
|
top: calc(50% - 0.25rem);
|
||||||
|
left: 0;
|
||||||
|
border-color: var(--secondary-color-4);
|
||||||
|
border-radius: 0 0 0 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="bottom"] {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: 8px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="bottom"]::after {
|
||||||
|
bottom: calc(100% - 0.25rem);
|
||||||
|
left: 50%;
|
||||||
|
border-color: var(--secondary-color-4);
|
||||||
|
border-radius: 0.1rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="left"] {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 100%;
|
||||||
|
margin-right: 8px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="left"]::after {
|
||||||
|
top: calc(50% - 0.25rem);
|
||||||
|
right: -0.25rem;
|
||||||
|
border-color: var(--secondary-color-4);
|
||||||
|
border-radius: 0 0.1rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alignment styles for top and bottom */
|
||||||
|
.popover-content[data-side="top"][data-align="start"],
|
||||||
|
.popover-content[data-side="bottom"][data-align="start"] {
|
||||||
|
left: 0;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="top"][data-align="end"],
|
||||||
|
.popover-content[data-side="bottom"][data-align="end"] {
|
||||||
|
right: 0;
|
||||||
|
left: auto;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alignment styles for left and right */
|
||||||
|
.popover-content[data-side="left"][data-align="start"],
|
||||||
|
.popover-content[data-side="right"][data-align="start"] {
|
||||||
|
top: 0;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="left"][data-align="center"],
|
||||||
|
.popover-content[data-side="right"][data-align="center"] {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content[data-side="left"][data-align="end"],
|
||||||
|
.popover-content[data-side="right"][data-align="end"] {
|
||||||
|
top: auto;
|
||||||
|
bottom: 0;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-title {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-description {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--secondary-color-5);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (width >= 40rem) {
|
||||||
|
.popover-content-actions {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content {
|
||||||
|
max-width: 32rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-cancel {
|
||||||
|
padding: 8px 18px;
|
||||||
|
border: 1px solid var(--primary-color-6);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--light, var(--primary-color))
|
||||||
|
var(--dark, var(--primary-color-3));
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-cancel:hover {
|
||||||
|
background-color: var(--primary-color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-cancel:focus-visible {
|
||||||
|
box-shadow: 0 0 0 2px var(--focused-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-action {
|
||||||
|
padding: 8px 18px;
|
||||||
|
border: 1px solid var(--primary-error-color);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--primary-error-color);
|
||||||
|
color: var(--contrast-error-color);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-action:hover {
|
||||||
|
background-color: var(--secondary-error-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-content-action:focus-visible {
|
||||||
|
box-shadow: 0 0 0 2px var(--focused-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-trigger {
|
||||||
|
padding: 8px 18px;
|
||||||
|
border: 1px solid var(--primary-color-6);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: var(--light, var(--primary-color))
|
||||||
|
var(--dark, var(--primary-color-3));
|
||||||
|
color: var(--secondary-color-4);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-trigger:hover {
|
||||||
|
background-color: var(--primary-color-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popover-trigger:focus-visible {
|
||||||
|
box-shadow: 0 0 0 2px var(--focused-border-color);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user