@purpurds/calendar 0.0.1
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.
- package/dist/LICENSE.txt +105 -0
- package/dist/calendar.cjs.js +14 -0
- package/dist/calendar.cjs.js.map +1 -0
- package/dist/calendar.d.ts +29 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.es.js +37514 -0
- package/dist/calendar.es.js.map +1 -0
- package/dist/metadata.js +7 -0
- package/dist/styles.css +1 -0
- package/eslint.config.mjs +2 -0
- package/package.json +73 -0
- package/src/calendar.module.scss +195 -0
- package/src/calendar.stories.tsx +101 -0
- package/src/calendar.test.tsx +89 -0
- package/src/calendar.tsx +112 -0
- package/src/global.d.ts +4 -0
package/dist/metadata.js
ADDED
package/dist/styles.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.rdp-root{--rdp-accent-color: blue;--rdp-accent-background-color: #f0f0ff;--rdp-day-height: 44px;--rdp-day-width: 44px;--rdp-day_button-border-radius: 100%;--rdp-day_button-border: 2px solid transparent;--rdp-day_button-height: 42px;--rdp-day_button-width: 42px;--rdp-selected-border: 2px solid var(--rdp-accent-color);--rdp-disabled-opacity: .5;--rdp-outside-opacity: .75;--rdp-today-color: var(--rdp-accent-color);--rdp-dropdown-gap: .5rem;--rdp-months-gap: 2rem;--rdp-nav_button-disabled-opacity: .5;--rdp-nav_button-height: 2.25rem;--rdp-nav_button-width: 2.25rem;--rdp-nav-height: 2.75rem;--rdp-range_middle-background-color: var(--rdp-accent-background-color);--rdp-range_middle-color: inherit;--rdp-range_start-color: white;--rdp-range_start-background: linear-gradient(var(--rdp-gradient-direction), transparent 50%, var(--rdp-range_middle-background-color) 50%);--rdp-range_start-date-background-color: var(--rdp-accent-color);--rdp-range_end-background: linear-gradient(var(--rdp-gradient-direction), var(--rdp-range_middle-background-color) 50%, transparent 50%);--rdp-range_end-color: white;--rdp-range_end-date-background-color: var(--rdp-accent-color);--rdp-week_number-border-radius: 100%;--rdp-week_number-border: 2px solid transparent;--rdp-week_number-height: var(--rdp-day-height);--rdp-week_number-opacity: .75;--rdp-week_number-width: var(--rdp-day-width);--rdp-weeknumber-text-align: center;--rdp-weekday-opacity: .75;--rdp-weekday-padding: .5rem 0rem;--rdp-weekday-text-align: center;--rdp-gradient-direction: 90deg;--rdp-animation_duration: .3s;--rdp-animation_timing: cubic-bezier(.4, 0, .2, 1)}.rdp-root[dir=rtl]{--rdp-gradient-direction: -90deg}.rdp-root[data-broadcast-calendar=true]{--rdp-outside-opacity: unset}.rdp-root{position:relative;box-sizing:border-box}.rdp-root *{box-sizing:border-box}.rdp-day{width:var(--rdp-day-width);height:var(--rdp-day-height);text-align:center}.rdp-day_button{background:none;padding:0;margin:0;cursor:pointer;font:inherit;color:inherit;justify-content:center;align-items:center;display:flex;width:var(--rdp-day_button-width);height:var(--rdp-day_button-height);border:var(--rdp-day_button-border);border-radius:var(--rdp-day_button-border-radius)}.rdp-day_button:disabled{cursor:revert}.rdp-caption_label{z-index:1;position:relative;display:inline-flex;align-items:center;white-space:nowrap;border:0}.rdp-dropdown:focus-visible~.rdp-caption_label{outline:5px auto Highlight;outline:5px auto -webkit-focus-ring-color}.rdp-button_next,.rdp-button_previous{border:none;background:none;padding:0;margin:0;cursor:pointer;font:inherit;color:inherit;-moz-appearance:none;-webkit-appearance:none;display:inline-flex;align-items:center;justify-content:center;position:relative;appearance:none;width:var(--rdp-nav_button-width);height:var(--rdp-nav_button-height)}.rdp-button_next:disabled,.rdp-button_next[aria-disabled=true],.rdp-button_previous:disabled,.rdp-button_previous[aria-disabled=true]{cursor:revert;opacity:var(--rdp-nav_button-disabled-opacity)}.rdp-chevron{display:inline-block;fill:var(--rdp-accent-color)}.rdp-root[dir=rtl] .rdp-nav .rdp-chevron{transform:rotate(180deg);transform-origin:50%}.rdp-dropdowns{position:relative;display:inline-flex;align-items:center;gap:var(--rdp-dropdown-gap)}.rdp-dropdown{z-index:2;opacity:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;position:absolute;inset-block-start:0;inset-block-end:0;inset-inline-start:0;width:100%;margin:0;padding:0;cursor:inherit;border:none;line-height:inherit}.rdp-dropdown_root{position:relative;display:inline-flex;align-items:center}.rdp-dropdown_root[data-disabled=true] .rdp-chevron{opacity:var(--rdp-disabled-opacity)}.rdp-month_caption{display:flex;align-content:center;height:var(--rdp-nav-height);font-weight:700;font-size:large}.rdp-root[data-nav-layout=around] .rdp-month,.rdp-root[data-nav-layout=after] .rdp-month{position:relative}.rdp-root[data-nav-layout=around] .rdp-month_caption{justify-content:center;margin-inline-start:var(--rdp-nav_button-width);margin-inline-end:var(--rdp-nav_button-width);position:relative}.rdp-root[data-nav-layout=around] .rdp-button_previous{position:absolute;inset-inline-start:0;top:0;height:var(--rdp-nav-height);display:inline-flex}.rdp-root[data-nav-layout=around] .rdp-button_next{position:absolute;inset-inline-end:0;top:0;height:var(--rdp-nav-height);display:inline-flex;justify-content:center}.rdp-months{position:relative;display:flex;flex-wrap:wrap;gap:var(--rdp-months-gap);max-width:fit-content}.rdp-month_grid{border-collapse:collapse}.rdp-nav{position:absolute;inset-block-start:0;inset-inline-end:0;display:flex;align-items:center;height:var(--rdp-nav-height)}.rdp-weekday{opacity:var(--rdp-weekday-opacity);padding:var(--rdp-weekday-padding);font-weight:500;font-size:smaller;text-align:var(--rdp-weekday-text-align);text-transform:var(--rdp-weekday-text-transform)}.rdp-week_number{opacity:var(--rdp-week_number-opacity);font-weight:400;font-size:small;height:var(--rdp-week_number-height);width:var(--rdp-week_number-width);border:var(--rdp-week_number-border);border-radius:var(--rdp-week_number-border-radius);text-align:var(--rdp-weeknumber-text-align)}.rdp-today:not(.rdp-outside){color:var(--rdp-today-color)}.rdp-selected{font-weight:700;font-size:large}.rdp-selected .rdp-day_button{border:var(--rdp-selected-border)}.rdp-outside{opacity:var(--rdp-outside-opacity)}.rdp-disabled{opacity:var(--rdp-disabled-opacity)}.rdp-hidden{visibility:hidden;color:var(--rdp-range_start-color)}.rdp-range_start{background:var(--rdp-range_start-background)}.rdp-range_start .rdp-day_button{background-color:var(--rdp-range_start-date-background-color);color:var(--rdp-range_start-color)}.rdp-range_middle{background-color:var(--rdp-range_middle-background-color)}.rdp-range_middle .rdp-day_button{border-color:transparent;border:unset;border-radius:unset;color:var(--rdp-range_middle-color)}.rdp-range_end{background:var(--rdp-range_end-background);color:var(--rdp-range_end-color)}.rdp-range_end .rdp-day_button{color:var(--rdp-range_start-color);background-color:var(--rdp-range_end-date-background-color)}.rdp-range_start.rdp-range_end{background:revert}.rdp-focusable{cursor:pointer}@keyframes rdp-slide_in_left{0%{transform:translate(-100%)}to{transform:translate(0)}}@keyframes rdp-slide_in_right{0%{transform:translate(100%)}to{transform:translate(0)}}@keyframes rdp-slide_out_left{0%{transform:translate(0)}to{transform:translate(-100%)}}@keyframes rdp-slide_out_right{0%{transform:translate(0)}to{transform:translate(100%)}}.rdp-weeks_before_enter{animation:rdp-slide_in_left var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-weeks_before_exit{animation:rdp-slide_out_left var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-weeks_after_enter{animation:rdp-slide_in_right var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-weeks_after_exit{animation:rdp-slide_out_right var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-root[dir=rtl] .rdp-weeks_after_enter{animation:rdp-slide_in_left var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-root[dir=rtl] .rdp-weeks_before_exit{animation:rdp-slide_out_right var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-root[dir=rtl] .rdp-weeks_before_enter{animation:rdp-slide_in_right var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-root[dir=rtl] .rdp-weeks_after_exit{animation:rdp-slide_out_left var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}@keyframes rdp-fade_in{0%{opacity:0}to{opacity:1}}@keyframes rdp-fade_out{0%{opacity:1}to{opacity:0}}.rdp-caption_after_enter{animation:rdp-fade_in var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-caption_after_exit{animation:rdp-fade_out var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-caption_before_enter{animation:rdp-fade_in var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}.rdp-caption_before_exit{animation:rdp-fade_out var(--rdp-animation_duration) var(--rdp-animation_timing) forwards}._purpur-button_w455q_1{align-items:center;border-radius:var(--purpur-border-radius-full);border-width:0;cursor:pointer;display:inline-flex;font-size:var(--purpur-typography-scale-100);font-family:var(--purpur-typography-family-default);font-weight:var(--purpur-typography-weight-medium);gap:var(--purpur-spacing-100);justify-content:center;line-height:var(--purpur-spacing-300);outline:0;position:relative;text-decoration:none;transition:all var(--purpur-motion-duration-100) ease;width:auto}._purpur-button_w455q_1:focus:before{border-radius:var(--purpur-border-radius-full);box-shadow:0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus);content:"";display:block;inset:calc(var(--purpur-spacing-25) * -1);position:absolute}._purpur-button_w455q_1:focus:not(:focus-visible):before{box-shadow:none}._purpur-button_w455q_1 svg{display:block}._purpur-button--xs_w455q_33{padding:var(--purpur-spacing-50) var(--purpur-spacing-250)}._purpur-button--xs_w455q_33._purpur-button--icon-only_w455q_36{padding:var(--purpur-spacing-100)}._purpur-button--sm_w455q_39{padding:var(--purpur-spacing-100) var(--purpur-spacing-250)}._purpur-button--sm_w455q_39._purpur-button--icon-only_w455q_36{padding:var(--purpur-spacing-100)}._purpur-button--md_w455q_45{padding:var(--purpur-spacing-150) var(--purpur-spacing-300)}._purpur-button--md_w455q_45._purpur-button--icon-only_w455q_36{padding:var(--purpur-spacing-150)}._purpur-button--lg_w455q_51{padding:var(--purpur-spacing-200) var(--purpur-spacing-400)}._purpur-button--lg_w455q_51._purpur-button--icon-only_w455q_36{padding:var(--purpur-spacing-200)}._purpur-button--full-width_w455q_57{width:100%}._purpur-button--primary_w455q_60{background-color:var(--purpur-color-background-interactive-primary);color:var(--purpur-color-text-interactive-on-primary)}._purpur-button--primary_w455q_60:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-primary-hover)}._purpur-button--primary_w455q_60:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-primary-active)}._purpur-button--primary-negative_w455q_70{background-color:var(--purpur-color-background-interactive-primary-negative);color:var(--purpur-color-text-interactive-on-primary-negative)}._purpur-button--primary-negative_w455q_70:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-primary-negative-hover);border-color:var(--purpur-color-background-interactive-primary-negative-hover)}._purpur-button--primary-negative_w455q_70:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-primary-negative-active);border-color:var(--purpur-color-background-interactive-primary-negative-active)}._purpur-button--secondary_w455q_82{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:inset 0 0 0 var(--purpur-border-width-xs) var(--purpur-color-border-interactive-primary);color:var(--purpur-color-text-interactive-primary)}._purpur-button--secondary_w455q_82:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-button--secondary_w455q_82:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-button--secondary-negative_w455q_93{background-color:var(--purpur-color-background-interactive-transparent);box-shadow:inset 0 0 0 var(--purpur-border-width-xs) var(--purpur-color-border-interactive-primary-negative);color:var(--purpur-color-text-interactive-primary-negative)}._purpur-button--secondary-negative_w455q_93:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-negative-hover)}._purpur-button--secondary-negative_w455q_93:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-negative-active)}._purpur-button--expressive_w455q_104{background-color:var(--purpur-color-background-interactive-expressive);color:var(--purpur-color-text-interactive-on-expressive)}._purpur-button--expressive_w455q_104:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-expressive-hover)}._purpur-button--expressive_w455q_104:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-expressive-active)}._purpur-button--expressive-negative_w455q_114{background-color:var(--purpur-color-background-interactive-expressive-negative);color:var(--purpur-color-text-interactive-on-expressive-negative)}._purpur-button--expressive-negative_w455q_114:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-expressive-negative-hover);color:var(--purpur-color-text-interactive-on-expressive-negative-hover)}._purpur-button--expressive-negative_w455q_114:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-expressive-negative-active);color:var(--purpur-color-text-interactive-on-expressive-negative-active)}._purpur-button--negative_w455q_126._purpur-button--disabled_w455q_64{background-color:var(--purpur-color-background-interactive-disabled-negative);color:var(--purpur-color-text-weak-negative)}._purpur-button--secondary_w455q_82._purpur-button--disabled_w455q_64,._purpur-button--secondary-negative_w455q_93._purpur-button--disabled_w455q_64{box-shadow:none}._purpur-button--destructive_w455q_133{background-color:var(--purpur-color-background-interactive-destructive);color:var(--purpur-color-text-interactive-on-destructive)}._purpur-button--destructive_w455q_133:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-destructive-hover)}._purpur-button--destructive_w455q_133:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-destructive-active)}._purpur-button--destructive-negative_w455q_143{background-color:var(--purpur-color-background-interactive-destructive);color:var(--purpur-color-text-interactive-on-destructive)}._purpur-button--destructive-negative_w455q_143:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-destructive-hover)}._purpur-button--destructive-negative_w455q_143:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-destructive-active)}._purpur-button--tertiary-purple_w455q_153{background-color:var(--purpur-color-background-interactive-transparent);color:var(--purpur-color-text-interactive-primary)}._purpur-button--tertiary-purple_w455q_153._purpur-button--disabled_w455q_64{background-color:var(--purpur-color-background-interactive-transparent)}._purpur-button--tertiary-purple_w455q_153:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-button--tertiary-purple_w455q_153:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-button--tertiary-purple-negative_w455q_166{background-color:var(--purpur-color-background-interactive-transparent);color:var(--purpur-color-text-interactive-primary-negative)}._purpur-button--tertiary-purple-negative_w455q_166._purpur-button--disabled_w455q_64{background-color:var(--purpur-color-background-interactive-transparent)}._purpur-button--tertiary-purple-negative_w455q_166:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-negative-hover);color:var(--purpur-color-text-interactive-primary-negative-hover)}._purpur-button--tertiary-purple-negative_w455q_166:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-negative-active);color:var(--purpur-color-text-interactive-primary-negative-active)}._purpur-button--text-negative_w455q_181{border-radius:0;padding-left:0;padding-right:0}._purpur-button--text-negative_w455q_181:focus-visible:before{border-radius:0}._purpur-button--text-negative_w455q_181{background-color:var(--purpur-color-background-interactive-transparent);color:var(--purpur-color-text-interactive-primary-negative)}._purpur-button--text-negative_w455q_181._purpur-button--disabled_w455q_64{background-color:var(--purpur-color-background-interactive-transparent)}._purpur-button--text-negative_w455q_181:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-negative-hover);color:var(--purpur-color-text-interactive-primary-negative-hover)}._purpur-button--text-negative_w455q_181:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-negative-active);color:var(--purpur-color-text-interactive-primary-negative-active)}._purpur-button--text_w455q_181{border-radius:0;padding-left:0;padding-right:0}._purpur-button--text_w455q_181:focus-visible:before{border-radius:0}._purpur-button--text_w455q_181{background-color:var(--purpur-color-background-interactive-transparent);color:var(--purpur-color-text-interactive-primary)}._purpur-button--text_w455q_181._purpur-button--disabled_w455q_64{background-color:var(--purpur-color-background-interactive-transparent);color:var(--purpur-color-text-weak)}._purpur-button--text_w455q_181:hover:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-button--text_w455q_181:active:not(._purpur-button--disabled_w455q_64){background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-button--disabled_w455q_64{background-color:var(--purpur-color-background-interactive-disabled);color:var(--purpur-color-text-weak);cursor:not-allowed}._purpur-icon_8u1lq_1{display:inline-block}._purpur-icon--xxs_8u1lq_4{width:var(--purpur-spacing-150);height:var(--purpur-spacing-150)}._purpur-icon--xs_8u1lq_8{width:var(--purpur-spacing-200);height:var(--purpur-spacing-200)}._purpur-icon--sm_8u1lq_12{width:var(--purpur-spacing-250);height:var(--purpur-spacing-250)}._purpur-icon--md_8u1lq_16{width:var(--purpur-spacing-300);height:var(--purpur-spacing-300)}._purpur-icon--lg_8u1lq_20{width:var(--purpur-spacing-400);height:var(--purpur-spacing-400)}._purpur-icon--xl_8u1lq_24{width:var(--purpur-spacing-600);height:var(--purpur-spacing-600)}._root_11ee2_3{--rdp-accent-color: var(--purpur-color-text-interactive-primary);--rdp-accent-background-color: var(--purpur-color-background-interactive-primary);--rdp-day-height: var(--purpur-spacing-400);--rdp-day-width: calc(var(--purpur-spacing-400) + var(--purpur-spacing-100));--rdp-day_button-border-radius: 100%;--rdp-day_button-border: none;--rdp-day_button-height: var(--purpur-spacing-400);--rdp-day_button-width: var(--purpur-spacing-400);--rdp-selected-border: var(--purpur-spacing-25) solid var(--rdp-accent-color);--rdp-disabled-opacity: 1;--rdp-outside-opacity: 1;--rdp-today-color: var(--rdp-accent-color);--rdp-dropdown-gap: var(--purpur-spacing-100);--rdp-months-gap: var(purpur-spacing-400);--rdp-nav_button-disabled-opacity: 1;--rdp-nav_button-height: calc(var(---purpur-spacing-50) + var(--purpur-spacing-400));--rdp-nav_button-width: calc(var(---purpur-spacing-50) + var(--purpur-spacing-400));--rdp-nav-height: calc(var(---purpur-spacing-150) + var(--purpur-spacing-400));--rdp-range_middle-background-color: var(--purpur-color-background-interactive-transparent-active);--rdp-range_middle-color: inherit;--rdp-range_start-color: white;--rdp-range_start-background: linear-gradient(var(--rdp-gradient-direction), transparent 50%, var(--rdp-range_middle-background-color) 50%);--rdp-range_start-date-background-color: var(--purpur-color-background-interactive-transparent-active);--rdp-range_end-background: linear-gradient(var(--rdp-gradient-direction), var(--rdp-range_middle-background-color) 50%, transparent 50%);--rdp-range_end-color: white;--rdp-range_end-date-background-color: var(--purpur-color-background-interactive-transparent-active);--rdp-week_number-border-radius: 100%;--rdp-week_number-border: var(--purpur-spacing-25) solid transparent;--rdp-week_number-height: var(--rdp-day-height);--rdp-week_number-opacity: 1;--rdp-week_number-width: var(--rdp-day-width);--rdp-weeknumber-text-align: center;--rdp-weekday-opacity: 1;--rdp-weekday-padding: var(--purpur-spacing-100) 0;--rdp-weekday-text-align: center;--rdp-gradient-direction: 90deg;--rdp-animation_duration: .3s;--rdp-animation_timing: cubic-bezier(.4, 0, .2, 1);min-width:fit-content}._root_11ee2_3 *:focus-visible{outline-color:var(--purpur-color-border-interactive-focus)}._root_11ee2_3 table{width:100%;border-collapse:separate;border-spacing:0 var(--purpur-spacing-100)}._months_11ee2_88{max-width:none}._month_11ee2_88{width:100%}._day_button_11ee2_96{margin:0 auto;font-weight:var(--purpur-typography-weight-medium)}._day_11ee2_96{color:var(--rdp-accent-color);padding:0}._day_11ee2_96:hover:not(._selected_11ee2_105) ._day_button_11ee2_96{background-color:var(--purpur-color-background-interactive-transparent-hover);color:var(--purpur-color-text-interactive-primary-active);border-radius:50%}._month_caption_11ee2_111{height:calc(var(--purpur-spacing-400) + var(--purpur-spacing-50));color:var(--purpur-color-text-default);padding-left:var(--purpur-spacing-100);font-size:var(--purpur-typography-scale-150);font-family:var(--purpur-typography-family-default);font-weight:var(--purpur-typography-weight-medium)}._dropdown_root_11ee2_120{color:var(--rdp-accent-color)}._weekday_11ee2_124,._week_number_11ee2_125{color:var(--purpur-color-text-medium);text-transform:uppercase}._selected_11ee2_105 ._day_button_11ee2_96{color:var(--purpur-color-text-interactive-on-primary);background-color:var(--purpur-color-background-interactive-primary);border-radius:100%}._selected_11ee2_105._range_middle_11ee2_135{background-color:var(--purpur-color-background-interactive-transparent-active)}._selected_11ee2_105._range_middle_11ee2_135 ._day_button_11ee2_96{background-color:transparent;color:var(--purpur-color-text-interactive-primary-active);border-radius:0}._disabled_11ee2_144{color:var(--purpur-color-text-weak);text-decoration:line-through solid var(--purpur-color-text-weak);opacity:100%}._disabled_11ee2_144 ._day_button_11ee2_96{font-weight:var(--purpur-typography-weight-normal)}._outside_11ee2_153{color:var(--purpur-color-text-weak);opacity:100%}._outside_11ee2_153 ._day_button_11ee2_96{font-weight:var(--purpur-typography-weight-normal)}._button_next_11ee2_161,._button_previous_11ee2_162{z-index:2;border-radius:var(--purpur-border-radius-full)!important}._button_next_11ee2_161:before,._button_previous_11ee2_162:before{border-radius:var(--purpur-border-radius-full)!important}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@purpurds/calendar",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"license": "AGPL-3.0-only",
|
|
5
|
+
"main": "./dist/calendar.cjs.js",
|
|
6
|
+
"types": "./dist/calendar.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./dist/calendar.cjs.js",
|
|
10
|
+
"types": "./dist/calendar.d.ts",
|
|
11
|
+
"default": "./dist/calendar.es.js"
|
|
12
|
+
},
|
|
13
|
+
"./styles": "./dist/styles.css",
|
|
14
|
+
"./metadata": "./dist/metadata.js"
|
|
15
|
+
},
|
|
16
|
+
"source": "src/calendar.tsx",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"classnames": "~2.5.0",
|
|
19
|
+
"react-day-picker": "~9.8.1",
|
|
20
|
+
"@purpurds/tokens": "7.7.0",
|
|
21
|
+
"@purpurds/icon": "7.7.0",
|
|
22
|
+
"@purpurds/button": "7.7.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"eslint": "9.24.0",
|
|
26
|
+
"@storybook/react-vite": "^9.0.18",
|
|
27
|
+
"@testing-library/dom": "~10.4.0",
|
|
28
|
+
"@testing-library/jest-dom": "~6.4.0",
|
|
29
|
+
"@testing-library/react": "~16.2.0",
|
|
30
|
+
"@testing-library/user-event": "~14.5.1",
|
|
31
|
+
"@types/node": "20.12.12",
|
|
32
|
+
"@types/react-dom": "^19.0.4",
|
|
33
|
+
"@types/react": "^19.0.10",
|
|
34
|
+
"jsdom": "~22.1.0",
|
|
35
|
+
"lint-staged": "15.5.0",
|
|
36
|
+
"prettier": "~2.8.8",
|
|
37
|
+
"react-dom": "^19.0.0",
|
|
38
|
+
"react": "^19.0.0",
|
|
39
|
+
"storybook": "^9.0.18",
|
|
40
|
+
"typescript": "^5.6.3",
|
|
41
|
+
"vite": "^6.2.1",
|
|
42
|
+
"vitest": "^3.1.2",
|
|
43
|
+
"@purpurds/component-rig": "1.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@types/react": "^18 || ^19",
|
|
47
|
+
"@types/react-dom": "^18 || ^19",
|
|
48
|
+
"react": "^18 || ^19",
|
|
49
|
+
"react-dom": "^18 || ^19"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"@types/react": {
|
|
53
|
+
"optional": true
|
|
54
|
+
},
|
|
55
|
+
"@types/react-dom": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build:dev": "vite",
|
|
61
|
+
"build:watch": "vite build --watch",
|
|
62
|
+
"build": "vite build",
|
|
63
|
+
"ci:build": "rushx build",
|
|
64
|
+
"coverage": "vitest run --coverage",
|
|
65
|
+
"lint:fix": "eslint . --fix",
|
|
66
|
+
"lint": "lint-staged --no-stash 2>&1",
|
|
67
|
+
"sbdev": "rush sbdev",
|
|
68
|
+
"test:unit": "vitest run --passWithNoTests",
|
|
69
|
+
"test:watch": "vitest --watch",
|
|
70
|
+
"test": "rushx test:unit",
|
|
71
|
+
"typecheck": "tsc -p ./tsconfig.json"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/* Variables declaration */
|
|
2
|
+
/* prettier-ignore */
|
|
3
|
+
.root {
|
|
4
|
+
--rdp-accent-color: var(--purpur-color-text-interactive-primary);
|
|
5
|
+
/* The accent color used for selected days and UI elements. */
|
|
6
|
+
--rdp-accent-background-color: var(--purpur-color-background-interactive-primary);
|
|
7
|
+
/* The accent background color used for selected days and UI elements. */
|
|
8
|
+
|
|
9
|
+
--rdp-day-height: var(--purpur-spacing-400);
|
|
10
|
+
/* The height of the day cells. */
|
|
11
|
+
--rdp-day-width: calc(var(--purpur-spacing-400) + var(--purpur-spacing-100));
|
|
12
|
+
/* The width of the day cells. */
|
|
13
|
+
|
|
14
|
+
--rdp-day_button-border-radius: 100%;
|
|
15
|
+
/* The border radius of the day cells. */
|
|
16
|
+
--rdp-day_button-border: none;
|
|
17
|
+
/* The border of the day cells. */
|
|
18
|
+
--rdp-day_button-height: var(--purpur-spacing-400);
|
|
19
|
+
/* The height of the day cells. */
|
|
20
|
+
--rdp-day_button-width: var(--purpur-spacing-400);
|
|
21
|
+
/* The width of the day cells. */
|
|
22
|
+
|
|
23
|
+
--rdp-selected-border: var(--purpur-spacing-25) solid var(--rdp-accent-color);
|
|
24
|
+
/* The border of the selected days. */
|
|
25
|
+
--rdp-disabled-opacity: 1;
|
|
26
|
+
/* The opacity of the disabled days. */
|
|
27
|
+
--rdp-outside-opacity: 1;
|
|
28
|
+
/* The opacity of the days outside the current month. */
|
|
29
|
+
--rdp-today-color: var(--rdp-accent-color);
|
|
30
|
+
/* The color of the today's date. */
|
|
31
|
+
|
|
32
|
+
--rdp-dropdown-gap: var(--purpur-spacing-100);
|
|
33
|
+
/* The gap between the dropdowns used in the month captons. */
|
|
34
|
+
|
|
35
|
+
--rdp-months-gap: var(purpur-spacing-400);
|
|
36
|
+
/* The gap between the months in the multi-month view. */
|
|
37
|
+
|
|
38
|
+
--rdp-nav_button-disabled-opacity: 1;
|
|
39
|
+
/* The opacity of the disabled navigation buttons. */
|
|
40
|
+
--rdp-nav_button-height: calc(var(---purpur-spacing-50) + var(--purpur-spacing-400));
|
|
41
|
+
/* The height of the navigation buttons. */
|
|
42
|
+
--rdp-nav_button-width: calc(var(---purpur-spacing-50) + var(--purpur-spacing-400));
|
|
43
|
+
/* The width of the navigation buttons. */
|
|
44
|
+
--rdp-nav-height: calc(var(---purpur-spacing-150) + var(--purpur-spacing-400));
|
|
45
|
+
/* The height of the navigation bar. */
|
|
46
|
+
|
|
47
|
+
--rdp-range_middle-background-color: var(--purpur-color-background-interactive-transparent-active);
|
|
48
|
+
/* The color of the background for days in the middle of a range. */
|
|
49
|
+
--rdp-range_middle-color: inherit;
|
|
50
|
+
/* The color of the range text. */
|
|
51
|
+
|
|
52
|
+
--rdp-range_start-color: white;
|
|
53
|
+
/* The color of the range text. */
|
|
54
|
+
--rdp-range_start-background: linear-gradient(var(--rdp-gradient-direction), transparent 50%, var(--rdp-range_middle-background-color) 50%);
|
|
55
|
+
/* Used for the background of the start of the selected range. */
|
|
56
|
+
--rdp-range_start-date-background-color: var(--purpur-color-background-interactive-transparent-active);
|
|
57
|
+
/* The background color of the date when at the start of the selected range. */
|
|
58
|
+
|
|
59
|
+
--rdp-range_end-background: linear-gradient(var(--rdp-gradient-direction), var(--rdp-range_middle-background-color) 50%, transparent 50%);
|
|
60
|
+
/* Used for the background of the end of the selected range. */
|
|
61
|
+
--rdp-range_end-color: white;
|
|
62
|
+
/* The color of the range text. */
|
|
63
|
+
--rdp-range_end-date-background-color: var(--purpur-color-background-interactive-transparent-active);
|
|
64
|
+
/* The background color of the date when at the end of the selected range. */
|
|
65
|
+
|
|
66
|
+
--rdp-week_number-border-radius: 100%;
|
|
67
|
+
/* The border radius of the week number. */
|
|
68
|
+
--rdp-week_number-border: var(--purpur-spacing-25) solid transparent;
|
|
69
|
+
/* The border of the week number. */
|
|
70
|
+
|
|
71
|
+
--rdp-week_number-height: var(--rdp-day-height);
|
|
72
|
+
/* The height of the week number cells. */
|
|
73
|
+
--rdp-week_number-opacity: 1;
|
|
74
|
+
/* The opacity of the week number. */
|
|
75
|
+
--rdp-week_number-width: var(--rdp-day-width);
|
|
76
|
+
/* The width of the week number cells. */
|
|
77
|
+
--rdp-weeknumber-text-align: center;
|
|
78
|
+
/* The text alignment of the weekday cells. */
|
|
79
|
+
|
|
80
|
+
--rdp-weekday-opacity: 1;
|
|
81
|
+
/* The opacity of the weekday. */
|
|
82
|
+
--rdp-weekday-padding: var(--purpur-spacing-100) 0;
|
|
83
|
+
/* The padding of the weekday. */
|
|
84
|
+
--rdp-weekday-text-align: center;
|
|
85
|
+
/* The text alignment of the weekday cells. */
|
|
86
|
+
|
|
87
|
+
--rdp-gradient-direction: 90deg;
|
|
88
|
+
|
|
89
|
+
--rdp-animation_duration: 0.3s;
|
|
90
|
+
--rdp-animation_timing: cubic-bezier(0.4, 0, 0.2, 1);
|
|
91
|
+
|
|
92
|
+
min-width: fit-content;
|
|
93
|
+
|
|
94
|
+
*:focus-visible {
|
|
95
|
+
outline-color: var(--purpur-color-border-interactive-focus);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
table {
|
|
99
|
+
width: 100%;
|
|
100
|
+
border-collapse: separate;
|
|
101
|
+
border-spacing: 0 var(--purpur-spacing-100);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.months {
|
|
106
|
+
max-width: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.month {
|
|
110
|
+
width: 100%;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.day_button {
|
|
114
|
+
margin: 0 auto;
|
|
115
|
+
font-weight: var(--purpur-typography-weight-medium);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.day {
|
|
119
|
+
color: var(--rdp-accent-color);
|
|
120
|
+
padding: 0;
|
|
121
|
+
|
|
122
|
+
&:hover:not(.selected) {
|
|
123
|
+
.day_button {
|
|
124
|
+
background-color: var(--purpur-color-background-interactive-transparent-hover);
|
|
125
|
+
color: var(--purpur-color-text-interactive-primary-active);
|
|
126
|
+
border-radius: 50%;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.month_caption {
|
|
132
|
+
height: calc(var(--purpur-spacing-400) + var(--purpur-spacing-50));
|
|
133
|
+
color: var(--purpur-color-text-default);
|
|
134
|
+
padding-left: var(--purpur-spacing-100);
|
|
135
|
+
font-size: var(--purpur-typography-scale-150);
|
|
136
|
+
font-family: var(--purpur-typography-family-default);
|
|
137
|
+
font-weight: var(--purpur-typography-weight-medium);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.dropdown_root {
|
|
141
|
+
color: var(--rdp-accent-color);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.weekday,
|
|
145
|
+
.week_number {
|
|
146
|
+
color: var(--purpur-color-text-medium);
|
|
147
|
+
text-transform: uppercase;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.selected {
|
|
151
|
+
.day_button {
|
|
152
|
+
color: var(--purpur-color-text-interactive-on-primary);
|
|
153
|
+
background-color: var(--purpur-color-background-interactive-primary);
|
|
154
|
+
border-radius: 100%;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
&.range_middle {
|
|
158
|
+
background-color: var(--purpur-color-background-interactive-transparent-active);
|
|
159
|
+
|
|
160
|
+
.day_button {
|
|
161
|
+
background-color: transparent;
|
|
162
|
+
color: var(--purpur-color-text-interactive-primary-active);
|
|
163
|
+
border-radius: 0;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.disabled {
|
|
169
|
+
color: var(--purpur-color-text-weak);
|
|
170
|
+
text-decoration: line-through solid var(--purpur-color-text-weak);
|
|
171
|
+
opacity: 100%;
|
|
172
|
+
|
|
173
|
+
.day_button {
|
|
174
|
+
font-weight: var(--purpur-typography-weight-normal);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.outside {
|
|
179
|
+
color: var(--purpur-color-text-weak);
|
|
180
|
+
opacity: 100%;
|
|
181
|
+
|
|
182
|
+
.day_button {
|
|
183
|
+
font-weight: var(--purpur-typography-weight-normal);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.button_next,
|
|
188
|
+
.button_previous {
|
|
189
|
+
z-index: 2;
|
|
190
|
+
border-radius: var(--purpur-border-radius-full) !important;
|
|
191
|
+
|
|
192
|
+
&::before {
|
|
193
|
+
border-radius: var(--purpur-border-radius-full) !important;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
2
|
+
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
|
+
|
|
6
|
+
import { Calendar, et } from "./calendar";
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: "Forms and Inputs/Calendar",
|
|
10
|
+
component: Calendar,
|
|
11
|
+
parameters: {
|
|
12
|
+
design: [
|
|
13
|
+
{
|
|
14
|
+
name: "Calendar",
|
|
15
|
+
type: "figma",
|
|
16
|
+
url: "https://www.figma.com/design/XEaIIFskrrxIBHMZDkIuIg/Purpur-DS---Component-library---guidelines?node-id=39042-5999&p=f&t=5D0qSGRGqXJgdAxm-0",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
args: {
|
|
21
|
+
mode: "single",
|
|
22
|
+
showWeekNumber: false,
|
|
23
|
+
showOutsideDays: false,
|
|
24
|
+
disabled: [],
|
|
25
|
+
},
|
|
26
|
+
argTypes: {
|
|
27
|
+
mode: {
|
|
28
|
+
control: { type: "radio" },
|
|
29
|
+
options: ["single", "range"],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
decorators: (Story) => (
|
|
33
|
+
<div style={{ width: "20rem" }}>
|
|
34
|
+
<Story />
|
|
35
|
+
</div>
|
|
36
|
+
),
|
|
37
|
+
} satisfies Meta<typeof Calendar>;
|
|
38
|
+
|
|
39
|
+
export default meta;
|
|
40
|
+
type Story = StoryObj<typeof Calendar>;
|
|
41
|
+
|
|
42
|
+
export const Showcase: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
mode: "single",
|
|
45
|
+
disabled: [{ dayOfWeek: [0, 6] }],
|
|
46
|
+
showWeekNumber: true,
|
|
47
|
+
showOutsideDays: true,
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const Controlled: Story = {
|
|
52
|
+
render: (args) => {
|
|
53
|
+
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
|
54
|
+
|
|
55
|
+
const handleDateSelect = (date: Date | undefined) => {
|
|
56
|
+
setSelectedDate(date);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div>
|
|
61
|
+
<div style={{ display: "flex", gap: "10px", marginBottom: "10px" }}>
|
|
62
|
+
<button
|
|
63
|
+
onClick={() => {
|
|
64
|
+
handleDateSelect(new Date());
|
|
65
|
+
}}
|
|
66
|
+
>
|
|
67
|
+
Select today
|
|
68
|
+
</button>
|
|
69
|
+
<button
|
|
70
|
+
onClick={() => {
|
|
71
|
+
handleDateSelect(new Date(Date.now() - 24 * 60 * 60 * 1000));
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
Select yesterday
|
|
75
|
+
</button>
|
|
76
|
+
<button
|
|
77
|
+
onClick={() => {
|
|
78
|
+
handleDateSelect(new Date(Date.now() + 24 * 60 * 60 * 1000));
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
Select tomorrow
|
|
82
|
+
</button>
|
|
83
|
+
</div>
|
|
84
|
+
<Calendar
|
|
85
|
+
{...args}
|
|
86
|
+
locale={et}
|
|
87
|
+
mode="single"
|
|
88
|
+
selected={selectedDate}
|
|
89
|
+
onSelect={(date: Date | undefined) => {
|
|
90
|
+
handleDateSelect(date);
|
|
91
|
+
}}
|
|
92
|
+
/>
|
|
93
|
+
<p>Selected date: {selectedDate ? selectedDate.toLocaleDateString() : "None"}</p>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const Range: Story = {
|
|
100
|
+
args: { mode: "range", numberOfMonths: 2 },
|
|
101
|
+
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import * as matchers from "@testing-library/jest-dom/matchers";
|
|
3
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { Calendar } from "./calendar";
|
|
7
|
+
|
|
8
|
+
expect.extend(matchers);
|
|
9
|
+
|
|
10
|
+
describe("Calendar", () => {
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
cleanup();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("renders without crashing in single mode", () => {
|
|
16
|
+
render(
|
|
17
|
+
<Calendar mode="single" selected={undefined} onSelect={() => {}} data-testid="calendar" />
|
|
18
|
+
);
|
|
19
|
+
expect(screen.getByTestId("calendar")).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("renders without crashing in range mode", () => {
|
|
23
|
+
render(
|
|
24
|
+
<Calendar mode="range" selected={undefined} onSelect={() => {}} data-testid="calendar" />
|
|
25
|
+
);
|
|
26
|
+
expect(screen.getByTestId("calendar")).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("calls onSelect when a day is clicked in single mode", () => {
|
|
30
|
+
const handleSelect = vi.fn();
|
|
31
|
+
render(
|
|
32
|
+
<Calendar mode="single" selected={undefined} onSelect={handleSelect} data-testid="calendar" />
|
|
33
|
+
);
|
|
34
|
+
// Find a day button and click it
|
|
35
|
+
const dayButton = screen.getAllByRole("button", { name: /\d+/ })[0];
|
|
36
|
+
fireEvent.click(dayButton);
|
|
37
|
+
expect(handleSelect).toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("calls onSelect when a day is clicked in range mode", () => {
|
|
41
|
+
const handleSelect = vi.fn();
|
|
42
|
+
render(
|
|
43
|
+
<Calendar mode="range" selected={undefined} onSelect={handleSelect} data-testid="calendar" />
|
|
44
|
+
);
|
|
45
|
+
// Find a day button and click it
|
|
46
|
+
const dayButton = screen.getAllByRole("button", { name: /\d+/ })[0];
|
|
47
|
+
fireEvent.click(dayButton);
|
|
48
|
+
expect(handleSelect).toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("shows the selected day in single mode", () => {
|
|
52
|
+
const selectedDate = new Date();
|
|
53
|
+
render(
|
|
54
|
+
<Calendar mode="single" selected={selectedDate} onSelect={() => {}} data-testid="calendar" />
|
|
55
|
+
);
|
|
56
|
+
// The selected day should have aria-pressed="true"
|
|
57
|
+
const selectedButton = screen
|
|
58
|
+
.getAllByRole("button")
|
|
59
|
+
.find((btn) => btn.getAttribute("tabIndex") === "0");
|
|
60
|
+
expect(selectedButton).toBeDefined();
|
|
61
|
+
expect(selectedButton).toHaveTextContent(String(selectedDate.getDate()));
|
|
62
|
+
expect(selectedButton).toHaveAttribute("aria-label", expect.stringContaining("selected"));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("applies custom data-testid", () => {
|
|
66
|
+
render(
|
|
67
|
+
<Calendar
|
|
68
|
+
mode="single"
|
|
69
|
+
selected={undefined}
|
|
70
|
+
onSelect={() => {}}
|
|
71
|
+
data-testid="custom-calendar"
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
expect(screen.getByTestId("custom-calendar")).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("applies custom class names", () => {
|
|
78
|
+
render(
|
|
79
|
+
<Calendar
|
|
80
|
+
mode="single"
|
|
81
|
+
selected={undefined}
|
|
82
|
+
onSelect={() => {}}
|
|
83
|
+
data-testid="custom-calendar"
|
|
84
|
+
className="custom-calendar-class"
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
expect(screen.getByTestId("custom-calendar")).toHaveClass("custom-calendar-class");
|
|
88
|
+
});
|
|
89
|
+
});
|
package/src/calendar.tsx
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
type DateRange,
|
|
4
|
+
DayPicker,
|
|
5
|
+
type DayPickerProps,
|
|
6
|
+
getDefaultClassNames,
|
|
7
|
+
} from "react-day-picker";
|
|
8
|
+
import { Button } from "@purpurds/button";
|
|
9
|
+
import { IconChevronLeft } from "@purpurds/icon/chevron-left";
|
|
10
|
+
import { IconChevronRight } from "@purpurds/icon/chevron-right";
|
|
11
|
+
|
|
12
|
+
export * from "react-day-picker/locale";
|
|
13
|
+
|
|
14
|
+
import "react-day-picker/style.css";
|
|
15
|
+
import "@purpurds/button/styles";
|
|
16
|
+
import "@purpurds/icon/styles";
|
|
17
|
+
import styles from "./calendar.module.scss";
|
|
18
|
+
|
|
19
|
+
type CalendarModeProps =
|
|
20
|
+
| {
|
|
21
|
+
mode: "single";
|
|
22
|
+
selected: Date | undefined;
|
|
23
|
+
onSelect: (date: Date | undefined) => void;
|
|
24
|
+
}
|
|
25
|
+
| {
|
|
26
|
+
mode: "range";
|
|
27
|
+
selected: DateRange | undefined;
|
|
28
|
+
onSelect: (dateRange: DateRange | undefined) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type CalendarProps = Omit<DayPickerProps, "mode" | "selected" | "onSelect"> &
|
|
32
|
+
CalendarModeProps & {
|
|
33
|
+
["data-testid"]?: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Calendar component that wraps the DayPicker from react-day-picker.
|
|
38
|
+
* You can use react-day-picker's props to customize the calendar.
|
|
39
|
+
* https://daypicker.dev/api
|
|
40
|
+
*
|
|
41
|
+
* Year range is set to 20 years (10 years before today, and 10 years after today).
|
|
42
|
+
* This can be customized using the `startMonth` and `endMonth` props.
|
|
43
|
+
*/
|
|
44
|
+
export const Calendar = (props: CalendarProps) => {
|
|
45
|
+
const defaultClassNames = getDefaultClassNames();
|
|
46
|
+
|
|
47
|
+
const calendarClassnames = {
|
|
48
|
+
...styles,
|
|
49
|
+
root: `${defaultClassNames.root} ${styles.root}`,
|
|
50
|
+
day_button: `${defaultClassNames.day_button} ${styles.day_button}`,
|
|
51
|
+
day: `${defaultClassNames.day} ${styles.day}`,
|
|
52
|
+
weekday: `${defaultClassNames.weekday} ${styles.weekday}`,
|
|
53
|
+
week_number: `${defaultClassNames.week_number} ${styles.week_number}`,
|
|
54
|
+
month_caption: `${defaultClassNames.month_caption} ${styles.month_caption}`,
|
|
55
|
+
disabled: `${defaultClassNames.disabled} ${styles.disabled}`,
|
|
56
|
+
dropdown: `${defaultClassNames.dropdown} ${styles.dropdown}`,
|
|
57
|
+
years_dropdown: `${defaultClassNames.years_dropdown} ${styles.years_dropdown}`,
|
|
58
|
+
outside: `${defaultClassNames.outside} ${styles.outside}`,
|
|
59
|
+
dropdown_root: `${defaultClassNames.dropdown_root} ${styles.dropdown_root}`,
|
|
60
|
+
months: `${defaultClassNames.months} ${styles.months}`,
|
|
61
|
+
month: `${defaultClassNames.month} ${styles.month}`,
|
|
62
|
+
button_next: `${defaultClassNames.button_next} ${styles.button_next}`,
|
|
63
|
+
button_previous: `${defaultClassNames.button_previous} ${styles.button_previous}`,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const yearsBack = new Date().getFullYear() - 10;
|
|
67
|
+
const yearsForward = new Date().getFullYear() + 10;
|
|
68
|
+
|
|
69
|
+
const [month, setMonth] = useState<Date | undefined>();
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (props.mode === "single" && props.selected instanceof Date) {
|
|
73
|
+
setMonth(props.selected);
|
|
74
|
+
} else if (
|
|
75
|
+
props.mode === "range" &&
|
|
76
|
+
props.selected &&
|
|
77
|
+
typeof props.selected === "object" &&
|
|
78
|
+
"from" in props.selected &&
|
|
79
|
+
props.selected.from instanceof Date
|
|
80
|
+
) {
|
|
81
|
+
setMonth(props.selected.from);
|
|
82
|
+
}
|
|
83
|
+
}, [props.selected, props.mode]);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<DayPicker
|
|
87
|
+
weekStartsOn={1}
|
|
88
|
+
captionLayout="dropdown-years"
|
|
89
|
+
startMonth={new Date(yearsBack, 0)}
|
|
90
|
+
endMonth={new Date(yearsForward, 11)}
|
|
91
|
+
{...props}
|
|
92
|
+
month={month}
|
|
93
|
+
onMonthChange={setMonth}
|
|
94
|
+
classNames={calendarClassnames}
|
|
95
|
+
navLayout="around"
|
|
96
|
+
components={{
|
|
97
|
+
PreviousMonthButton: (props) => (
|
|
98
|
+
<Button variant="text" size="sm" iconOnly {...props}>
|
|
99
|
+
<IconChevronLeft size="sm" />
|
|
100
|
+
</Button>
|
|
101
|
+
),
|
|
102
|
+
NextMonthButton: (props) => (
|
|
103
|
+
<Button variant="text" size="sm" iconOnly {...props}>
|
|
104
|
+
<IconChevronRight size="sm" />
|
|
105
|
+
</Button>
|
|
106
|
+
),
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
Calendar.displayName = "Calendar";
|
package/src/global.d.ts
ADDED