Published
Edited
Nov 22, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
run = async () => {
const dayjs = await require('dayjs');
const _ = await require('lodash');
const Wrapper = () => {
// CHANGE CODE BELOW THIS LINE
// My Imported Components START
const decrementSelectedMonth = (selectedMonth, setSelectedMonth) => {
const newMonth = selectedMonth.subtract(1, "month");
setSelectedMonth(newMonth);
};

const incrementSelectedMonth = (selectedMonth, setSelectedMonth) => {
const newMonth = selectedMonth.add(1, "month");
setSelectedMonth(newMonth);
};

const onDayClick = (day, selectedDays, setSelectedDays, allowsRange) => {
const newSelectedDays =
allowsRange && selectedDays[0] ? [day, selectedDays[0]] : [day, day];
setSelectedDays(newSelectedDays);
};

const useSelector = (originalState, selector) => {
const [computedState, setComputedState] = React.useState(
selector(originalState)
);
React.useEffect(() => {
setComputedState(selector(originalState));
}, [originalState]);
return computedState;
};

const labelStyle = {
color: "rgba(113, 113, 113, 1)",
fontFamily: "sans-serif",
fontStyle: "normal",
fontWeight: 500,
fontSize: "14px",
lineHeight: "130%",
};
const paragraphStyle = {
fontFamily: "sans-serif",
fontStyle: "normal",
fontWeight: "normal",
fontSize: "18px",
lineHeight: "130%",
};
const subheadStyle = {
fontFamily: "sans-serif",
fontStyle: "normal",
fontWeight: 500,
fontSize: "24px",
lineHeight: "130%",
};
const dayStyle = {
width: "40px",
height: "40px",
display: "flex",
alignItems: "center",
justifyContent: "center",
marginTop: "-2px",
position: "relative",
border: "2px solid transparent",
cursor: "pointer",
boxSizing: "border-box",
};
const dayStartStyle = {
border: "2px solid rgba(90, 100, 255, 1)",
borderRadius: "20px 0 0 20px",
borderRightWidth: 0,
borderLeftWidth: "2px",
paddingRight: "2px",
};
const dayEndStyle = {
border: "2px solid rgba(90, 100, 255, 1)",
borderRadius: "0 20px 20px 0",
borderLeftWidth: 0,
borderRightWidth: "2px",
paddingLeft: "2px",
};
const dayStartAndEndStyle = {
borderRadius: "20px",
borderLeftWidth: "2px",
borderRightWidth: "2px",
paddingLeft: 0,
paddingRight: 0,
};
const dayMiddleStyle = {
border: "2px solid rgba(90, 100, 255, 1)",
borderLeftWidth: 0,
borderRightWidth: 0,
paddingLeft: "2px",
paddingRight: "2px",
};
const CalendarGrid = ({
currentDay,
selectedMonth,
firstSelectedDay,
secondSelectedDay,
onDayClick,
enabledDays,
}) => {
const startOfMonth = selectedMonth.startOf("month");
const days = _.times(selectedMonth.daysInMonth(), (dayIndex) =>
startOfMonth.add(dayIndex, "day")
);
const weeks = _.reduce(
days,
(weeks, day) => {
const mostRecentWeek = weeks[_.size(weeks) - 1];
if (day.day() === 0 && !_.isEmpty(mostRecentWeek)) {
weeks.push([day]);
} else {
mostRecentWeek.push(day);
}
return weeks;
},
[[]]
);
return (
jsx`<div
style={{
display: "flex",
flexDirection: "column",
}}
>
<div style={{ display: "flex", flexDirection: "row" }}>
<div style={{ ...labelStyle, ...dayStyle }}>S</div>
<div style={{ ...labelStyle, ...dayStyle }}>M</div>
<div style={{ ...labelStyle, ...dayStyle }}>T</div>
<div style={{ ...labelStyle, ...dayStyle }}>W</div>
<div style={{ ...labelStyle, ...dayStyle }}>T</div>
<div style={{ ...labelStyle, ...dayStyle }}>F</div>
<div style={{ ...labelStyle, ...dayStyle }}>S</div>
</div>
{_.map(weeks, (week, index) => (
<div
style={{ display: "flex", flexDirection: "row", justifyContent: index === 0 ? 'flex-end' : undefined }}
key={week[0].toISOString()}
>
{_.map(week, (day) => {
const isDisabled = enabledDays
? !_.find(enabledDays, (enabledDay) =>
day.isSame(enabledDay, "day")
)
: false;
const isCurrentDay = day.isSame(currentDay, "day");
const isStartDay =
!_.isNil(firstSelectedDay) && day.isSame(firstSelectedDay, "day");
const isMiddleDay =
!_.isNil(firstSelectedDay) &&
!_.isNil(secondSelectedDay) &&
day.isAfter(firstSelectedDay, "day") &&
day.isBefore(secondSelectedDay, "day");
const isEndDay =
!_.isNil(firstSelectedDay) &&
day.isSame(secondSelectedDay || firstSelectedDay, "day");
const isAfterCurrent =
day.isAfter(currentDay, "day") && _.isNil(enabledDays);
const styles = _.compact([
dayStyle,
isStartDay ? dayStartStyle : null,
isMiddleDay ? dayMiddleStyle : null,
isEndDay ? dayEndStyle : null,
isStartDay && isEndDay ? dayStartAndEndStyle : null,
]);
return (
<div
style={_.defaults(..._.reverse(styles))}
key={day.toISOString()}
onClick={!isDisabled ? () => onDayClick(day) : undefined}
>
<div style={labelStyle}>{day.date().toString()}</div>
</div>
);
})}
</div>
))}
</div>`
);
};

const sortSelectedDays = (selectedDays) =>
_.sortBy(selectedDays, (day) => (day ? day.unix() : Number.MAX_SAFE_INTEGER));

const DatePicker = (props) => {
const currentDay = dayjs();
const [selectedMonth, setSelectedMonth] = React.useState(currentDay.clone());
const [selectedDays, setSelectedDays] = React.useState([]);
const [firstSelectedDay, secondSelectedDay] = useSelector(
selectedDays,
sortSelectedDays
);
React.useEffect(() => {
if (props.value) {
if (props.allowsRange) {
if (!_.isEqual(props.value, [firstSelectedDay, secondSelectedDay])) {
setSelectedDays(props.value);
setSelectedMonth(props.value[1]);
}
} else {
const newValue = [props.value, props.value];
if (!_.isEqual(newValue, [firstSelectedDay, secondSelectedDay])) {
setSelectedDays([props.value, props.value]);
setSelectedMonth(props.value);
}
}
}
}, [props.value]);
React.useEffect(() => {
if (firstSelectedDay && secondSelectedDay) {
if (props.allowsRange) {
if (!_.isEqual([firstSelectedDay, secondSelectedDay], props.value)) {
props.onChange([firstSelectedDay, secondSelectedDay]);
}
} else {
if (!_.isEqual(firstSelectedDay, props.value)) {
props.onChange(firstSelectedDay);
}
}
}
}, [firstSelectedDay, secondSelectedDay]);
return (
jsx`<div style={{ display: "flex", flexDirection: "column", width: "280px" }}>
<div
style={{ display: "flex", flexDirection: "row", alignItems: "center" }}
>
<div style={{...paragraphStyle, flex: 1}}>{selectedMonth.format("MMMM YYYY")}</div>
<div
style={{
padding: "12px",
color: "rgba(90, 100, 255, 1)",
textAlign: "center",
marginLeft: "8px",
cursor: "pointer",
}}
onClick={() =>
decrementSelectedMonth(selectedMonth, setSelectedMonth)
}
>
{"<"}
</div>
<div
style={{
padding: "12px",
color: "rgba(90, 100, 255, 1)",
textAlign: "center",
marginLeft: "8px",
cursor: "pointer",
}}
onClick={() =>
incrementSelectedMonth(selectedMonth, setSelectedMonth)
}
>
{">"}
</div>
</div>
<CalendarGrid
currentDay={currentDay}
selectedMonth={selectedMonth}
onDayClick={(day) =>
onDayClick(
day,
selectedDays,
setSelectedDays,
props.allowsRange || false
)
}
firstSelectedDay={firstSelectedDay}
secondSelectedDay={secondSelectedDay}
enabledDays={props.enabledDays}
/>
<div
style={{
marginTop: "16px",
width: "100%",
display: "flex",
flexDirection: "row",
justifyContent: "center",
}}
>
<div style={{ subheadStyle }}>
{firstSelectedDay
? firstSelectedDay.format("MMM D")
: props.allowsRange
? "Start"
: "Date"}
</div>
{props.allowsRange && (
<div style={{ subheadStyle }}>
&nbsp;–&nbsp;
{secondSelectedDay ? secondSelectedDay.format("MMM D") : "End"}
</div>
)}
</div>
</div>`
);
};
// My Imported Components END

const jsx = (val) => `jsx${val}`;
const SingleDay = () => {
const [value, setValue] = React.useState();
return jsx`<DatePicker value={value} onChange={setValue} />`;
};
const Range = () => {
const [value, setValue] = React.useState();
return jsx`<DatePicker allowsRange={true} value={value} onChange={setValue} />`;
};

const App = () => {
return jsx`<div>
<SingleDay />
<Range />
</div>`;
}
//CHANGE CODE ABOVE THIS LINE
return jsx`<App />`;
}
const code = `const go = async () => {
const dayjs= await require('dayjs');
const _ = await require('lodash');
const Wrapper = ${Wrapper.toString().replace(/jsx`([^`]+)`/g, '$1')}
ReactDOM.render(Wrapper(), root);
};
go();`;
return Prettier.format(code, {parser: "typescript", plugins: [PrettierTypescriptParser]});
}
Insert cell
md`## Dependencies`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more