mirror of https://github.com/Budibase/budibase.git
37 changed files with 612 additions and 128 deletions
@ -0,0 +1,178 @@ |
|||||
|
<script> |
||||
|
import { onMount } from "svelte" |
||||
|
import { |
||||
|
startOfMonth, |
||||
|
endOfMonth, |
||||
|
getDate, |
||||
|
getMonth, |
||||
|
getYear, |
||||
|
addMonths, |
||||
|
subMonths, |
||||
|
format, |
||||
|
} from "date-fns" |
||||
|
import { MDCMenu } from "@material/menu" |
||||
|
import { Textfield } from "../Textfield" |
||||
|
import Icon from "../Common/Icon.svelte" |
||||
|
import ripple from "../Common/Ripple.js" |
||||
|
import { Body1, Body2, Caption } from "../Typography" |
||||
|
import { IconButton } from "../IconButton" |
||||
|
|
||||
|
let menu |
||||
|
let instance |
||||
|
let textfieldValue = "" |
||||
|
|
||||
|
let daysArr = [] |
||||
|
let navDate = new Date() |
||||
|
const weekdayMap = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"] |
||||
|
|
||||
|
export let date = new Date() |
||||
|
export let label = "" |
||||
|
export let onSelect = selectedDate => {} |
||||
|
|
||||
|
onMount(() => { |
||||
|
if (!!menu) { |
||||
|
instance = new MDCMenu(menu) |
||||
|
instance.open = false |
||||
|
instance.setFixedPostion = true |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
function selectDate(dayOfMonth) { |
||||
|
let month = getMonth(navDate) |
||||
|
let year = getYear(navDate) |
||||
|
date = new Date(year, month, dayOfMonth) |
||||
|
onSelect(date) |
||||
|
} |
||||
|
|
||||
|
function addMonth() { |
||||
|
navDate = addMonths(navDate, 1) |
||||
|
} |
||||
|
|
||||
|
function subtractMonth() { |
||||
|
navDate = subMonths(navDate, 1) |
||||
|
} |
||||
|
|
||||
|
function openCalendar(isOpen) { |
||||
|
instance.open = isOpen === undefined ? !instance.open : isOpen |
||||
|
} |
||||
|
|
||||
|
function textFieldChange(value) { |
||||
|
const isDate = /^\d{1,2}\/\d{1,2}\/\d{4}$/ |
||||
|
if (isDate.test(value)) { |
||||
|
const [year, month, day] = value.split("/").reverse() |
||||
|
if (month > 0 && month <= 12 && (day > 0 && day <= 31)) { |
||||
|
date = new Date(year, month - 1, day) |
||||
|
navDate = date |
||||
|
openCalendar(true) |
||||
|
onSelect(date) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$: dateMonthEnds = endOfMonth(navDate).getDate() |
||||
|
$: dateMonthBegins = startOfMonth(navDate).getDay() |
||||
|
$: dayStart = dateMonthBegins + 1 //1 = sunday |
||||
|
$: monthAndYear = format(navDate, "MMMM y") |
||||
|
$: selectedDate = format(date, "dd/MM/yyyy") |
||||
|
$: dayOfSelectedDate = getDate(date) |
||||
|
$: for (let d = 1; d <= dateMonthEnds; d++) { |
||||
|
if (d === 1) { |
||||
|
daysArr = [d] |
||||
|
} else { |
||||
|
daysArr = [...daysArr, d] |
||||
|
} |
||||
|
} |
||||
|
$: rowRepeater = |
||||
|
dateMonthBegins > 5 && daysArr[daysArr.length - 1] > 30 ? 6 : 5 |
||||
|
$: sameMonthAndYear = |
||||
|
getMonth(date) === getMonth(navDate) && getYear(date) === getYear(navDate) |
||||
|
</script> |
||||
|
|
||||
|
<div class="mdc-menu-surface--anchor"> |
||||
|
<Textfield |
||||
|
{label} |
||||
|
onChange={textFieldChange} |
||||
|
value={selectedDate} |
||||
|
trailingIcon={true} |
||||
|
useIconButton={true} |
||||
|
iconButtonClick={openCalendar} |
||||
|
icon="calendar_today" /> |
||||
|
|
||||
|
<div |
||||
|
bind:this={menu} |
||||
|
class="mdc-menu mdc-menu-surface bbmd-menu" |
||||
|
style={`margin-top: 70px`}> |
||||
|
<div class="calendar-container"> |
||||
|
<div class="month-picker"> |
||||
|
<div> |
||||
|
<IconButton icon="chevron_left" onClick={subtractMonth} /> |
||||
|
</div> |
||||
|
<div class="centreText"> |
||||
|
<Body1 text={monthAndYear} /> |
||||
|
</div> |
||||
|
<div> |
||||
|
<IconButton icon="chevron_right" onClick={addMonth} /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="week-days"> |
||||
|
{#each weekdayMap as day, i} |
||||
|
<div class="centreText"> |
||||
|
<Caption text={day} /> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
<div |
||||
|
class="day-picker" |
||||
|
style={`grid-template-rows: repeat(${rowRepeater}, 40px)`}> |
||||
|
{#each daysArr as day, i} |
||||
|
<div |
||||
|
use:ripple |
||||
|
style={i === 0 ? `grid-column-start: ${dayStart}` : ``} |
||||
|
on:click={() => selectDate(day)} |
||||
|
class={`bbmd-day ${dayOfSelectedDate === day && sameMonthAndYear ? 'selected' : ''}`}> |
||||
|
<Body2 text={day} /> |
||||
|
</div> |
||||
|
{/each} |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<!-- Superfluous but necessary to keep the menu instance sweet --> |
||||
|
<ul class="mdc-list" role="menu" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.bbmd-menu { |
||||
|
width: 330px; |
||||
|
height: auto; |
||||
|
padding: 5px; |
||||
|
} |
||||
|
|
||||
|
.month-picker { |
||||
|
display: grid; |
||||
|
grid-template-columns: 40px 1fr 40px; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.calendar-container { |
||||
|
display: grid; |
||||
|
height: 100%; |
||||
|
grid-template-rows: repeat(3, auto); |
||||
|
grid-gap: 5px; |
||||
|
} |
||||
|
|
||||
|
.week-days { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(7, 1fr); |
||||
|
} |
||||
|
|
||||
|
.day-picker { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(7, 1fr); |
||||
|
} |
||||
|
|
||||
|
.centreText { |
||||
|
text-align: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,15 @@ |
|||||
|
@import "@material/ripple/mdc-ripple.scss"; |
||||
|
@import "@material/theme/mixins"; |
||||
|
|
||||
|
.bbmd-day { |
||||
|
transition: background-color .25s cubic-bezier(0.25, 0.46, 0.45, 0.94); |
||||
|
display: flex; |
||||
|
border-radius: 50%; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.selected { |
||||
|
@include mdc-theme-prop(background-color, primary); |
||||
|
@include mdc-theme-prop(color, on-primary); |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
import "./_style.scss"; |
||||
|
export { default as DatePicker } from "./DatePicker.svelte"; |
||||
@ -0,0 +1,66 @@ |
|||||
|
<script> |
||||
|
import ripple from "../Common/Ripple.js" |
||||
|
import ClassBuilder from "../ClassBuilder.js" |
||||
|
|
||||
|
const cb = new ClassBuilder("icon-button") |
||||
|
|
||||
|
let on = false |
||||
|
|
||||
|
export let _bb |
||||
|
export let context = "" |
||||
|
export let onClick = () => {} |
||||
|
export let disabled = false |
||||
|
export let href = "" |
||||
|
export let icon = "" |
||||
|
export let onIcon = "" //on state icon for toggle button |
||||
|
export let size = "medium" |
||||
|
|
||||
|
function onButtonClick() { |
||||
|
open = !open |
||||
|
onClick() |
||||
|
} |
||||
|
|
||||
|
$: isToggleButton = !!icon && !!onIcon |
||||
|
$: useLinkButton = !!href |
||||
|
|
||||
|
$: customs = { size } |
||||
|
$: props = { customs, extras: ["material-icons", context] } |
||||
|
$: iconBtnClass = cb.build({ props }) |
||||
|
</script> |
||||
|
|
||||
|
{#if useLinkButton} |
||||
|
<a |
||||
|
on:click={onButtonClick} |
||||
|
class={iconBtnClass} |
||||
|
{href} |
||||
|
{disabled} |
||||
|
role="button" |
||||
|
tabindex="0"> |
||||
|
{#if isToggleButton} |
||||
|
<i |
||||
|
use:ripple |
||||
|
class="material-icons mdc-icon-button__icon mdc-icon-button__icon--on"> |
||||
|
{onIcon} |
||||
|
</i> |
||||
|
<i use:ripple class="material-icons mdc-icon-button__icon">{icon}</i> |
||||
|
{:else}{icon}{/if} |
||||
|
</a> |
||||
|
{:else} |
||||
|
<button |
||||
|
on:click={onButtonClick} |
||||
|
class={iconBtnClass} |
||||
|
{disabled} |
||||
|
role="button" |
||||
|
aria-label="Add to favorites" |
||||
|
aria-pressed="false" |
||||
|
tabindex="0"> |
||||
|
{#if isToggleButton} |
||||
|
<i use:ripple class="material-icons mdc-icon-button__icon">{icon}</i> |
||||
|
<i |
||||
|
use:ripple |
||||
|
class="material-icons mdc-icon-button__icon mdc-icon-button__icon--on"> |
||||
|
{onIcon} |
||||
|
</i> |
||||
|
{:else}{icon}{/if} |
||||
|
</button> |
||||
|
{/if} |
||||
@ -0,0 +1,18 @@ |
|||||
|
@import "@material/icon-button/mdc-icon-button"; |
||||
|
|
||||
|
.mdc-icon-button { |
||||
|
|
||||
|
&.bbmd-mdc-icon-button--size-large { |
||||
|
@include mdc-icon-button-icon-size(24px); |
||||
|
} |
||||
|
|
||||
|
&.bbmd-mdc-icon-button--size-medium { |
||||
|
@include mdc-icon-button-icon-size(20px); |
||||
|
} |
||||
|
|
||||
|
&.bbmd-mdc-icon-button--size-small { |
||||
|
@include mdc-icon-button-icon-size(16px); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,2 @@ |
|||||
|
import "./_style.scss"; |
||||
|
export { default as IconButton } from "./IconButton.svelte"; |
||||
Loading…
Reference in new issue