6eDesign / svelte-calendar

A lightweight datepicker with neat animations and a unique UX.
https://6edesign.github.io/svelte-calendar/
MIT License
537 stars 89 forks source link

3.0.19 and SvelteKit v1.0.0-next.169 - Failed to load module for SSR: /src/lib/stores/datepicker #128

Closed nstuyvesant closed 3 years ago

nstuyvesant commented 3 years ago

Related to #126 but for svelte-calendar@3.0.19.

Upgraded svelte-calender@2.0.4 to 3.0.19 in my SvelteKit v1.0.0-next.169 project.

What seems to have been resolved... https://github.com/6eDesign/svelte-calendar/issues/127 https://github.com/6eDesign/svelte-calendar/issues/125

Now getting:

500 failed to load module for ssr: /src/lib/stores/datepicker Error: failed to load module for ssr: /src/lib/stores/datepicker at instantiateModule (/Users/nates/dev/shy-svelte/node_modules/vite/dist/node/chunks/dep-874085d4.js:75059:15)

I do not have a /src/lib/stores folder in my project. Seems like svelte-calendar should be looking in node_modules/svelte-calendar/stores instead.

jonathangreenemeier-vizio commented 3 years ago

Hey @nstuyvesant - thanks. I'm working thru these issues with sveltekit compatibility as I, too, would like this to work well with the new kit platform.

I'm attempting to move the experimentation to a @next dist-tag right now. Will update you when/if things are resolved.

jonathangreenemeier-vizio commented 3 years ago

It is a bit odd, though. I am seeing a different error than you with 3.0.19 in a svelte-kit app. My error looks more like this:

11:39:24 AM [vite] Error when evaluating SSR module /src/routes/index.svelte:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: C:\Users\6E\kit-test\node_modules\svelte-calendar\index.js
require() of ES modules is not supported.
require() of C:\Users\6E\kit-test\node_modules\svelte-calendar\index.js from C:\Users\6E\kit-test\node_modules\vite\dist\node\chunks\dep-874085d4.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from C:\Users\6E\kit-test\node_modules\svelte-calendar\package.json.

    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Module.require (internal/modules/cjs/loader.js:952:19)
    at require (internal/modules/cjs/helpers.js:88:18)
    at nodeRequire (C:\Users\6E\kit-test\node_modules\vite\dist\node\chunks\dep-874085d4.js:75135:17)
    at ssrImport (C:\Users\6E\kit-test\node_modules\vite\dist\node\chunks\dep-874085d4.js:75077:20)
    at eval (/src/routes/index.svelte:7:37)
    at async instantiateModule (C:\Users\6E\kit-test\node_modules\vite\dist\node\chunks\dep-874085d4.js:75120:9)
jonathangreenemeier-vizio commented 3 years ago

@nstuyvesant - Could you please try updating to svelte-calendar@next and re-testing on a fresh svelte-kit app? You may need to update your svelte.config.js to the following (adding the optimizeDeps property):

import preprocess from 'svelte-preprocess';

/** @type {import('@sveltejs/kit').Config} */
const config = {
    // Consult https://github.com/sveltejs/svelte-preprocess
    // for more information about preprocessors
    preprocess: preprocess(),

    kit: {
        // hydrate the <div id="svelte"> element in src/app.html
        target: '#svelte',
    vite: {
      optimizeDeps: {
        include: ['just-throttle','dayjs'],
      },
    }
    }
};

export default config;
nstuyvesant commented 3 years ago

Will give it a try and let you know my results - thanks!

nstuyvesant commented 3 years ago

Installing svelte-calendar@next (3.0.26) with the additions to svelte.config.js's vite.optimizeDeps.include gets rid of the SSR error. When I run the dev server, I can bring up the datepicker but the behavior is different from 2.04 so I may need to look over the examples to get it to work the same way it did before. I can show the calendar and select the date which then enlarges but does not dismiss the calendar. I'm using Safari 14.1.2 on macOS 11.6 but Chrome behaved the same way.

jonathangreenemeier-vizio commented 3 years ago

I'm curious if you see the same behavior on the demo/docs site https://6edesign.github.io/svelte-calendar/ ?

I have not seen the behavior you're describing with the Datepicker component. The InlineCalendar component does currently behave this way, though.

6eDesign commented 3 years ago

If you can, @nstuyvesant, please provide the code you're using to implement the Datepicker so I can try to reproduce.

v3.0.0 does introduce many breaking changes but I think they're all for the better (either immediately or because they will aid in adding new functionality later [time pickers, date ranges, etc]).

6eDesign commented 3 years ago

Closing this issue for the time being. Feel free to open new issues with any additional problems you run into. Thanks for helping me test out the new v3 refactor.

nstuyvesant commented 3 years ago

Here's the Svelte component where I use the Datepicker. I wonder if the differences we experience may be related to typescript.

<script lang="ts">
  import { Datepicker } from 'svelte-calendar'
  import { Icon } from 'sveltestrap'
  import { DateDayOption, DateMonthOption, DateWeekdayOption, DateYearOption } from '../enums'
  import { classes, locations, teachers, schedule, findParentRecord } from '../stores'
  import AddToCalendar from '$lib/AddToCalendar.svelte'
  import DialogClass from '$lib/DialogClass.svelte'
  import DialogLocation from '$lib/DialogLocation.svelte'
  import DialogTeacher from '$lib/DialogTeacher.svelte'
  import ButtonScheduledClass from '$lib/ButtonScheduledClass.svelte'

  // Preset filters to generic values
  let classId = -1
  let day = -1
  let locationId = -1
  let teacherId = -1
  let timeOfDay = -1

  const start = new Date()
  start.setHours(0,0,0,0)
  let selectedDate = new Date(start)
  const dayRowDateOptions = { weekday: DateWeekdayOption.Long, month: DateMonthOption.Long, day: DateDayOption.Numeric }
  const datePickerOptions = { weekday: DateWeekdayOption.Long, month: DateMonthOption.Long, day: DateDayOption.Numeric, year: DateYearOption.Numeric }

  function addDays(days: number): void {
    const d = new Date(selectedDate)
    d.setDate(d.getDate() + days)
    if (d >= start) {
      selectedDate = new Date(d)
    }
  }

  const prevWeek = () => addDays(-7)
  const nextWeek = () => addDays(7)

  function getDateTime(targetDate, time: string): Date {
    const d = new Date(targetDate)
    d.setHours(+time.substring(0,2), +time.substring(3,5), 0, 0)
    return d
  }

  function inSession(eventDate: Date, startTime: string, endTime: string): boolean {
    const now = new Date()
    return now >= getDateTime(eventDate, startTime) && now <= getDateTime(eventDate, endTime)
  }

  function thisWeek(thisDate: Date): Date[] {
    const weekArray = []
    for (let day = 1; day <= 7; day++) {
      const d = new Date(thisDate)
      d.setDate(d.getDate() - (d.getDay() - day + 1))

      // If a date is in the past, move it forward to next week's date
      const today = new Date()
      today.setHours(0,0,0,0)
      if (d < today) d.setDate(d.getDate() + 7)

      weekArray.push(d)
    }
    return weekArray
  }

  function search(scheduledClass){
    return Object.keys(this).every(key => {
      if(this[key] == -1) {
        return true
      }
      return scheduledClass[key] == this[key]
    })
  }

  $: selectedDateString = selectedDate.toLocaleDateString('en-us', datePickerOptions)
  $: datesInView = thisWeek(selectedDate)
  $: filteredList = $schedule.filter(search, {
    day,
    classId,
    locationId,
    teacherId,
    timeOfDay
  })
</script>

<template>
  <table class="table table-striped">
    <thead>
      <tr>
        <th colspan="5" class="align-middle p-3">
          <div class="row justify-content-center">
            <div class="col-auto">
              <div class="btn-group" role="group" aria-label="Week Picker">
                <button on:click={prevWeek} class="btn btn-sm btn-secondary"><Icon name="caret-left"/></button>
                <Datepicker bind:selected={selectedDate} {start} formatted="today"><button class="btn btn-sm btn-secondary date-picker">{selectedDateString}</button></Datepicker>
                <button on:click={nextWeek} class="btn btn-sm btn-secondary"><Icon name="caret-right"/></button>
              </div>
            </div>
          </div>
        </th>
      </tr>
      <tr class="filters">
        <th class="ps-0" scope="col">
          When<br/>
          <select bind:value={day} class="form-select form-select-sm bg-secondary mb-1" aria-label="Day of the week">
            <optgroup>
              <option value={-1} selected>All days</option>
            </optgroup>
            <optgroup>
              <option value={1}>Sunday</option>
              <option value={2}>Monday</option>
              <option value={3}>Tuesday</option>
              <option value={4}>Wednesday</option>
              <option value={5}>Thursday</option>
              <option value={6}>Friday</option>
              <option value={7}>Saturday</option>
            </optgroup>
          </select>
          {#if false}
            <select bind:value={timeOfDay} class="form-select form-select-sm bg-secondary" aria-label="Time of day">
              <optgroup>
                <option value={-1} selected>All times</option>
              </optgroup>
              <optgroup>
                <option value="morning">Morning</option>
                <option value="afternoon">Afternoon</option>
                <option value="evening">Evening</option>
              </optgroup>
            </select>
          {/if}
        </th>
        <th scope="col">
          Class<br/>
          <select bind:value={classId} class="form-select form-select-sm bg-secondary" aria-label="Time of day">
            <optgroup>
              <option value={-1} selected>All</option>
            </optgroup>
            <optgroup>
              {#each $classes as course}
                <option value={course.id}>{course.name}</option>
              {/each}
            </optgroup>
          </select>
        </th>
        <th scope="col" colspan="2">
          Instructor<br/>
          <select bind:value={teacherId} class="form-select form-select-sm bg-secondary" aria-label="Instructor">
            <optgroup>
              <option value={-1} selected>All</option>
            </optgroup>
            <optgroup>
              {#each $teachers as teacher}
                <option value={teacher.id}>{teacher.name}</option>
              {/each}
            </optgroup>
          </select>
        </th>
        <th class="pe-0" scope="col">
          Location<br/>
          <select bind:value={locationId} class="form-select form-select-sm bg-secondary" aria-label="Instructor">
            <optgroup>
              <option value={-1} selected>All</option>
            </optgroup>
            <optgroup>
              {#each $locations as location}
                <option value={location.id}>{location.name}</option>
              {/each}
            </optgroup>
          </select>
        </th>
      </tr>
    </thead>
    <tbody class="border">
      {#each filteredList as scheduledClass, i}
        {#if i == 0 || (filteredList[i-1] !== undefined && scheduledClass.day != filteredList[i-1].day)}
          <tr>
            <th class="bg-secondary" colspan="5">{datesInView[scheduledClass.day-1].toLocaleString('en-US', dayRowDateOptions)}</th>
          </tr>
        {/if}
        <tr>
          <td class="align-middle">
            <AddToCalendar showTimes={true} subject={`${scheduledClass.className} with ${scheduledClass.teacherName}`} description={scheduledClass.classDescription} location={scheduledClass.locationName} eventDate={datesInView[scheduledClass.day-1]} startTime={scheduledClass.startTime} endTime={scheduledClass.endTime} />
          </td>
          <td class="align-middle">
            <DialogClass alt="Class details" classRecord={findParentRecord($classes, scheduledClass.classId)} canceled={scheduledClass.canceled}/>
          </td>
          <td class="align-middle">
            <DialogTeacher alt="Teacher details" teacher={findParentRecord($teachers, scheduledClass.teacherId)} canceled={scheduledClass.canceled}/>
          </td>
          <td class="align-middle text-center">
            <ButtonScheduledClass scheduledClassId={scheduledClass.id} canceled={scheduledClass.canceled} showJoin={scheduledClass.locationId == 5 && inSession(datesInView[scheduledClass.day-1], scheduledClass.startTime, scheduledClass.endTime)} />
          </td>
          <td class="align-middle">
            <DialogLocation alt="Location details" location={findParentRecord($locations, scheduledClass.locationId)} canceled={scheduledClass.canceled}/>
          </td>
        </tr>
      {/each}
    </tbody>
  </table>
</template>

<style lang="scss">
  .filters {
    vertical-align: top;
  }

  // Workaround because Datepicker has containing divs and doesn't look good in input group
  .date-picker {
    border-radius: 0;
  }
</style>
nstuyvesant commented 3 years ago

Just tried creating a new SvelteKit demo project with the adjustment to the svelte.config.js and adding the Datepicker to the /routes/index.svelte and it worked although I got a warning in VS Code that the formatted property is required (when using the documented example of <Datepicker /> so I used <Datepicker formatted="today" />.

6eDesign commented 3 years ago

Ok formatted does not need to be a required property. I can change that.

For your more complex example could you try following the custom trigger example in the docs? You're missing the in/out actions on your custom trigger.