Skip to content

refactor: Replace date-fns usage with new @layerstack/utils date utils (based on d3-time) to reduce bundle size #602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/funny-days-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte-ux': patch
---

refactor: Replace `date-fns` usage with new `@layerstack/utils` date utils (based on d3-time) to reduce bundle size
11 changes: 5 additions & 6 deletions packages/svelte-ux/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,15 @@
"dependencies": {
"@floating-ui/dom": "^1.7.0",
"@fortawesome/fontawesome-common-types": "^6.7.2",
"@layerstack/svelte-actions": "1.0.1-next.6",
"@layerstack/svelte-stores": "1.0.2-next.6",
"@layerstack/svelte-table": "1.0.1-next.6",
"@layerstack/tailwind": "2.0.0-next.8",
"@layerstack/utils": "2.0.0-next.6",
"@layerstack/svelte-actions": "1.0.1-next.11",
"@layerstack/svelte-stores": "1.0.2-next.11",
"@layerstack/svelte-table": "1.0.1-next.11",
"@layerstack/tailwind": "2.0.0-next.13",
"@layerstack/utils": "2.0.0-next.11",
"@mdi/js": "^7.4.47",
"culori": "^4.0.1",
"d3-array": "^3.2.4",
"d3-scale": "^4.0.2",
"date-fns": "^4.1.0",
"esm-env": "^1.2.2",
"immer": "^10.1.1",
"lodash-es": "^4.17.21",
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte-ux/src/lib/components/DateButton.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import { isWithinInterval } from 'date-fns';

import { cls } from '@layerstack/tailwind';
import {
Expand All @@ -9,6 +8,7 @@
type CustomIntlDateTimeFormatOptions,
type SelectedDate,
getDateFuncsByPeriodType,
isDateWithin,
} from '@layerstack/utils';

import Button from './Button.svelte';
Expand Down Expand Up @@ -50,7 +50,7 @@
? selected.some((d) => isSame(date, d))
: selected instanceof Object
? selected.from
? isWithinInterval(date, {
? isDateWithin(date, {
start: start(selected.from),
end: end(selected.to ?? selected.from),
})
Expand Down
5 changes: 2 additions & 3 deletions packages/svelte-ux/src/lib/components/DateField.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { createEventDispatcher, type ComponentProps } from 'svelte';
import { parse as parseDate, format as formatDate } from 'date-fns';
import { type DisabledDate } from '@layerstack/utils';
import { formatDate, parseDate, type DisabledDate } from '@layerstack/utils';
import { cls } from '@layerstack/tailwind';

import { getComponentSettings, getSettings } from './settings.js';
Expand Down Expand Up @@ -54,7 +53,7 @@
function onInputChange(e: any) {
inputValue = e.detail.value;
const lastValue = value;
const parsedValue = parseDate(inputValue ?? '', actualFormat, new Date());
const parsedValue = parseDate(inputValue ?? '', actualFormat);
value = isNaN(parsedValue.valueOf()) ? null : parsedValue;
if (value != lastValue) {
dispatch('change', { value });
Expand Down
14 changes: 8 additions & 6 deletions packages/svelte-ux/src/lib/components/DateRange.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<script lang="ts">
import { isAfter, isBefore, isSameDay } from 'date-fns';
import { cls } from '@layerstack/tailwind';
import { omit } from '@layerstack/utils/object';
import { mdScreen } from '@layerstack/svelte-stores';
Expand All @@ -8,6 +7,9 @@
DayOfWeek,
getDateFuncsByPeriodType,
type DisabledDate,
isDateAfter,
isDateBefore,
isSameInterval,
} from '@layerstack/utils';
import { getDateRangePresets, type DateRange } from '@layerstack/utils/dateRange';
import { hasDayOfWeek, replaceDayOfWeek, missingDayOfWeek } from '@layerstack/utils/date';
Expand Down Expand Up @@ -72,7 +74,7 @@
}

function onDateChange(date: Date) {
// Apply date-fns function based on type and from/to.
// Apply date function based on type and from/to.
let newSelected = { ...selected, periodType: selectedPeriodType };

const { start, end } = getDateFuncsByPeriodType($localeSettings, selectedPeriodType);
Expand All @@ -81,12 +83,12 @@

if (activeDate === 'from') {
newSelected.from = start(date);
if (selected!.to != null && isAfter(date, selected!.to)) {
if (selected!.to != null && isDateAfter(date, selected!.to)) {
newSelected.to = end(date);
}
} else {
newSelected.to = end(date);
if (selected!.from != null && isBefore(date, selected!.from)) {
if (selected!.from != null && isDateBefore(date, selected!.from)) {
newSelected.from = start(date);
newActiveDate = 'to';
}
Expand Down Expand Up @@ -134,9 +136,9 @@
].find(
(x) =>
x.value.from &&
isSameDay(x.value.from, selected!.from!) &&
isSameInterval('day', x.value.from, selected!.from!) &&
x.value.to &&
isSameDay(x.value.to, selected!.to!)
isSameInterval('day', x.value.to, selected!.to!)
);

if (prevPeriodTypePreset && newPeriodType) {
Expand Down
13 changes: 10 additions & 3 deletions packages/svelte-ux/src/lib/components/DateSelect.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
TODO:
- [ ] Set max-height / overflow on MonthListByYear, YearList
*/
import { startOfMonth as startOfMonthFunc } from 'date-fns';
import { PeriodType, type DisabledDate, type SelectedDate } from '@layerstack/utils';
import {
PeriodType,
type DisabledDate,
type SelectedDate,
startOfInterval,
} from '@layerstack/utils';

import Month from './Month.svelte';
import MonthListByYear from './MonthListByYear.svelte';
Expand All @@ -20,7 +24,10 @@
export let disabledDates: DisabledDate | undefined = undefined;

// @ts-expect-error
$: startOfMonth = selected?.[activeDate] ? startOfMonthFunc(selected[activeDate]) : undefined;
$: startOfMonth = selected?.[activeDate]
? // @ts-expect-error
startOfInterval('month', selected[activeDate])
: undefined;
</script>

{#if periodType === PeriodType.Month || periodType === PeriodType.Quarter}
Expand Down
46 changes: 22 additions & 24 deletions packages/svelte-ux/src/lib/components/Month.svelte
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
<script lang="ts">
import {
startOfDay as startOfDayFunc,
endOfDay as endOfDayFunc,
startOfMonth as startOfMonthFunc,
endOfMonth as endOfMonthFunc,
addMonths,
isSameDay,
isWithinInterval,
} from 'date-fns';
import { type SelectedDate, PeriodType, hasKeyOf } from '@layerstack/utils';
import { getMonthDaysByWeek } from '@layerstack/utils/date';
type SelectedDate,
PeriodType,
hasKeyOf,
startOfInterval,
endOfInterval,
intervalOffset,
} from '@layerstack/utils';
import { getMonthDaysByWeek, isDateWithin, isSameInterval } from '@layerstack/utils/date';

import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';

Expand All @@ -21,18 +19,18 @@
export let selected: SelectedDate = undefined;

export let startOfMonth =
(selected instanceof Date && startOfMonthFunc(selected)) ||
(selected instanceof Array && selected.length && startOfMonthFunc(selected[0])) ||
(selected instanceof Date && startOfInterval('month', selected)) ||
(selected instanceof Array && selected.length && startOfInterval('month', selected[0])) ||
(selected &&
hasKeyOf<{ from: Date }>(selected, 'from') &&
selected.from &&
startOfMonthFunc(selected.from)) ||
startOfMonthFunc(new Date());
startOfInterval('month', selected.from)) ||
startOfInterval('month', new Date());

const { format } = getSettings();
$: dateFormat = $format.settings.formats.dates;

$: endOfMonth = endOfMonthFunc(startOfMonth);
$: endOfMonth = endOfInterval('month', startOfMonth);
$: monthDaysByWeek = getMonthDaysByWeek(startOfMonth, dateFormat.weekStartsOn);

/**
Expand All @@ -56,27 +54,27 @@
return disabledDates instanceof Function
? disabledDates(date)
: disabledDates instanceof Date
? isSameDay(date, disabledDates)
? isSameInterval('day', date, disabledDates)
: disabledDates instanceof Array
? disabledDates.some((d) => isSameDay(date, d))
? disabledDates.some((d) => isSameInterval('day', date, d))
: disabledDates instanceof Object
? isWithinInterval(date, {
start: startOfDayFunc(disabledDates.from),
end: endOfDayFunc(disabledDates.to || disabledDates.from),
? isDateWithin(date, {
start: startOfInterval('day', disabledDates.from),
end: endOfInterval('day', disabledDates.to || disabledDates.from),
})
: false;
};

$: isDayHidden = (day: Date) => {
const isCurrentMonth = isWithinInterval(day, {
const isCurrentMonth = isDateWithin(day, {
start: startOfMonth,
end: endOfMonth,
});
return !isCurrentMonth && !showOutsideDays;
};

$: isDayFaded = (day: Date) => {
const isCurrentMonth = isWithinInterval(day, {
const isCurrentMonth = isDateWithin(day, {
start: startOfMonth,
end: endOfMonth,
});
Expand All @@ -102,7 +100,7 @@
<Button
icon={mdiChevronLeft}
class="p-2"
on:click={() => (startOfMonth = addMonths(startOfMonth, -1))}
on:click={() => (startOfMonth = intervalOffset('month', startOfMonth, -1))}
/>

<div class="flex flex-1 items-center justify-center">
Expand All @@ -114,7 +112,7 @@
<Button
icon={mdiChevronRight}
class="p-2"
on:click={() => (startOfMonth = addMonths(startOfMonth, 1))}
on:click={() => (startOfMonth = intervalOffset('month', startOfMonth, 1))}
/>
</div>
{/if}
Expand Down
21 changes: 14 additions & 7 deletions packages/svelte-ux/src/lib/components/MonthList.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import { isSameMonth, isWithinInterval, startOfMonth, endOfMonth } from 'date-fns';
import { type DisabledDate, type SelectedDate, PeriodType } from '@layerstack/utils';
import {
type DisabledDate,
type SelectedDate,
PeriodType,
isSameInterval,
startOfInterval,
endOfInterval,
isDateWithin,
} from '@layerstack/utils';
import { getMonths } from '@layerstack/utils/date';

import DateButton from './DateButton.svelte';
Expand All @@ -18,13 +25,13 @@
return disabledDates instanceof Function
? disabledDates(date)
: disabledDates instanceof Date
? isSameMonth(date, disabledDates)
? isSameInterval('month', date, disabledDates)
: disabledDates instanceof Array
? disabledDates.some((d) => isSameMonth(date, d))
? disabledDates.some((d) => isSameInterval('month', date, d))
: disabledDates instanceof Object
? isWithinInterval(date, {
start: startOfMonth(disabledDates.from),
end: endOfMonth(disabledDates.to || disabledDates.from),
? isDateWithin(date, {
start: startOfInterval('month', disabledDates.from),
end: endOfInterval('month', disabledDates.to || disabledDates.from),
})
: false;
};
Expand Down
7 changes: 3 additions & 4 deletions packages/svelte-ux/src/lib/components/MonthListByYear.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script lang="ts">
import { addYears, subYears } from 'date-fns';
import { getMinSelectedDate, getMaxSelectedDate } from '@layerstack/utils/date';
import type { SelectedDate } from '@layerstack/utils';
import { intervalOffset, type SelectedDate } from '@layerstack/utils';

import Button from './Button.svelte';
import MonthList from './MonthList.svelte';
Expand All @@ -15,14 +14,14 @@
minYear ??
(minDate
? minDate.getFullYear()
: subYears(getMinSelectedDate(selected) || new Date(), 2).getFullYear());
: intervalOffset('year', getMinSelectedDate(selected) || new Date(), -2).getFullYear());

let maxYear: number;
$: maxYear =
maxYear ??
(maxDate
? maxDate.getFullYear()
: addYears(getMaxSelectedDate(selected) || new Date(), 2).getFullYear());
: intervalOffset('year', getMaxSelectedDate(selected) || new Date(), 2).getFullYear());

$: years = Array.from({ length: maxYear - minYear + 1 }, (_, i) => minYear + i);

Expand Down
31 changes: 16 additions & 15 deletions packages/svelte-ux/src/lib/components/YearList.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import {
addYears,
subYears,
isSameYear,
isWithinInterval,
startOfYear,
endOfYear,
} from 'date-fns';
import { type DisabledDate, type SelectedDate, PeriodType } from '@layerstack/utils';
type DisabledDate,
type SelectedDate,
PeriodType,
intervalOffset,
isSameInterval,
startOfInterval,
endOfInterval,
isDateWithin,
} from '@layerstack/utils';
import { getMinSelectedDate, getMaxSelectedDate } from '@layerstack/utils/date';

import Button from './Button.svelte';
Expand All @@ -29,14 +30,14 @@
minYear ??
(minDate
? minDate.getFullYear()
: subYears(getMinSelectedDate(selected) || new Date(), 2).getFullYear());
: intervalOffset('year', getMinSelectedDate(selected) || new Date(), -2).getFullYear());

let maxYear: number;
$: maxYear =
maxYear ??
(maxDate
? maxDate.getFullYear()
: addYears(getMaxSelectedDate(selected) || new Date(), 2).getFullYear());
: intervalOffset('year', getMaxSelectedDate(selected) || new Date(), 2).getFullYear());

$: years = Array.from({ length: maxYear - minYear + 1 }, (_, i) => minYear + i) ?? [];

Expand All @@ -47,13 +48,13 @@
return disabledDates instanceof Function
? disabledDates(date)
: disabledDates instanceof Date
? isSameYear(date, disabledDates)
? isSameInterval('year', date, disabledDates)
: disabledDates instanceof Array
? disabledDates.some((d) => isSameYear(date, d))
? disabledDates.some((d) => isSameInterval('year', date, d))
: disabledDates instanceof Object
? isWithinInterval(date, {
start: startOfYear(disabledDates.from),
end: endOfYear(disabledDates.to || disabledDates.from),
? isDateWithin(date, {
start: startOfInterval('year', disabledDates.from),
end: endOfInterval('year', disabledDates.to || disabledDates.from),
})
: false;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import { addDays } from 'date-fns';
import { Button, DateField, getSettings } from 'svelte-ux';
import { mdiCalendarStart, mdiCalendarEnd } from '@mdi/js';
import { intervalOffset } from '@layerstack/utils';

import { Button, DateField, getSettings } from 'svelte-ux';
import Preview from '$lib/components/Preview.svelte';

const { localeSettings } = getSettings();
Expand Down Expand Up @@ -32,13 +32,13 @@
<Button on:click={() => (value = new Date())}>
{$localeSettings.dictionary.Date.PeriodDay.Current}
</Button>
<Button on:click={() => (value = addDays(new Date(), -1))}>
<Button on:click={() => (value = intervalOffset('day', new Date(), -1))}>
{$localeSettings.dictionary.Date.PeriodDay.Last}
</Button>
<Button on:click={() => (value = addDays(new Date(), -7))}>
<Button on:click={() => (value = intervalOffset('day', new Date(), -7))}>
{$localeSettings.dictionary.Date.PeriodWeek.Last}
</Button>
<Button on:click={() => (value = addDays(new Date(), 7))}>
<Button on:click={() => (value = intervalOffset('day', new Date(), 7))}>
<!-- TODO: Add to dictionary -->
Next week
</Button>
Expand Down
Loading