diff --git a/.eslintrc.json b/.eslintrc.json index 2c810eecc5..ce4b07ca2f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,4 +1,5 @@ { + "root": true, "env": { "browser": true, "es6": true, diff --git a/.prettierignore b/.prettierignore index 87f0191a94..72cdc75a23 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,4 +12,5 @@ packages/pro/coverage packages/account-portal/packages/ui/build packages/account-portal/packages/ui/.routify packages/account-portal/packages/server/build +packages/account-portal/packages/server/coverage **/*.ivm.bundle.js \ No newline at end of file diff --git a/lerna.json b/lerna.json index 94631c6820..6ba05e19ee 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.23.12", + "version": "2.24.1", "npmClient": "yarn", "packages": [ "packages/*", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index b4a9a3969c..1b3510cf3b 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -83,7 +83,6 @@ "dayjs": "^1.10.8", "easymde": "^2.16.1", "svelte-dnd-action": "^0.9.8", - "svelte-flatpickr": "3.2.3", "svelte-portal": "^1.0.0" }, "resolutions": { diff --git a/packages/bbui/src/Actions/click_outside.js b/packages/bbui/src/Actions/click_outside.js index ee478c70c0..124f43ff04 100644 --- a/packages/bbui/src/Actions/click_outside.js +++ b/packages/bbui/src/Actions/click_outside.js @@ -1,23 +1,25 @@ +// These class names will never trigger a callback if clicked, no matter what const ignoredClasses = [ ".download-js-link", - ".flatpickr-calendar", ".spectrum-Menu", ".date-time-popover", ] + +// These class names will only trigger a callback when clicked if the registered +// component is not nested inside them. For example, clicking inside a modal +// will not close the modal, or clicking inside a popover will not close the +// popover. const conditionallyIgnoredClasses = [ ".spectrum-Underlay", ".drawer-wrapper", ".spectrum-Popover", ] let clickHandlers = [] +let candidateTarget -/** - * Handle a body click event - */ +// Processes a "click outside" event and invokes callbacks if our source element +// is valid const handleClick = event => { - // Treat right clicks (context menu events) as normal clicks - const eventType = event.type === "contextmenu" ? "click" : event.type - // Ignore click if this is an ignored class if (event.target.closest('[data-ignore-click-outside="true"]')) { return @@ -30,11 +32,6 @@ const handleClick = event => { // Process handlers clickHandlers.forEach(handler => { - // Check that we're the right kind of click event - if (handler.allowedType && eventType !== handler.allowedType) { - return - } - // Check that the click isn't inside the target if (handler.element.contains(event.target)) { return @@ -52,17 +49,43 @@ const handleClick = event => { handler.callback?.(event) }) } -document.documentElement.addEventListener("click", handleClick, true) -document.documentElement.addEventListener("mousedown", handleClick, true) -document.documentElement.addEventListener("contextmenu", handleClick, true) + +// On mouse up we only trigger a "click outside" callback if we targetted the +// same element that we did on mouse down. This fixes all sorts of issues where +// we get annoying callbacks firing when we drag to select text. +const handleMouseUp = e => { + if (candidateTarget === e.target) { + handleClick(e) + } + candidateTarget = null +} + +// On mouse down we store which element was targetted for comparison later +const handleMouseDown = e => { + // Only handle the primary mouse button here. + // We handle context menu (right click) events in another handler. + if (e.button !== 0) { + return + } + candidateTarget = e.target + + // Clear any previous listeners in case of multiple down events, and register + // a single mouse up listener + document.removeEventListener("mouseup", handleMouseUp) + document.addEventListener("mouseup", handleMouseUp, true) +} + +// Global singleton listeners for our events +document.addEventListener("mousedown", handleMouseDown) +document.addEventListener("contextmenu", handleClick) /** * Adds or updates a click handler */ -const updateHandler = (id, element, anchor, callback, allowedType) => { +const updateHandler = (id, element, anchor, callback) => { let existingHandler = clickHandlers.find(x => x.id === id) if (!existingHandler) { - clickHandlers.push({ id, element, anchor, callback, allowedType }) + clickHandlers.push({ id, element, anchor, callback }) } else { existingHandler.callback = callback } @@ -89,8 +112,7 @@ export default (element, opts) => { const callback = newOpts?.callback || (typeof newOpts === "function" ? newOpts : null) const anchor = newOpts?.anchor || element - const allowedType = newOpts?.allowedType || "click" - updateHandler(id, element, anchor, callback, allowedType) + updateHandler(id, element, anchor, callback) } update(opts) return { diff --git a/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte index bc06530044..d7056db6d6 100644 --- a/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte @@ -4,6 +4,9 @@ import dayjs from "dayjs" import NumberInput from "./NumberInput.svelte" import { createEventDispatcher } from "svelte" + import isoWeek from "dayjs/plugin/isoWeek" + + dayjs.extend(isoWeek) export let value @@ -43,7 +46,7 @@ return [] } let monthEnd = monthStart.endOf("month") - let calendarStart = monthStart.startOf("week") + let calendarStart = monthStart.startOf("isoWeek") const numWeeks = Math.ceil((monthEnd.diff(calendarStart, "day") + 1) / 7) let mondays = [] diff --git a/packages/bbui/src/Layout/Page.svelte b/packages/bbui/src/Layout/Page.svelte index 2169a12459..62dd9cc909 100644 --- a/packages/bbui/src/Layout/Page.svelte +++ b/packages/bbui/src/Layout/Page.svelte @@ -7,11 +7,11 @@ export let narrower = false export let noPadding = false - let sidePanelVisble = false + let sidePanelVisible = false setContext("side-panel", { - open: () => (sidePanelVisble = true), - close: () => (sidePanelVisble = false), + open: () => (sidePanelVisible = true), + close: () => (sidePanelVisible = false), }) @@ -24,9 +24,9 @@
{ - sidePanelVisble = false + sidePanelVisible = false }} > diff --git a/packages/bbui/src/helpers.js b/packages/bbui/src/helpers.js index e100362359..90b447f3c1 100644 --- a/packages/bbui/src/helpers.js +++ b/packages/bbui/src/helpers.js @@ -154,7 +154,7 @@ export const parseDate = (value, { enableTime = true }) => { // schema flags export const stringifyDate = ( value, - { enableTime = true, timeOnly = false, ignoreTimezones = false } + { enableTime = true, timeOnly = false, ignoreTimezones = false } = {} ) => { if (!value) { return null @@ -210,7 +210,7 @@ const localeDateFormat = new Intl.DateTimeFormat() // Formats a dayjs date according to schema flags export const getDateDisplayValue = ( value, - { enableTime = true, timeOnly = false } + { enableTime = true, timeOnly = false } = {} ) => { if (!value?.isValid()) { return "" diff --git a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/PromptUser.svelte b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/PromptUser.svelte index 85d395e4f4..b808733d08 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/PromptUser.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonActionEditor/actions/PromptUser.svelte @@ -1,8 +1,10 @@ @@ -281,7 +289,7 @@ url={navItem.url} subLinks={navItem.subLinks} internalLink={navItem.internalLink} - on:clickLink={() => (mobileOpen = false)} + on:clickLink={handleClickLink} leftNav={navigation === "Left"} {mobile} {navStateStore} @@ -316,10 +324,7 @@
diff --git a/packages/client/src/components/app/SidePanel.svelte b/packages/client/src/components/app/SidePanel.svelte index 825b401bb8..bff5a78837 100644 --- a/packages/client/src/components/app/SidePanel.svelte +++ b/packages/client/src/components/app/SidePanel.svelte @@ -5,6 +5,9 @@ const { styleable, sidePanelStore, builderStore, dndIsDragging } = getContext("sdk") + export let onClose + export let ignoreClicksOutside + // Automatically show and hide the side panel when inside the builder. // For some unknown reason, svelte reactivity breaks if we reference the // reactive variable "open" inside the following expression, or if we define @@ -26,6 +29,10 @@ } } + // $: { + + // } + // Derive visibility $: open = $sidePanelStore.contentId === $component.id @@ -36,10 +43,17 @@ let renderKey = null $: { if (open) { + sidePanelStore.actions.setIgnoreClicksOutside(ignoreClicksOutside) renderKey = Math.random() } } + const handleSidePanelClose = async () => { + if (onClose) { + await onClose() + } + } + const showInSidePanel = (el, visible) => { const update = visible => { const target = document.getElementById("side-panel-container") @@ -51,6 +65,7 @@ } else { if (target.contains(node)) { target.removeChild(node) + handleSidePanelClose() } } } diff --git a/packages/client/src/components/app/forms/DateTimeField.svelte b/packages/client/src/components/app/forms/DateTimeField.svelte index 8ab8e65a15..499f0443cb 100644 --- a/packages/client/src/components/app/forms/DateTimeField.svelte +++ b/packages/client/src/components/app/forms/DateTimeField.svelte @@ -49,7 +49,6 @@ readonly={fieldState.readonly} error={fieldState.error} id={fieldState.fieldId} - appendTo={document.getElementById("flatpickr-root")} {enableTime} {timeOnly} {time24hr} diff --git a/packages/client/src/components/app/forms/validation.js b/packages/client/src/components/app/forms/validation.js index cdedd85cf2..bdd7213cb0 100644 --- a/packages/client/src/components/app/forms/validation.js +++ b/packages/client/src/components/app/forms/validation.js @@ -1,5 +1,6 @@ -import flatpickr from "flatpickr" +import dayjs from "dayjs" import { FieldTypes } from "../../../constants" +import { Helpers } from "@budibase/bbui" /** * Creates a validation function from a combination of schema-level constraints @@ -81,7 +82,7 @@ export const createValidatorFromConstraints = ( // Date constraint if (exists(schemaConstraints.datetime?.earliest)) { const limit = schemaConstraints.datetime.earliest - const limitString = flatpickr.formatDate(new Date(limit), "F j Y, H:i") + const limitString = Helpers.getDateDisplayValue(dayjs(limit)) rules.push({ type: "datetime", constraint: "minValue", @@ -91,7 +92,7 @@ export const createValidatorFromConstraints = ( } if (exists(schemaConstraints.datetime?.latest)) { const limit = schemaConstraints.datetime.latest - const limitString = flatpickr.formatDate(new Date(limit), "F j Y, H:i") + const limitString = Helpers.getDateDisplayValue(dayjs(limit)) rules.push({ type: "datetime", constraint: "maxValue", diff --git a/packages/client/src/stores/sidePanel.js b/packages/client/src/stores/sidePanel.js index 3b3b9f5f4d..b25914c484 100644 --- a/packages/client/src/stores/sidePanel.js +++ b/packages/client/src/stores/sidePanel.js @@ -3,6 +3,7 @@ import { writable, derived } from "svelte/store" export const createSidePanelStore = () => { const initialState = { contentId: null, + ignoreClicksOutside: true, } const store = writable(initialState) const derivedStore = derived(store, $store => { @@ -32,11 +33,18 @@ export const createSidePanelStore = () => { }, 50) } + const setIgnoreClicksOutside = bool => { + store.update(state => { + state.ignoreClicksOutside = bool + return state + }) + } return { subscribe: derivedStore.subscribe, actions: { open, close, + setIgnoreClicksOutside, }, } } diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index d883ee1b55..4de1012b01 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -240,6 +240,7 @@ const triggerAutomationHandler = async action => { const navigationHandler = action => { const { url, peek, externalNewTab } = action.parameters routeStore.actions.navigate(url, peek, externalNewTab) + closeSidePanelHandler() } const queryExecutionHandler = async action => { @@ -541,16 +542,22 @@ export const enrichButtonActions = (actions, context) => { // then execute the rest of the actions in the chain const result = await callback() if (result !== false) { - // Generate a new total context to pass into the next enrichment + // Generate a new total context for the next enrichment buttonContext.push(result) const newContext = { ...context, actions: buttonContext } - // Enrich and call the next button action if there is more than one action remaining + // Enrich and call the next button action if there is more + // than one action remaining const next = enrichButtonActions( actions.slice(i + 1), newContext ) - resolve(typeof next === "function" ? await next() : true) + if (typeof next === "function") { + // Pass the event context back into the new action chain + resolve(await next(eventContext)) + } else { + resolve(true) + } } else { resolve(false) } diff --git a/packages/frontend-core/src/components/FilterBuilder.svelte b/packages/frontend-core/src/components/FilterBuilder.svelte index 074c2dbd9b..1c569cb459 100644 --- a/packages/frontend-core/src/components/FilterBuilder.svelte +++ b/packages/frontend-core/src/components/FilterBuilder.svelte @@ -124,6 +124,7 @@ const fieldSchema = schemaFields.find(x => x.name === filter.field) filter.type = fieldSchema?.type filter.subtype = fieldSchema?.subtype + filter.formulaType = fieldSchema?.formulaType // Update external type based on field filter.externalType = getSchema(filter)?.externalType diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index af1f1b6b78..bb38c5094f 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -121,8 +121,14 @@ const onContextMenu = e => { e.preventDefault() - ui.actions.blur() - open = !open + + // The timeout allows time for clickoutside to close other open popvers + // before we show this one. Without the timeout, this popover closes again + // before it's even visible as clickoutside closes it. + setTimeout(() => { + ui.actions.blur() + open = !open + }, 10) } const sortAscending = () => { diff --git a/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte index 13430a3f7d..b11d42f52d 100644 --- a/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/MenuOverlay.svelte @@ -18,7 +18,7 @@ focusedCellAPI, focusedRowId, notifications, - isDatasourcePlus, + hasBudibaseIdentifiers, } = getContext("grid") let anchor @@ -82,7 +82,7 @@ copyToClipboard($focusedRow?._id)} on:click={menu.actions.close} > @@ -90,7 +90,7 @@ copyToClipboard($focusedRow?._rev)} on:click={menu.actions.close} > diff --git a/packages/frontend-core/src/components/grid/stores/datasource.js b/packages/frontend-core/src/components/grid/stores/datasource.js index 7b95fe7430..6c309380e6 100644 --- a/packages/frontend-core/src/components/grid/stores/datasource.js +++ b/packages/frontend-core/src/components/grid/stores/datasource.js @@ -75,14 +75,18 @@ export const deriveStores = context => { } ) - const isDatasourcePlus = derived(datasource, $datasource => { - return ["table", "viewV2"].includes($datasource?.type) + const hasBudibaseIdentifiers = derived(datasource, $datasource => { + let type = $datasource?.type + if (type === "provider") { + type = $datasource.value?.datasource?.type + } + return ["table", "viewV2", "link"].includes(type) }) return { schema, enrichedSchema, - isDatasourcePlus, + hasBudibaseIdentifiers, } } diff --git a/packages/frontend-core/src/components/grid/stores/menu.js b/packages/frontend-core/src/components/grid/stores/menu.js index ea32285a95..d7f0e21b84 100644 --- a/packages/frontend-core/src/components/grid/stores/menu.js +++ b/packages/frontend-core/src/components/grid/stores/menu.js @@ -17,6 +17,7 @@ export const createActions = context => { const open = (cellId, e) => { e.preventDefault() + e.stopPropagation() // Get DOM node for grid data wrapper to compute relative position to const gridNode = document.getElementById(gridID) diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index fc941ecc7f..6193b59d1d 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -83,7 +83,7 @@ export const createActions = context => { error, notifications, fetch, - isDatasourcePlus, + hasBudibaseIdentifiers, refreshing, } = context const instanceLoaded = writable(false) @@ -196,9 +196,16 @@ export const createActions = context => { // Handles validation errors from the rows API and updates local validation // state, storing error messages against relevant cells const handleValidationError = (rowId, error) => { + let errorString + if (typeof error === "string") { + errorString = error + } else if (typeof error?.message === "string") { + errorString = error.message + } + // If the server doesn't reply with a valid error, assume that the source // of the error is the focused cell's column - if (!error?.json?.validationErrors && error?.message) { + if (!error?.json?.validationErrors && errorString) { const focusedColumn = get(focusedCellId)?.split("-")[1] if (focusedColumn) { error = { @@ -261,7 +268,7 @@ export const createActions = context => { focusedCellId.set(`${rowId}-${erroredColumns[0]}`) } } else { - get(notifications).error(error?.message || "An unknown error occurred") + get(notifications).error(errorString || "An unknown error occurred") } } @@ -458,14 +465,14 @@ export const createActions = context => { } let rowsToAppend = [] let newRow - const $isDatasourcePlus = get(isDatasourcePlus) + const $hasBudibaseIdentifiers = get(hasBudibaseIdentifiers) for (let i = 0; i < newRows.length; i++) { newRow = newRows[i] // Ensure we have a unique _id. // This means generating one for non DS+, overwriting any that may already // exist as we cannot allow duplicates. - if (!$isDatasourcePlus) { + if (!$hasBudibaseIdentifiers) { newRow._id = Helpers.uuid() } @@ -510,7 +517,7 @@ export const createActions = context => { const cleanRow = row => { let clone = { ...row } delete clone.__idx - if (!get(isDatasourcePlus)) { + if (!get(hasBudibaseIdentifiers)) { delete clone._id } return clone diff --git a/packages/server/src/api/controllers/application.ts b/packages/server/src/api/controllers/application.ts index 6acdfcd465..e73058239b 100644 --- a/packages/server/src/api/controllers/application.ts +++ b/packages/server/src/api/controllers/application.ts @@ -52,6 +52,8 @@ import { FetchAppPackageResponse, DuplicateAppRequest, DuplicateAppResponse, + UpdateAppRequest, + UpdateAppResponse, } from "@budibase/types" import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts" import sdk from "../../sdk" @@ -450,7 +452,7 @@ export async function create(ctx: UserCtx) { // This endpoint currently operates as a PATCH rather than a PUT // Thus name and url fields are handled only if present export async function update( - ctx: UserCtx<{ name?: string; url?: string }, App> + ctx: UserCtx ) { const apps = (await dbCore.getAllApps({ dev: true })) as App[] // validation diff --git a/packages/server/src/api/controllers/table/utils.ts b/packages/server/src/api/controllers/table/utils.ts index cb2063abf9..dfbee2e4fc 100644 --- a/packages/server/src/api/controllers/table/utils.ts +++ b/packages/server/src/api/controllers/table/utils.ts @@ -129,6 +129,7 @@ export async function importToRows( for (let i = 0; i < data.length; i++) { let row = data[i] row._id = generateRowID(table._id!) + row.type = "row" row.tableId = table._id // We use a reference to table here and update it after input processing, diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 931510a88f..cec686ba74 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -61,9 +61,7 @@ describe.each([ } async function createRows(rows: Record[]) { - for (const row of rows) { - await config.api.row.save(table._id!, row) - } + await config.api.row.bulkImport(table._id!, { rows }) } class SearchAssertion { diff --git a/packages/server/src/sdk/app/rows/search/sqs.ts b/packages/server/src/sdk/app/rows/search/sqs.ts index 10cc5aa6c6..dabccc4b55 100644 --- a/packages/server/src/sdk/app/rows/search/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/sqs.ts @@ -131,11 +131,6 @@ export async function search( }, relationships, } - // make sure only rows returned - request.filters!.equal = { - ...request.filters?.equal, - type: "row", - } if (params.sort) { const sortField = table.schema[params.sort] diff --git a/packages/server/src/tests/utilities/api/application.ts b/packages/server/src/tests/utilities/api/application.ts index d28df223b5..bb9357c893 100644 --- a/packages/server/src/tests/utilities/api/application.ts +++ b/packages/server/src/tests/utilities/api/application.ts @@ -5,6 +5,8 @@ import { type FetchAppDefinitionResponse, type FetchAppPackageResponse, DuplicateAppResponse, + UpdateAppRequest, + UpdateAppResponse, } from "@budibase/types" import { Expectations, TestAPI } from "./base" import { AppStatus } from "../../../db/utils" @@ -109,11 +111,11 @@ export class ApplicationAPI extends TestAPI { update = async ( appId: string, - app: { name?: string; url?: string }, + app: UpdateAppRequest, expectations?: Expectations - ): Promise => { + ): Promise => { return await this._put(`/api/applications/${appId}`, { - fields: app, + body: app, expectations, }) } diff --git a/packages/types/src/api/web/application.ts b/packages/types/src/api/web/application.ts index 68c349be5a..bb4d8c7f72 100644 --- a/packages/types/src/api/web/application.ts +++ b/packages/types/src/api/web/application.ts @@ -44,3 +44,6 @@ export interface PublishResponse { status: string appUrl: string } + +export interface UpdateAppRequest extends Partial {} +export interface UpdateAppResponse extends App {} diff --git a/yarn.lock b/yarn.lock index 120f69cb43..3896be062b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11441,11 +11441,6 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatpickr@^4.5.2: - version "4.6.13" - resolved "https://registry.yarnpkg.com/flatpickr/-/flatpickr-4.6.13.tgz#8a029548187fd6e0d670908471e43abe9ad18d94" - integrity sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw== - flatted@^3.1.0: version "3.2.5" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" @@ -21082,20 +21077,6 @@ svelte-dnd-action@^0.9.8: postcss "^8.4.29" postcss-scss "^4.0.8" -svelte-flatpickr@3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.2.3.tgz#db5dd7ad832ef83262b45e09737955ad3d591fc8" - integrity sha512-PNkqK4Napx8nTvCwkaUXdnKo8dISThaxEOK+szTUXcY6H0dQM0TSyuoMaVWY2yX7pM+PN5cpCQCcVe8YvTRFSw== - dependencies: - flatpickr "^4.5.2" - -svelte-flatpickr@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/svelte-flatpickr/-/svelte-flatpickr-3.3.4.tgz#80b1ed2d6bc37df78b1660404e9326bfc0a206f4" - integrity sha512-i+QqJRs8zPRKsxv8r2GIk1fsb8cI3ozn3/aHXtViAoNKLy0j4PV7OSWavgEZC1wlAa34qi2hMkUh+vg6qt2DRA== - dependencies: - flatpickr "^4.5.2" - svelte-hmr@^0.15.1: version "0.15.3" resolved "https://registry.yarnpkg.com/svelte-hmr/-/svelte-hmr-0.15.3.tgz#df54ccde9be3f091bf5f18fc4ef7b8eb6405fbe6"