From afffd4d234d551a50d1c1b41977bf8db87242a3d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 2 Nov 2023 15:37:18 +0000 Subject: [PATCH 001/338] Add initial version of new date picker without time support --- packages/bbui/package.json | 1 + .../bbui/src/Form/Core/BBDatePicker.svelte | 313 ++++++++++++++++++ packages/bbui/src/index.js | 1 + .../pages/builder/portal/apps/index.svelte | 31 ++ yarn.lock | 5 + 5 files changed, 351 insertions(+) create mode 100644 packages/bbui/src/Form/Core/BBDatePicker.svelte diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 78eed2b608..ccc59646b4 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -44,6 +44,7 @@ "@spectrum-css/avatar": "3.0.2", "@spectrum-css/button": "3.0.1", "@spectrum-css/buttongroup": "3.0.2", + "@spectrum-css/calendar": "3.0.2", "@spectrum-css/checkbox": "3.0.2", "@spectrum-css/dialog": "3.0.1", "@spectrum-css/divider": "1.0.3", diff --git a/packages/bbui/src/Form/Core/BBDatePicker.svelte b/packages/bbui/src/Form/Core/BBDatePicker.svelte new file mode 100644 index 0000000000..d159b3dcd6 --- /dev/null +++ b/packages/bbui/src/Form/Core/BBDatePicker.svelte @@ -0,0 +1,313 @@ + + +
+
+ {#if !!error} + + {/if} + +
+ +
+ + +
+
+
+ {activeMonth.format("MMMM YYYY")} +
+ + +
+
+ + + + {#each DaysOfWeek as day} + + {/each} + + + + {#each mondays as monday} + + {#each [0, 1, 2, 3, 4, 5, 6] as dayOffset} + {@const date = monday.add(dayOffset, "days")} + {@const outsideMonth = date.month() !== activeMonth.month()} + + {/each} + + {/each} + + +
+
+
+ + diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index cda6b5acbf..407a8c2cec 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -54,6 +54,7 @@ export { default as Notification } from "./Notification/Notification.svelte" export { default as SideNavigation } from "./SideNavigation/Navigation.svelte" export { default as SideNavigationItem } from "./SideNavigation/Item.svelte" export { default as DatePicker } from "./Form/DatePicker.svelte" +export { default as BBDatePicker } from "./Form/Core/BBDatePicker.svelte" export { default as Multiselect } from "./Form/Multiselect.svelte" export { default as Context } from "./context" export { default as Table } from "./Table/Table.svelte" diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index 59267f37e7..828e620dba 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -10,6 +10,8 @@ Notification, Body, Search, + DatePicker, + BBDatePicker, } from "@budibase/bbui" import Spinner from "components/common/Spinner.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte" @@ -202,9 +204,38 @@ notifications.error("Error getting init info") } }) + + let foo = "2023-11-14T15:00:00" +
+

Date only

+
+ (foo = e.detail)} + /> +
+

Date time

+
+ (foo = e.detail)} /> +
+

Date time no timezone

+
+ (foo = e.detail)} + /> +
+
+
+
+
+ {#each Object.keys(automationErrors || {}) as appId} Date: Thu, 2 Nov 2023 17:20:36 +0000 Subject: [PATCH 002/338] Add editable month and year picker to calendar --- packages/bbui/src/Actions/click_outside.js | 15 +-- .../bbui/src/Form/Core/BBDatePicker.svelte | 101 ++++++++++++++++-- .../pages/builder/portal/apps/index.svelte | 1 + 3 files changed, 100 insertions(+), 17 deletions(-) diff --git a/packages/bbui/src/Actions/click_outside.js b/packages/bbui/src/Actions/click_outside.js index 1961dca47c..e2cf38953c 100644 --- a/packages/bbui/src/Actions/click_outside.js +++ b/packages/bbui/src/Actions/click_outside.js @@ -1,8 +1,4 @@ -const ignoredClasses = [ - ".flatpickr-calendar", - ".spectrum-Popover", - ".download-js-link", -] +const ignoredClasses = [".flatpickr-calendar", ".download-js-link"] let clickHandlers = [] /** @@ -25,7 +21,14 @@ const handleClick = event => { return } - // Ignore clicks for modals, unless the handler is registered from a modal + // Ignore clicks for popovers, unless the handler is registered from one + const sourceInPopover = handler.anchor.closest(".spectrum-Popover") != null + const clickInPopover = event.target.closest(".spectrum-Popover") != null + if (clickInPopover && !sourceInPopover) { + return + } + + // Ignore clicks for modals, unless the handler is registered from one const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null const clickInModal = event.target.closest(".spectrum-Underlay") != null if (clickInModal && !sourceInModal) { diff --git a/packages/bbui/src/Form/Core/BBDatePicker.svelte b/packages/bbui/src/Form/Core/BBDatePicker.svelte index d159b3dcd6..7163314246 100644 --- a/packages/bbui/src/Form/Core/BBDatePicker.svelte +++ b/packages/bbui/src/Form/Core/BBDatePicker.svelte @@ -6,6 +6,7 @@ import Popover from "../../Popover/Popover.svelte" import dayjs from "dayjs" import { createEventDispatcher } from "svelte" + import Select from "../Select.svelte" export let id = null export let disabled = false @@ -30,22 +31,40 @@ "Saturday", "Sunday", ] + const MonthsOfYear = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ] let isOpen = false let anchor let calendar let today = dayjs() - let activeMonth + let calendarDate $: parsedValue = parseValue(value) $: displayValue = getDisplayValue(parsedValue) - $: activeMonth = dayjs(parsedValue || today).startOf("month") - $: mondays = getMondays(activeMonth) + $: calendarDate = dayjs(parsedValue || today).startOf("month") + $: mondays = getMondays(calendarDate) const clearDateOnBackspace = event => { + // Ignore if we're typing a value + if (document.activeElement?.tagName.toLowerCase() === "input") { + return + } if (["Backspace", "Clear", "Delete"].includes(event.key)) { handleChange(null) - calendar.hide() + calendar?.hide() } } @@ -129,6 +148,17 @@ dispatch("change", newValue) } + + const handleYearChange = e => { + let year = parseInt(e.target.value) + if (isNaN(year)) { + year = calendarDate.year() + } else { + year = Math.max(0, Math.min(9999, year)) + } + e.target.value = year + calendarDate = calendarDate.year(year) + }
- {activeMonth.format("MMMM YYYY")} +
+
From 1b5bb8dd044f19bbc15ac765265fd4a9f2b30271 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 2 Nov 2023 18:35:58 +0000 Subject: [PATCH 003/338] Add style improvements to new date picker --- packages/bbui/src/Form/Core/BBDatePicker.svelte | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/bbui/src/Form/Core/BBDatePicker.svelte b/packages/bbui/src/Form/Core/BBDatePicker.svelte index 7163314246..8c99caea36 100644 --- a/packages/bbui/src/Form/Core/BBDatePicker.svelte +++ b/packages/bbui/src/Form/Core/BBDatePicker.svelte @@ -69,6 +69,7 @@ } const onOpen = () => { + calendarDate = dayjs(parsedValue || today).startOf("month") isOpen = true if (useKeyboardShortcuts) { document.addEventListener("keyup", clearDateOnBackspace) @@ -307,13 +308,13 @@ aria-selected="false" aria-invalid="false" title={date.format("dddd, MMMM D, YYYY")} - on:click={outsideMonth ? null : handleChange(date)} + on:click={() => handleChange(date)} > {date.date()} @@ -350,9 +351,6 @@ .spectrum-Calendar { padding: 8px; } - .is-outsideMonth { - pointer-events: none; - } .spectrum-Calendar-title { display: flex; justify-content: center; @@ -362,6 +360,10 @@ .spectrum-Calendar-header button { border-radius: 4px; } + .spectrum-Calendar-date.is-outsideMonth { + visibility: visible; + color: var(--spectrum-global-color-gray-400); + } .month-selector :global(.spectrum-Picker), .year-selector { @@ -377,6 +379,7 @@ font-family: var(--font-sans); } .month-selector :global(.spectrum-Picker:hover), + .month-selector :global(.spectrum-Picker.is-open), .year-selector:hover { background: var(--spectrum-global-color-gray-200); } From f078039aa4d7c962d67948ddaace7b7cf2bb17b9 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 2 Nov 2023 20:34:40 +0000 Subject: [PATCH 004/338] Add time field and sanitise all typeable fields to prevent errors and improve experience --- .../bbui/src/Form/Core/BBDatePicker.svelte | 144 +++++++++++++++--- 1 file changed, 121 insertions(+), 23 deletions(-) diff --git a/packages/bbui/src/Form/Core/BBDatePicker.svelte b/packages/bbui/src/Form/Core/BBDatePicker.svelte index 8c99caea36..92dbb7f18e 100644 --- a/packages/bbui/src/Form/Core/BBDatePicker.svelte +++ b/packages/bbui/src/Form/Core/BBDatePicker.svelte @@ -7,6 +7,8 @@ import dayjs from "dayjs" import { createEventDispatcher } from "svelte" import Select from "../Select.svelte" + import Icon from "../../Icon/Icon.svelte" + import ActionButton from "../../ActionButton/ActionButton.svelte" export let id = null export let disabled = false @@ -150,16 +152,50 @@ dispatch("change", newValue) } - const handleYearChange = e => { - let year = parseInt(e.target.value) - if (isNaN(year)) { - year = calendarDate.year() - } else { - year = Math.max(0, Math.min(9999, year)) - } - e.target.value = year - calendarDate = calendarDate.year(year) + const handleMinuteChange = e => { + handleChange(parsedValue.minute(parseInt(e.target.value))) } + + const handleHourChange = e => { + handleChange(parsedValue.hour(parseInt(e.target.value))) + } + + const handleDateChange = date => { + // Select this date at midnight if no current date + if (!parsedValue) { + handleChange(date) + } + // Otherwise persist selected time + else { + handleChange( + parsedValue.year(date.year()).month(date.month()).date(date.date()) + ) + } + } + + const handleCalendarYearChange = e => { + calendarDate = calendarDate.year(parseInt(e.target.value)) + } + + const cleanNumber = ({ max, pad, fallback }) => { + return e => { + if (e.target.value) { + const value = parseInt(e.target.value) + if (isNaN(value)) { + e.target.value = fallback + } else { + e.target.value = Math.min(max, value).toString().padStart(pad, "0") + } + } else { + e.target.value = fallback + } + } + } + + // Sanitization utils + const cleanYear = cleanNumber({ max: 9999, pad: 0, fallback: today.year() }) + const cleanHour = cleanNumber({ max: 23, pad: 2, fallback: "00" }) + const cleanMinute = cleanNumber({ max: 59, pad: 2, fallback: "00" })
+ {#if parsedValue && enableTime} +
+ + : + +
+ {/if} From de0f87fa324e4bf80c94cac5026f4dbd64f4a470 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 09:34:54 +0000 Subject: [PATCH 005/338] Break new datetime picker into granular components --- .../bbui/src/Form/Core/BBDatePicker.svelte | 493 ------------------ .../Form/Core/DateTimePicker/Calendar.svelte | 206 ++++++++ .../Core/DateTimePicker/DateTimeField.svelte | 98 ++++ .../Core/DateTimePicker/DateTimePicker.svelte | 170 ++++++ .../Core/DateTimePicker/TimePicker.svelte | 65 +++ .../src/Form/Core/DateTimePicker/utils.js | 14 + packages/bbui/src/index.js | 2 +- .../pages/builder/portal/apps/index.svelte | 16 +- 8 files changed, 566 insertions(+), 498 deletions(-) delete mode 100644 packages/bbui/src/Form/Core/BBDatePicker.svelte create mode 100644 packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte create mode 100644 packages/bbui/src/Form/Core/DateTimePicker/DateTimeField.svelte create mode 100644 packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte create mode 100644 packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte create mode 100644 packages/bbui/src/Form/Core/DateTimePicker/utils.js diff --git a/packages/bbui/src/Form/Core/BBDatePicker.svelte b/packages/bbui/src/Form/Core/BBDatePicker.svelte deleted file mode 100644 index 92dbb7f18e..0000000000 --- a/packages/bbui/src/Form/Core/BBDatePicker.svelte +++ /dev/null @@ -1,493 +0,0 @@ - - -
-
- {#if !!error} - - {/if} - -
- -
- - -
-
-
-
- -
- - -
-
- - - - {#each DaysOfWeek as day} - - {/each} - - - - {#each mondays as monday} - - {#each [0, 1, 2, 3, 4, 5, 6] as dayOffset} - {@const date = monday.add(dayOffset, "days")} - {@const outsideMonth = date.month() !== calendarDate.month()} - - {/each} - - {/each} - - -
-
- {#if parsedValue && enableTime} -
- - : - -
- {/if} - - - diff --git a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte new file mode 100644 index 0000000000..f943a63254 --- /dev/null +++ b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte @@ -0,0 +1,206 @@ + + +
+
+
+
+ +
+ + +
+
+ + + + {#each DaysOfWeek as day} + + {/each} + + + + {#each mondays as monday} + + {#each [0, 1, 2, 3, 4, 5, 6] as dayOffset} + {@const date = monday.add(dayOffset, "days")} + {@const outsideMonth = date.month() !== calendarDate.month()} + + {/each} + + {/each} + + +
+
+ + diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimeField.svelte b/packages/bbui/src/Form/Core/DateTimePicker/DateTimeField.svelte new file mode 100644 index 0000000000..cf7d141109 --- /dev/null +++ b/packages/bbui/src/Form/Core/DateTimePicker/DateTimeField.svelte @@ -0,0 +1,98 @@ + + +
+
+ {#if !!error} + + {/if} + +
+ +
+ + diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte new file mode 100644 index 0000000000..a0e51a38fb --- /dev/null +++ b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte @@ -0,0 +1,170 @@ + + + + + + {#if isOpen} +
+ {#if showCalendar} + + {/if} + {#if showCalendar && showTime} +
+ {/if} + {#if showTime} + + {/if} +
+ {/if} +
+ + diff --git a/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte new file mode 100644 index 0000000000..60e1f313dc --- /dev/null +++ b/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte @@ -0,0 +1,65 @@ + + +
+ + : + +
+ + diff --git a/packages/bbui/src/Form/Core/DateTimePicker/utils.js b/packages/bbui/src/Form/Core/DateTimePicker/utils.js new file mode 100644 index 0000000000..953c3eb6c0 --- /dev/null +++ b/packages/bbui/src/Form/Core/DateTimePicker/utils.js @@ -0,0 +1,14 @@ +export const cleanInput = ({ max, pad, fallback }) => { + return e => { + if (e.target.value) { + const value = parseInt(e.target.value) + if (isNaN(value)) { + e.target.value = fallback + } else { + e.target.value = Math.min(max, value).toString().padStart(pad, "0") + } + } else { + e.target.value = fallback + } + } +} diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index 407a8c2cec..689cde05d7 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -54,7 +54,7 @@ export { default as Notification } from "./Notification/Notification.svelte" export { default as SideNavigation } from "./SideNavigation/Navigation.svelte" export { default as SideNavigationItem } from "./SideNavigation/Item.svelte" export { default as DatePicker } from "./Form/DatePicker.svelte" -export { default as BBDatePicker } from "./Form/Core/BBDatePicker.svelte" +export { default as DateTimePicker } from "./Form/Core/DateTimePicker/DateTimePicker.svelte" export { default as Multiselect } from "./Form/Multiselect.svelte" export { default as Context } from "./context" export { default as Table } from "./Table/Table.svelte" diff --git a/packages/builder/src/pages/builder/portal/apps/index.svelte b/packages/builder/src/pages/builder/portal/apps/index.svelte index 5fca9fb189..dbe4b9626d 100644 --- a/packages/builder/src/pages/builder/portal/apps/index.svelte +++ b/packages/builder/src/pages/builder/portal/apps/index.svelte @@ -11,7 +11,7 @@ Body, Search, DatePicker, - BBDatePicker, + DateTimePicker, } from "@budibase/bbui" import Spinner from "components/common/Spinner.svelte" import CreateAppModal from "components/start/CreateAppModal.svelte" @@ -212,7 +212,7 @@

Date only

- (foo = e.detail)} @@ -220,17 +220,25 @@

Date time

- (foo = e.detail)} /> + (foo = e.detail)} + />

Date time no timezone

- (foo = e.detail)} />
+

Time only

+
+ (foo = e.detail)} /> +



From a6bda4fce7aacf66a8f0d0712964615ce56d1cfd Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 09:35:24 +0000 Subject: [PATCH 006/338] Remove unused props --- .../bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte index a0e51a38fb..87fbe9f70b 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte @@ -14,8 +14,6 @@ export let placeholder = null export let timeOnly = false export let ignoreTimezones = false - export let range = false - export let flatpickr export let useKeyboardShortcuts = true const dispatch = createEventDispatcher() From 819df964bc59db0ae2ec026a44153a6efd917e08 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 11:18:03 +0000 Subject: [PATCH 007/338] Fix style issues in client apps and remove extraneous dependencies --- packages/bbui/package.json | 2 +- .../Form/Core/DateTimePicker/Calendar.svelte | 16 ++++++- .../Core/DateTimePicker/DateTimeField.svelte | 12 +++--- .../Core/DateTimePicker/DateTimePicker.svelte | 3 ++ packages/client/package.json | 9 +--- yarn.lock | 42 +++---------------- 6 files changed, 30 insertions(+), 54 deletions(-) diff --git a/packages/bbui/package.json b/packages/bbui/package.json index ccc59646b4..3601af74e9 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -44,7 +44,7 @@ "@spectrum-css/avatar": "3.0.2", "@spectrum-css/button": "3.0.1", "@spectrum-css/buttongroup": "3.0.2", - "@spectrum-css/calendar": "3.0.2", + "@spectrum-css/calendar": "3.2.7", "@spectrum-css/checkbox": "3.0.2", "@spectrum-css/dialog": "3.0.1", "@spectrum-css/divider": "1.0.3", diff --git a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte index f943a63254..f8386fe6da 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte @@ -1,5 +1,4 @@ + + + + diff --git a/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte index 60e1f313dc..a062c4ce0b 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte @@ -1,6 +1,7 @@
- : - @@ -45,7 +46,6 @@ diff --git a/packages/client/src/components/app/forms/NewDateTimeField.svelte b/packages/client/src/components/app/forms/NewDateTimeField.svelte new file mode 100644 index 0000000000..6bcd20d250 --- /dev/null +++ b/packages/client/src/components/app/forms/NewDateTimeField.svelte @@ -0,0 +1,53 @@ + + + + {#if fieldState} + + {/if} + From 96bc6fc789521b536e6a128da7a1f1acdb81452f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 17:05:27 +0000 Subject: [PATCH 009/338] Add better support for time only date strings --- .../Form/Core/DateTimePicker/DateTimePicker.svelte | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte index b04ae3cdb6..3ae2571166 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte @@ -56,8 +56,17 @@ } const parseValue = value => { - // Sanity check that we have a valid value - const parsedDate = dayjs(value) + let parsedDate + + // Attempt to parse as a time-only string if required + if (typeof value === "string" && timeOnly) { + parsedDate = dayjs(`0-${value}`) + } + + // Attempt to parse as normal if required + if (!parsedDate?.isValid()) { + parsedDate = dayjs(value) + } if (!parsedDate?.isValid()) { return null } From 93630b36e2a57b83d197c65d816d42eed849f2e1 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 17:07:31 +0000 Subject: [PATCH 010/338] Fix small inconsistency when picker is open for multiple minutes --- packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte | 2 +- packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte index d48afa1f35..ae32190e2e 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte @@ -34,7 +34,7 @@ const now = dayjs() let calendarDate - $: calendarDate = dayjs(value || now).startOf("month") + $: calendarDate = dayjs(value || dayjs()).startOf("month") $: mondays = getMondays(calendarDate) const getMondays = monthStart => { diff --git a/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte index a062c4ce0b..adf2a5e87a 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte @@ -6,9 +6,7 @@ export let value export let onChange - const now = dayjs() - - $: displayValue = value || now + $: displayValue = value || dayjs() const handleHourChange = e => { onChange(displayValue.hour(parseInt(e.target.value))) From e758582822f474aa7e2c3112fd34a8f022952b00 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 19:26:43 +0000 Subject: [PATCH 011/338] Add cross browser style improvements for datepicker --- .../Form/Core/DateTimePicker/Calendar.svelte | 13 ++++++++++--- .../Core/DateTimePicker/DateTimePicker.svelte | 18 ++++++++++++++---- .../Core/DateTimePicker/NumberInput.svelte | 1 + 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte index ae32190e2e..6491ba94ab 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte @@ -61,6 +61,10 @@ onChange(base.year(date.year()).month(date.month()).date(date.date())) } + export const setDate = date => { + calendarDate = date + } + const cleanYear = cleanInput({ max: 9999, pad: 0, fallback: now.year() }) @@ -85,6 +89,7 @@ value={calendarDate.year()} min={0} max={9999} + width={64} on:change={handleCalendarYearChange} on:input={cleanYear} /> @@ -173,6 +178,9 @@ .spectrum-Calendar { width: auto; } + .spectrum-Calendar-header { + width: auto; + } .spectrum-Calendar-title { display: flex; justify-content: flex-start; @@ -196,12 +204,11 @@ background: var(--spectrum-global-color-blue-400); } .spectrum-Calendar tr { + box-sizing: content-box; height: 40px; } .spectrum-Calendar-tableCell { - height: 32px; - width: 32px; - padding: 4px; + box-sizing: content-box; } .spectrum-Calendar-nextMonth, .spectrum-Calendar-prevMonth { diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte index 3ae2571166..cccfea612c 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte @@ -25,6 +25,7 @@ let isOpen = false let anchor let popover + let calendar $: parsedValue = parseValue(value) $: showCalendar = !timeOnly @@ -107,6 +108,12 @@ dispatch("change", newValue) } + + const setToNow = () => { + const now = dayjs() + calendar?.setDate(now) + handleChange(now) + } {#if showCalendar} - + {/if} @@ -164,7 +175,7 @@ display: flex; justify-content: space-between; align-items: center; - gap: 32px; + gap: 60px; } .footer.spaced { padding-top: 14px; @@ -175,6 +186,5 @@ display: flex; justify-content: flex-end; gap: 6px; - margin-right: 4px; } diff --git a/packages/bbui/src/Form/Core/DateTimePicker/NumberInput.svelte b/packages/bbui/src/Form/Core/DateTimePicker/NumberInput.svelte index 5b7c6dccce..8d7c72e94e 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/NumberInput.svelte +++ b/packages/bbui/src/Form/Core/DateTimePicker/NumberInput.svelte @@ -34,6 +34,7 @@ font-weight: bold; font-family: var(--font-sans); -webkit-font-smoothing: antialiased; + box-sizing: content-box; } input:focus, input:hover { From 78af50bafe9956793ace3f78c3aa77a9f367879b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 19:34:12 +0000 Subject: [PATCH 012/338] Use new datepicker except when a range is needed --- .../Calendar.svelte | 0 .../DateInput.svelte} | 0 .../Form/Core/DatePicker/DatePicker.svelte | 10 ++++ .../FlatpickrDatePicker.svelte} | 2 +- .../NumberInput.svelte | 0 .../SpectrumDatePicker.svelte} | 2 +- .../TimePicker.svelte | 0 .../{DateTimePicker => DatePicker}/utils.js | 0 packages/bbui/src/Form/Core/index.js | 2 +- packages/bbui/src/Form/DatePicker.svelte | 2 +- packages/bbui/src/index.js | 2 +- .../new/_components/componentStructure.json | 1 + .../pages/builder/portal/apps/index.svelte | 40 -------------- .../app/forms/NewDateTimeField.svelte | 53 ------------------- 14 files changed, 16 insertions(+), 98 deletions(-) rename packages/bbui/src/Form/Core/{DateTimePicker => DatePicker}/Calendar.svelte (100%) rename packages/bbui/src/Form/Core/{DateTimePicker/DateTimeField.svelte => DatePicker/DateInput.svelte} (100%) create mode 100644 packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte rename packages/bbui/src/Form/Core/{DatePicker.svelte => DatePicker/FlatpickrDatePicker.svelte} (99%) rename packages/bbui/src/Form/Core/{DateTimePicker => DatePicker}/NumberInput.svelte (100%) rename packages/bbui/src/Form/Core/{DateTimePicker/DateTimePicker.svelte => DatePicker/SpectrumDatePicker.svelte} (98%) rename packages/bbui/src/Form/Core/{DateTimePicker => DatePicker}/TimePicker.svelte (100%) rename packages/bbui/src/Form/Core/{DateTimePicker => DatePicker}/utils.js (100%) delete mode 100644 packages/client/src/components/app/forms/NewDateTimeField.svelte diff --git a/packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte similarity index 100% rename from packages/bbui/src/Form/Core/DateTimePicker/Calendar.svelte rename to packages/bbui/src/Form/Core/DatePicker/Calendar.svelte diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimeField.svelte b/packages/bbui/src/Form/Core/DatePicker/DateInput.svelte similarity index 100% rename from packages/bbui/src/Form/Core/DateTimePicker/DateTimeField.svelte rename to packages/bbui/src/Form/Core/DatePicker/DateInput.svelte diff --git a/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte new file mode 100644 index 0000000000..ec1514718c --- /dev/null +++ b/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte @@ -0,0 +1,10 @@ + + + diff --git a/packages/bbui/src/Form/Core/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/FlatpickrDatePicker.svelte similarity index 99% rename from packages/bbui/src/Form/Core/DatePicker.svelte rename to packages/bbui/src/Form/Core/DatePicker/FlatpickrDatePicker.svelte index 7ce15292be..8d92f7633d 100644 --- a/packages/bbui/src/Form/Core/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/FlatpickrDatePicker.svelte @@ -5,7 +5,7 @@ import "@spectrum-css/textfield/dist/index-vars.css" import "@spectrum-css/picker/dist/index-vars.css" import { createEventDispatcher } from "svelte" - import { uuid } from "../../helpers" + import { uuid } from "../../../helpers" export let id = null export let disabled = false diff --git a/packages/bbui/src/Form/Core/DateTimePicker/NumberInput.svelte b/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte similarity index 100% rename from packages/bbui/src/Form/Core/DateTimePicker/NumberInput.svelte rename to packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte diff --git a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte similarity index 98% rename from packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte rename to packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte index cccfea612c..d01097d357 100644 --- a/packages/bbui/src/Form/Core/DateTimePicker/DateTimePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte @@ -7,7 +7,7 @@ import { createEventDispatcher } from "svelte" import TimePicker from "./TimePicker.svelte" import Calendar from "./Calendar.svelte" - import DateTimeInput from "./DateTimeField.svelte" + import DateTimeInput from "./DateInput.svelte" import ActionButton from "../../../ActionButton/ActionButton.svelte" export let id = null diff --git a/packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/TimePicker.svelte similarity index 100% rename from packages/bbui/src/Form/Core/DateTimePicker/TimePicker.svelte rename to packages/bbui/src/Form/Core/DatePicker/TimePicker.svelte diff --git a/packages/bbui/src/Form/Core/DateTimePicker/utils.js b/packages/bbui/src/Form/Core/DatePicker/utils.js similarity index 100% rename from packages/bbui/src/Form/Core/DateTimePicker/utils.js rename to packages/bbui/src/Form/Core/DatePicker/utils.js diff --git a/packages/bbui/src/Form/Core/index.js b/packages/bbui/src/Form/Core/index.js index b0edf52748..4af2507147 100644 --- a/packages/bbui/src/Form/Core/index.js +++ b/packages/bbui/src/Form/Core/index.js @@ -8,7 +8,7 @@ export { default as CoreTextArea } from "./TextArea.svelte" export { default as CoreCombobox } from "./Combobox.svelte" export { default as CoreSwitch } from "./Switch.svelte" export { default as CoreSearch } from "./Search.svelte" -export { default as CoreDatePicker } from "./DatePicker.svelte" +export { default as CoreDatePicker } from "./DatePicker/DatePicker.svelte" export { default as CoreDropzone } from "./Dropzone.svelte" export { default as CoreStepper } from "./Stepper.svelte" export { default as CoreRichTextField } from "./RichTextField.svelte" diff --git a/packages/bbui/src/Form/DatePicker.svelte b/packages/bbui/src/Form/DatePicker.svelte index 04ce8b5467..49a66003fc 100644 --- a/packages/bbui/src/Form/DatePicker.svelte +++ b/packages/bbui/src/Form/DatePicker.svelte @@ -1,6 +1,6 @@ -
-

Date only

-
- (foo = e.detail)} - /> -
-

Date time

-
- (foo = e.detail)} - /> -
-

Date time no timezone

-
- (foo = e.detail)} - /> -
-

Time only

-
- (foo = e.detail)} /> -
-
-
-
- -
- {#each Object.keys(automationErrors || {}) as appId} - import { CoreDatePicker } from "@budibase/bbui" - import Field from "./Field.svelte" - - export let field - export let label - export let placeholder - export let disabled = false - export let enableTime = true - export let timeOnly = false - export let time24hr = false - export let ignoreTimezones = false - export let validation - export let defaultValue - export let onChange - - let fieldState - let fieldApi - - const handleChange = e => { - const changed = fieldApi.setValue(e.detail) - if (onChange && changed) { - onChange({ value: e.detail }) - } - } - - - - {#if fieldState} - - {/if} - From b2352157cdf1cec5b136c766660539649e0a25fe Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 3 Nov 2023 20:27:30 +0000 Subject: [PATCH 013/338] Improve grid integration with new datepicker --- packages/bbui/src/Actions/position_dropdown.js | 6 ++++-- .../src/Form/Core/DatePicker/DatePicker.svelte | 5 +++++ .../Core/DatePicker/SpectrumDatePicker.svelte | 17 ++++++++++++++++- .../src/components/grid/cells/DateCell.svelte | 15 +++++---------- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/bbui/src/Actions/position_dropdown.js b/packages/bbui/src/Actions/position_dropdown.js index f2018272f6..7c4f7e8a2a 100644 --- a/packages/bbui/src/Actions/position_dropdown.js +++ b/packages/bbui/src/Actions/position_dropdown.js @@ -39,11 +39,13 @@ export default function positionDropdown(element, opts) { styles = customUpdate(anchorBounds, elementBounds, styles) } else { // Determine vertical styles + const topSpace = anchorBounds.top + const bottomSpace = window.innerHeight - anchorBounds.bottom if (align === "right-outside") { styles.top = anchorBounds.top } else if ( - window.innerHeight - anchorBounds.bottom < - (maxHeight || 100) + window.innerHeight - anchorBounds.bottom < (maxHeight || 100) && + topSpace - bottomSpace > 100 ) { styles.top = anchorBounds.top - elementBounds.height - offset styles.maxHeight = maxHeight || 240 diff --git a/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte index ec1514718c..a03be24753 100644 --- a/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/DatePicker.svelte @@ -1,10 +1,15 @@ diff --git a/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte index d01097d357..97774da51b 100644 --- a/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte @@ -4,7 +4,7 @@ import "@spectrum-css/textfield/dist/index-vars.css" import Popover from "../../../Popover/Popover.svelte" import dayjs from "dayjs" - import { createEventDispatcher } from "svelte" + import { createEventDispatcher, onMount } from "svelte" import TimePicker from "./TimePicker.svelte" import Calendar from "./Calendar.svelte" import DateTimeInput from "./DateInput.svelte" @@ -19,6 +19,9 @@ export let timeOnly = false export let ignoreTimezones = false export let useKeyboardShortcuts = true + export let appendTo = null + export let api = null + export let align = "left" const dispatch = createEventDispatcher() @@ -114,6 +117,13 @@ calendar?.setDate(now) handleChange(now) } + + onMount(() => { + api = { + open: () => popover?.show(), + close: () => popover?.hide(), + } + }) {#if isOpen} diff --git a/packages/frontend-core/src/components/grid/cells/DateCell.svelte b/packages/frontend-core/src/components/grid/cells/DateCell.svelte index 53b159ee30..fae75277b4 100644 --- a/packages/frontend-core/src/components/grid/cells/DateCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DateCell.svelte @@ -10,7 +10,7 @@ export let readonly = false export let api - let flatpickr + let datePickerAPI let isOpen // Adding the 0- will turn a string like 00:00:00 into a valid ISO @@ -41,7 +41,7 @@ // Ensure we close flatpickr when unselected $: { if (!focused) { - flatpickr?.close() + datePickerAPI?.close() } } @@ -52,8 +52,8 @@ onMount(() => { api = { onKeyDown, - focus: () => flatpickr?.open(), - blur: () => flatpickr?.close(), + focus: () => datePickerAPI?.open(), + blur: () => datePickerAPI?.close(), isActive: () => isOpen, } }) @@ -75,12 +75,10 @@ onChange(e.detail)} - appendTo={document.documentElement} enableTime={!dateOnly} {timeOnly} - time24hr ignoreTimezones={schema.ignoreTimezones} - bind:flatpickr + bind:api={datePickerAPI} on:open={() => (isOpen = true)} on:close={() => (isOpen = false)} useKeyboardShortcuts={false} @@ -110,9 +108,6 @@ position: absolute; opacity: 0; } - .picker :global(.flatpickr) { - min-width: 0; - } .picker :global(.spectrum-Textfield-input) { width: 100%; } From f5369e7cddda61fde78e643309d367f24880382e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 2 Apr 2024 10:50:37 +0100 Subject: [PATCH 014/338] Update pro --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 3820c0c93a..6b62505be0 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 3820c0c93a3e448e10a60a9feb5396844b537ca8 +Subproject commit 6b62505be0c0b50a57b4f4980d86541ebdc86428 From 2e4e3eac60f7ed722df649c48d6de0105e91ec01 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 2 Apr 2024 10:59:30 +0100 Subject: [PATCH 015/338] Update styles of selected day --- packages/bbui/src/Form/Core/DatePicker/Calendar.svelte | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte index 6491ba94ab..75e8e193e6 100644 --- a/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte @@ -125,7 +125,6 @@
@@ -194,11 +193,13 @@ visibility: visible; color: var(--spectrum-global-color-gray-400); } - .spectrum-Calendar-date.is-today:before { + .spectrum-Calendar-date.is-today, + .spectrum-Calendar-date.is-today::before { border-color: var(--spectrum-global-color-gray-400); } - .spectrum-Calendar-date.is-today { - border-color: var(--spectrum-global-color-gray-400); + .spectrum-Calendar-date.is-today.is-selected, + .spectrum-Calendar-date.is-today.is-selected::before { + border-color: var(--spectrum-global-color-blue-700); } .spectrum-Calendar-date.is-selected:not(.is-range-selection) { background: var(--spectrum-global-color-blue-400); From d71766b9a4624cf7cd81bfb641281bfff35bac1f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 2 Apr 2024 11:01:50 +0100 Subject: [PATCH 016/338] Revert clickoutside --- packages/bbui/src/Actions/click_outside.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/bbui/src/Actions/click_outside.js b/packages/bbui/src/Actions/click_outside.js index 305ae65ac8..7873b7fd6c 100644 --- a/packages/bbui/src/Actions/click_outside.js +++ b/packages/bbui/src/Actions/click_outside.js @@ -1,4 +1,8 @@ -const ignoredClasses = [".flatpickr-calendar", ".download-js-link"] +const ignoredClasses = [ + ".flatpickr-calendar", + ".spectrum-Popover", + ".download-js-link", +] let clickHandlers = [] /** @@ -21,13 +25,6 @@ const handleClick = event => { return } - // Ignore clicks for popovers, unless the handler is registered from one - const sourceInPopover = handler.anchor.closest(".spectrum-Popover") != null - const clickInPopover = event.target.closest(".spectrum-Popover") != null - if (clickInPopover && !sourceInPopover) { - return - } - // Ignore clicks for modals, unless the handler is registered from one const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null const clickInModal = event.target.closest(".spectrum-Underlay") != null From 24f67617669d90442da2c4f8b2a019c8f4d59cc5 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 2 Apr 2024 11:02:10 +0100 Subject: [PATCH 017/338] Revert clickoutside --- packages/bbui/src/Actions/click_outside.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bbui/src/Actions/click_outside.js b/packages/bbui/src/Actions/click_outside.js index 7873b7fd6c..eafca657f3 100644 --- a/packages/bbui/src/Actions/click_outside.js +++ b/packages/bbui/src/Actions/click_outside.js @@ -25,7 +25,7 @@ const handleClick = event => { return } - // Ignore clicks for modals, unless the handler is registered from one + // Ignore clicks for modals, unless the handler is registered from a modal const sourceInModal = handler.anchor.closest(".spectrum-Underlay") != null const clickInModal = event.target.closest(".spectrum-Underlay") != null if (clickInModal && !sourceInModal) { From 4673fbeaaac1efd9b63d97b8e35001862c7e9d02 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 2 Apr 2024 11:08:37 +0100 Subject: [PATCH 018/338] Fix overflow --- packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte index 97774da51b..3a96044df4 100644 --- a/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte @@ -185,6 +185,7 @@ diff --git a/packages/bbui/src/Form/Core/DatePicker/FlatpickrDatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/FlatpickrDatePicker.svelte deleted file mode 100644 index ac163de3bb..0000000000 --- a/packages/bbui/src/Form/Core/DatePicker/FlatpickrDatePicker.svelte +++ /dev/null @@ -1,268 +0,0 @@ - - -{#key redrawOptions} - -
- - -
- -
- -
-
-{/key} -{#if open} - -
-{/if} - - diff --git a/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte deleted file mode 100644 index bd9fb8269e..0000000000 --- a/packages/bbui/src/Form/Core/DatePicker/SpectrumDatePicker.svelte +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - {#if isOpen} -
- {#if showCalendar} - - {/if} - -
- {/if} -
- - diff --git a/packages/bbui/src/Form/Core/DateRangePicker.svelte b/packages/bbui/src/Form/Core/DateRangePicker.svelte new file mode 100644 index 0000000000..9084942ba7 --- /dev/null +++ b/packages/bbui/src/Form/Core/DateRangePicker.svelte @@ -0,0 +1,69 @@ + + +
+ (fromDate = e.detail)} + enableTime={false} + /> +
+ +
+ (toDate = e.detail)} + enableTime={false} + /> +
+ + diff --git a/packages/bbui/src/Form/Core/index.js b/packages/bbui/src/Form/Core/index.js index 4af2507147..533a1956c5 100644 --- a/packages/bbui/src/Form/Core/index.js +++ b/packages/bbui/src/Form/Core/index.js @@ -9,6 +9,7 @@ export { default as CoreCombobox } from "./Combobox.svelte" export { default as CoreSwitch } from "./Switch.svelte" export { default as CoreSearch } from "./Search.svelte" export { default as CoreDatePicker } from "./DatePicker/DatePicker.svelte" +export { default as CoreDateRangePicker } from "./DateRangePicker.svelte" export { default as CoreDropzone } from "./Dropzone.svelte" export { default as CoreStepper } from "./Stepper.svelte" export { default as CoreRichTextField } from "./RichTextField.svelte" diff --git a/packages/bbui/src/Form/DatePicker.svelte b/packages/bbui/src/Form/DatePicker.svelte index 5e84947f0a..cf249e5bb8 100644 --- a/packages/bbui/src/Form/DatePicker.svelte +++ b/packages/bbui/src/Form/DatePicker.svelte @@ -15,18 +15,12 @@ export let placeholder = null export let appendTo = undefined export let ignoreTimezones = false - export let range = false export let helpText = null + const dispatch = createEventDispatcher() const onChange = e => { - if (range) { - // Flatpickr cant take two dates and work out what to display, needs to be provided a string. - // Like - "Date1 to Date2". Hence passing in that specifically from the array - value = e?.detail[1] - } else { - value = e.detail - } + value = e.detail dispatch("change", e.detail) } @@ -43,7 +37,6 @@ {time24hr} {appendTo} {ignoreTimezones} - {range} on:change={onChange} /> diff --git a/packages/bbui/src/Form/DateRangePicker.svelte b/packages/bbui/src/Form/DateRangePicker.svelte new file mode 100644 index 0000000000..39c2acb96a --- /dev/null +++ b/packages/bbui/src/Form/DateRangePicker.svelte @@ -0,0 +1,34 @@ + + + + + diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index 222e558675..f28e185305 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -3,13 +3,34 @@ import "./bbui.css" // Spectrum icons import "@spectrum-css/icon/dist/index-vars.css" -// Components +// Form components export { default as Input } from "./Form/Input.svelte" export { default as Stepper } from "./Form/Stepper.svelte" export { default as TextArea } from "./Form/TextArea.svelte" export { default as Select } from "./Form/Select.svelte" export { default as Combobox } from "./Form/Combobox.svelte" export { default as Dropzone } from "./Form/Dropzone.svelte" +export { default as DatePicker } from "./Form/DatePicker.svelte" +export { default as DateRangePicker } from "./Form/DateRangePicker.svelte" +export { default as Toggle } from "./Form/Toggle.svelte" +export { default as RadioGroup } from "./Form/RadioGroup.svelte" +export { default as Checkbox } from "./Form/Checkbox.svelte" +export { default as InputDropdown } from "./Form/InputDropdown.svelte" +export { default as PickerDropdown } from "./Form/PickerDropdown.svelte" +export { default as EnvDropdown } from "./Form/EnvDropdown.svelte" +export { default as Multiselect } from "./Form/Multiselect.svelte" +export { default as Search } from "./Form/Search.svelte" +export { default as RichTextField } from "./Form/RichTextField.svelte" +export { default as Slider } from "./Form/Slider.svelte" +export { default as File } from "./Form/File.svelte" + +// Core form components to be used elsewhere (standard components) +export * from "./Form/Core" + +// Fancy form components +export * from "./FancyForm" + +// Components export { default as Drawer } from "./Drawer/Drawer.svelte" export { default as DrawerContent } from "./Drawer/DrawerContent.svelte" export { default as Avatar } from "./Avatar/Avatar.svelte" @@ -21,12 +42,6 @@ export { default as ButtonGroup } from "./ButtonGroup/ButtonGroup.svelte" export { default as ClearButton } from "./ClearButton/ClearButton.svelte" export { default as Icon } from "./Icon/Icon.svelte" export { default as IconAvatar } from "./Icon/IconAvatar.svelte" -export { default as Toggle } from "./Form/Toggle.svelte" -export { default as RadioGroup } from "./Form/RadioGroup.svelte" -export { default as Checkbox } from "./Form/Checkbox.svelte" -export { default as InputDropdown } from "./Form/InputDropdown.svelte" -export { default as PickerDropdown } from "./Form/PickerDropdown.svelte" -export { default as EnvDropdown } from "./Form/EnvDropdown.svelte" export { default as DetailSummary } from "./DetailSummary/DetailSummary.svelte" export { default as Popover } from "./Popover/Popover.svelte" export { default as ProgressBar } from "./ProgressBar/ProgressBar.svelte" @@ -37,11 +52,6 @@ export { default as Page } from "./Layout/Page.svelte" export { default as Link } from "./Link/Link.svelte" export { default as Tooltip } from "./Tooltip/Tooltip.svelte" export { default as TempTooltip } from "./Tooltip/TempTooltip.svelte" -export { - default as AbsTooltip, - TooltipPosition, - TooltipType, -} from "./Tooltip/AbsTooltip.svelte" export { default as TooltipWrapper } from "./Tooltip/TooltipWrapper.svelte" export { default as Menu } from "./Menu/Menu.svelte" export { default as MenuSection } from "./Menu/Section.svelte" @@ -53,9 +63,6 @@ export { default as NotificationDisplay } from "./Notification/NotificationDispl export { default as Notification } from "./Notification/Notification.svelte" export { default as SideNavigation } from "./SideNavigation/Navigation.svelte" export { default as SideNavigationItem } from "./SideNavigation/Item.svelte" -export { default as DatePicker } from "./Form/DatePicker.svelte" -export { default as DateTimePicker } from "./Form/Core/DatePicker/SpectrumDatePicker.svelte" -export { default as Multiselect } from "./Form/Multiselect.svelte" export { default as Context } from "./context" export { default as Table } from "./Table/Table.svelte" export { default as Tabs } from "./Tabs/Tabs.svelte" @@ -65,7 +72,6 @@ export { default as Tag } from "./Tags/Tag.svelte" export { default as TreeView } from "./TreeView/Tree.svelte" export { default as TreeItem } from "./TreeView/Item.svelte" export { default as Divider } from "./Divider/Divider.svelte" -export { default as Search } from "./Form/Search.svelte" export { default as Pagination } from "./Pagination/Pagination.svelte" export { default as Badge } from "./Badge/Badge.svelte" export { default as StatusLight } from "./StatusLight/StatusLight.svelte" @@ -77,15 +83,15 @@ export { default as CopyInput } from "./Input/CopyInput.svelte" export { default as BannerDisplay } from "./Banner/BannerDisplay.svelte" export { default as MarkdownEditor } from "./Markdown/MarkdownEditor.svelte" export { default as MarkdownViewer } from "./Markdown/MarkdownViewer.svelte" -export { default as RichTextField } from "./Form/RichTextField.svelte" export { default as List } from "./List/List.svelte" export { default as ListItem } from "./List/ListItem.svelte" export { default as IconSideNav } from "./IconSideNav/IconSideNav.svelte" export { default as IconSideNavItem } from "./IconSideNav/IconSideNavItem.svelte" -export { default as Slider } from "./Form/Slider.svelte" export { default as Accordion } from "./Accordion/Accordion.svelte" -export { default as File } from "./Form/File.svelte" export { default as OptionSelectDnD } from "./OptionSelectDnD/OptionSelectDnD.svelte" +export { default as AbsTooltip } from "./Tooltip/AbsTooltip.svelte" +export { TooltipPosition, TooltipType } from "./Tooltip/AbsTooltip.svelte" + // Renderers export { default as BoldRenderer } from "./Table/BoldRenderer.svelte" export { default as CodeRenderer } from "./Table/CodeRenderer.svelte" @@ -97,9 +103,6 @@ export { default as Heading } from "./Typography/Heading.svelte" export { default as Detail } from "./Typography/Detail.svelte" export { default as Code } from "./Typography/Code.svelte" -// Core form components to be used elsewhere (standard components) -export * from "./Form/Core" - // Actions export { default as autoResizeTextArea } from "./Actions/autoresize_textarea" export { default as positionDropdown } from "./Actions/position_dropdown" @@ -111,6 +114,3 @@ export { banner, BANNER_TYPES } from "./Stores/banner" // Helpers export * as Helpers from "./helpers" - -// Fancy form components -export * from "./FancyForm" diff --git a/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte b/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte index bbf28b2fe8..212243aa76 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/backups/index.svelte @@ -1,7 +1,7 @@ @@ -207,7 +207,7 @@ View plans
- {:else if !backupData?.length && !loading && !filterOpt && !startDate} + {:else if !backupData?.length && !loading && !filterOpt && !date}
BackupsDefault @@ -236,19 +236,19 @@ bind:value={filterOpt} />
- { - startDate = e.detail?.[0] - endDate = e.detail?.[1] + date = e.detail }} + enableTime={false} />
- +
diff --git a/packages/client/src/components/app/DateRangePicker.svelte b/packages/client/src/components/app/DateRangePicker.svelte deleted file mode 100644 index 5c710ad766..0000000000 --- a/packages/client/src/components/app/DateRangePicker.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - -
- Date: Mon, 15 Apr 2024 13:46:49 +0100 Subject: [PATCH 039/338] Revert "Revert "adds sidepanel open and close actions, and gives the user the option to disable click-outside closure of sidepanel"" --- packages/bbui/src/Layout/Page.svelte | 10 +++++----- packages/client/manifest.json | 16 +++++++++++++++- packages/client/src/components/app/Layout.svelte | 5 ++++- .../client/src/components/app/SidePanel.svelte | 14 ++++++++++++++ packages/client/src/stores/sidePanel.js | 9 +++++++++ 5 files changed, 47 insertions(+), 7 deletions(-) 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/client/manifest.json b/packages/client/manifest.json index 40abc7a9a0..c9e28e202b 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -6723,7 +6723,21 @@ "illegalChildren": ["section", "sidepanel"], "showEmptyState": false, "draggable": false, - "info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action." + "info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action.", + "sendEvents": true, + "settings": [ + { + "type": "boolean", + "key": "clickOutsideToClose", + "label": "Click outside to close", + "defaultValue": true + }, + { + "type": "event", + "key": "onSidePanelClose", + "label": "On side panel close" + } + ] }, "rowexplorer": { "block": true, diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index 8508e943ff..bae2bd0faf 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -73,7 +73,10 @@ $context.device.width, $context.device.height ) - $: autoCloseSidePanel = !$builderStore.inBuilder && $sidePanelStore.open + $: autoCloseSidePanel = + !$builderStore.inBuilder && + $sidePanelStore.open && + $sidePanelStore.clickOutsideToClose $: screenId = $builderStore.inBuilder ? `${$builderStore.screen?._id}-screen` : "screen" diff --git a/packages/client/src/components/app/SidePanel.svelte b/packages/client/src/components/app/SidePanel.svelte index 825b401bb8..624617ad69 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 sidePanelClose + export let clickOutsideToClose + // 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 @@ } } + $: { + sidePanelStore.actions.setSidepanelState(clickOutsideToClose) + } + // Derive visibility $: open = $sidePanelStore.contentId === $component.id @@ -40,6 +47,12 @@ } } + const handleSidePanelClose = async () => { + if (sidePanelClose) { + await sidePanelClose() + } + } + const showInSidePanel = (el, visible) => { const update = visible => { const target = document.getElementById("side-panel-container") @@ -51,6 +64,7 @@ } else { if (target.contains(node)) { target.removeChild(node) + handleSidePanelClose() } } } diff --git a/packages/client/src/stores/sidePanel.js b/packages/client/src/stores/sidePanel.js index 3b3b9f5f4d..df66eca01c 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, + clickOutsideToClose: true, } const store = writable(initialState) const derivedStore = derived(store, $store => { @@ -32,11 +33,19 @@ export const createSidePanelStore = () => { }, 50) } + const setSidepanelState = bool => { + clearTimeout(timeout) + store.update(state => { + state.clickOutsideToClose = bool + return state + }) + } return { subscribe: derivedStore.subscribe, actions: { open, close, + setSidepanelState, }, } } From ea3cd0cd9b456af44579474e2df6393e134308dc Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 11:27:27 +0100 Subject: [PATCH 040/338] Support reordering on mobile --- .../src/components/grid/cells/HeaderCell.svelte | 14 +++++++++----- .../src/components/grid/stores/reorder.js | 17 ++++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 657f618759..f0ba29b499 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -18,7 +18,6 @@ export let column export let idx - export let orderable = true const { reorder, @@ -66,6 +65,7 @@ $: resetSearchValue(column.name) $: searching = searchValue != null $: debouncedUpdateFilter(searchValue) + $: orderable = !column.primaryDisplay const getSortingLabels = type => { switch (type) { @@ -112,7 +112,7 @@ } const onMouseDown = e => { - if (e.button === 0 && orderable) { + if ((e.touches?.length || e.button === 0) && orderable) { timeout = setTimeout(() => { reorder.actions.startReordering(column.name, e) }, 200) @@ -120,7 +120,7 @@ } const onMouseUp = e => { - if (e.button === 0 && orderable) { + if ((e.touches?.length || e.button === 0) && orderable) { clearTimeout(timeout) } } @@ -258,6 +258,9 @@ Use as display column @@ -378,7 +382,7 @@ Move right diff --git a/packages/frontend-core/src/components/grid/stores/reorder.js b/packages/frontend-core/src/components/grid/stores/reorder.js index f820593174..7bf0423b23 100644 --- a/packages/frontend-core/src/components/grid/stores/reorder.js +++ b/packages/frontend-core/src/components/grid/stores/reorder.js @@ -40,6 +40,7 @@ export const createActions = context => { // Callback when dragging on a colum header and starting reordering const startReordering = (column, e) => { + console.log("start", column) const $visibleColumns = get(visibleColumns) const $bounds = get(bounds) const $stickyColumn = get(stickyColumn) @@ -55,6 +56,11 @@ export const createActions = context => { x: 0, column: $stickyColumn.name, }) + } else if (!$visibleColumns[0].primaryDisplay) { + breakpoints.unshift({ + x: 0, + column: null, + }) } // Update state @@ -69,6 +75,9 @@ export const createActions = context => { // Add listeners to handle mouse movement document.addEventListener("mousemove", onReorderMouseMove) document.addEventListener("mouseup", stopReordering) + document.addEventListener("touchmove", onReorderMouseMove) + document.addEventListener("touchend", stopReordering) + document.addEventListener("touchcancel", stopReordering) // Trigger a move event immediately so ensure a candidate column is chosen onReorderMouseMove(e) @@ -77,7 +86,7 @@ export const createActions = context => { // Callback when moving the mouse when reordering columns const onReorderMouseMove = e => { // Immediately handle the current position - const x = e.clientX + const x = e.clientX ?? e.touches?.[0]?.clientX reorder.update(state => ({ ...state, latestX: x, @@ -168,6 +177,9 @@ export const createActions = context => { // Remove event handlers document.removeEventListener("mousemove", onReorderMouseMove) document.removeEventListener("mouseup", stopReordering) + document.removeEventListener("touchmove", onReorderMouseMove) + document.removeEventListener("touchend", stopReordering) + document.removeEventListener("touchcancel", stopReordering) // Save column changes await columns.actions.saveChanges() @@ -185,8 +197,7 @@ export const createActions = context => { if (--targetIdx < sourceIdx) { targetIdx++ } - state.splice(targetIdx, 0, removed[0]) - return state.slice() + return state.toSpliced(targetIdx, 0, removed[0]) }) } From f8087f0fb3cc78f9846040d0a78dfbcce07930a0 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 11:45:54 +0100 Subject: [PATCH 041/338] Automatically unpin the grid sticky column when available space is restricted --- .../src/components/grid/cells/GridCell.svelte | 3 + .../grid/controls/HideColumnsButton.svelte | 1 + .../grid/overlays/ResizeOverlay.svelte | 1 + .../src/components/grid/stores/columns.js | 64 +++++++++++++------ .../src/components/grid/stores/reorder.js | 19 +++--- .../src/components/grid/stores/ui.js | 2 +- 6 files changed, 59 insertions(+), 31 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/GridCell.svelte b/packages/frontend-core/src/components/grid/cells/GridCell.svelte index 74d98ec130..32a9dea83b 100644 --- a/packages/frontend-core/src/components/grid/cells/GridCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/GridCell.svelte @@ -43,6 +43,9 @@ on:mouseup on:click on:contextmenu + on:touchstart + on:touchend + on:touchcancel {style} > {#if error} diff --git a/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte b/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte index 01c9dc648b..4e19e64297 100644 --- a/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte +++ b/packages/frontend-core/src/components/grid/controls/HideColumnsButton.svelte @@ -81,6 +81,7 @@ size="S" value={column.visible} on:change={e => toggleVisibility(column, e.detail)} + disabled={column.primaryDisplay} /> {/each}
diff --git a/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte index 1140962583..e96517a434 100644 --- a/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte @@ -21,6 +21,7 @@ class="resize-slider" class:visible={activeColumn === $stickyColumn.name} on:mousedown={e => resize.actions.startResizing($stickyColumn, e)} + on:touchstart={e => resize.actions.startResizing($stickyColumn, e)} on:dblclick={() => resize.actions.resetSize($stickyColumn)} style="left:{GutterWidth + $stickyColumn.width}px;" > diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index 31638e7c79..8ceaae105f 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -48,22 +48,28 @@ export const createStores = () => { export const deriveStores = context => { const { columns, stickyColumn } = context - // Derive if we have any normal columns - const hasNonAutoColumn = derived( + // Quick access to all columns + const allColumns = derived( [columns, stickyColumn], ([$columns, $stickyColumn]) => { let allCols = $columns || [] if ($stickyColumn) { allCols = [...allCols, $stickyColumn] } - const normalCols = allCols.filter(column => { - return !column.schema?.autocolumn - }) - return normalCols.length > 0 + return allCols } ) + // Derive if we have any normal columns + const hasNonAutoColumn = derived(allColumns, $allColumns => { + const normalCols = $allColumns.filter(column => { + return !column.schema?.autocolumn + }) + return normalCols.length > 0 + }) + return { + allColumns, hasNonAutoColumn, } } @@ -142,24 +148,26 @@ export const createActions = context => { } export const initialise = context => { - const { definition, columns, stickyColumn, enrichedSchema } = context + const { + definition, + columns, + stickyColumn, + allColumns, + enrichedSchema, + compact, + } = context // Merge new schema fields with existing schema in order to preserve widths - enrichedSchema.subscribe($enrichedSchema => { + const processColumns = $enrichedSchema => { if (!$enrichedSchema) { columns.set([]) stickyColumn.set(null) return } const $definition = get(definition) - const $columns = get(columns) + const $allColumns = get(allColumns) const $stickyColumn = get(stickyColumn) - - // Generate array of all columns to easily find pre-existing columns - let allColumns = $columns || [] - if ($stickyColumn) { - allColumns.push($stickyColumn) - } + const $compact = get(compact) // Find primary display let primaryDisplay @@ -171,7 +179,7 @@ export const initialise = context => { // Get field list let fields = [] Object.keys($enrichedSchema).forEach(field => { - if (field !== primaryDisplay) { + if ($compact || field !== primaryDisplay) { fields.push(field) } }) @@ -181,7 +189,7 @@ export const initialise = context => { fields .map(field => { const fieldSchema = $enrichedSchema[field] - const oldColumn = allColumns?.find(x => x.name === field) + const oldColumn = $allColumns?.find(x => x.name === field) return { name: field, label: fieldSchema.displayName || field, @@ -189,9 +197,18 @@ export const initialise = context => { width: fieldSchema.width || oldColumn?.width || DefaultColumnWidth, visible: fieldSchema.visible ?? true, order: fieldSchema.order ?? oldColumn?.order, + primaryDisplay: field === primaryDisplay, } }) .sort((a, b) => { + // If we don't have a pinned column then primary display will be in + // the normal columns list, and should be first + if (a.name === primaryDisplay) { + return -1 + } else if (b.name === primaryDisplay) { + return 1 + } + // Sort by order first const orderA = a.order const orderB = b.order @@ -214,12 +231,12 @@ export const initialise = context => { ) // Update sticky column - if (!primaryDisplay) { + if ($compact || !primaryDisplay) { stickyColumn.set(null) return } const stickySchema = $enrichedSchema[primaryDisplay] - const oldStickyColumn = allColumns?.find(x => x.name === primaryDisplay) + const oldStickyColumn = $allColumns?.find(x => x.name === primaryDisplay) stickyColumn.set({ name: primaryDisplay, label: stickySchema.displayName || primaryDisplay, @@ -228,6 +245,13 @@ export const initialise = context => { visible: true, order: 0, left: GutterWidth, + primaryDisplay: true, }) - }) + } + + // Process columns when schema changes + enrichedSchema.subscribe(processColumns) + + // Process columns when compact flag changes + compact.subscribe(() => processColumns(get(enrichedSchema))) } diff --git a/packages/frontend-core/src/components/grid/stores/reorder.js b/packages/frontend-core/src/components/grid/stores/reorder.js index 7bf0423b23..30a5c144de 100644 --- a/packages/frontend-core/src/components/grid/stores/reorder.js +++ b/packages/frontend-core/src/components/grid/stores/reorder.js @@ -40,7 +40,6 @@ export const createActions = context => { // Callback when dragging on a colum header and starting reordering const startReordering = (column, e) => { - console.log("start", column) const $visibleColumns = get(visibleColumns) const $bounds = get(bounds) const $stickyColumn = get(stickyColumn) @@ -167,13 +166,6 @@ export const createActions = context => { // Ensure auto-scrolling is stopped stopAutoScroll() - // Swap position of columns - let { sourceColumn, targetColumn } = get(reorder) - moveColumn(sourceColumn, targetColumn) - - // Reset state - reorder.set(reorderInitialState) - // Remove event handlers document.removeEventListener("mousemove", onReorderMouseMove) document.removeEventListener("mouseup", stopReordering) @@ -181,8 +173,15 @@ export const createActions = context => { document.removeEventListener("touchend", stopReordering) document.removeEventListener("touchcancel", stopReordering) - // Save column changes - await columns.actions.saveChanges() + // Ensure there's actually a change + let { sourceColumn, targetColumn } = get(reorder) + if (sourceColumn !== targetColumn) { + moveColumn(sourceColumn, targetColumn) + await columns.actions.saveChanges() + } + + // Reset state + reorder.set(reorderInitialState) } // Moves a column after another columns. diff --git a/packages/frontend-core/src/components/grid/stores/ui.js b/packages/frontend-core/src/components/grid/stores/ui.js index da0558bb5b..928f93f3e1 100644 --- a/packages/frontend-core/src/components/grid/stores/ui.js +++ b/packages/frontend-core/src/components/grid/stores/ui.js @@ -98,7 +98,7 @@ export const deriveStores = context => { // Derive whether we should use the compact UI, depending on width const compact = derived([stickyColumn, width], ([$stickyColumn, $width]) => { - return ($stickyColumn?.width || 0) + $width + GutterWidth < 1100 + return ($stickyColumn?.width || 0) + $width + GutterWidth < 800 }) return { From c11139914fadeb898fde3f07fc769fc281facaea Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 11:55:44 +0100 Subject: [PATCH 042/338] Add support for resizing columns on mobile --- .../src/components/grid/lib/utils.js | 7 +++++++ .../grid/overlays/ResizeOverlay.svelte | 1 + .../grid/overlays/ScrollOverlay.svelte | 16 +++++----------- .../src/components/grid/stores/reorder.js | 3 ++- .../src/components/grid/stores/resize.js | 14 ++++++++++++-- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/packages/frontend-core/src/components/grid/lib/utils.js b/packages/frontend-core/src/components/grid/lib/utils.js index c7c618e6f8..02e6d66e0e 100644 --- a/packages/frontend-core/src/components/grid/lib/utils.js +++ b/packages/frontend-core/src/components/grid/lib/utils.js @@ -20,3 +20,10 @@ export const getColumnIcon = column => { return result || "Text" } + +export const parseEventLocation = e => { + return { + x: e.clientX ?? e.touches?.[0]?.clientX, + y: e.clientY ?? e.touches?.[0]?.clientY, + } +} diff --git a/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte index e96517a434..e564108430 100644 --- a/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte @@ -33,6 +33,7 @@ class="resize-slider" class:visible={activeColumn === column.name} on:mousedown={e => resize.actions.startResizing(column, e)} + on:touchstart={e => resize.actions.startResizing(column, e)} on:dblclick={() => resize.actions.resetSize(column)} style={getStyle(column, offset, $scrollLeft)} > diff --git a/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte index 43a64f3fbd..e0ead3727c 100644 --- a/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte @@ -2,6 +2,7 @@ import { getContext } from "svelte" import { domDebounce } from "../../../utils/utils" import { DefaultRowHeight, ScrollBarSize } from "../lib/constants" + import { parseEventLocation } from "../lib/utils" const { scroll, @@ -53,17 +54,10 @@ } } - const getLocation = e => { - return { - y: e.touches?.[0]?.clientY ?? e.clientY, - x: e.touches?.[0]?.clientX ?? e.clientX, - } - } - // V scrollbar drag handlers const startVDragging = e => { e.preventDefault() - initialMouse = getLocation(e).y + initialMouse = parseEventLocation(e).y initialScroll = $scrollTop document.addEventListener("mousemove", moveVDragging) document.addEventListener("touchmove", moveVDragging) @@ -73,7 +67,7 @@ closeMenu() } const moveVDragging = domDebounce(e => { - const delta = getLocation(e).y - initialMouse + const delta = parseEventLocation(e).y - initialMouse const weight = delta / availHeight const newScrollTop = initialScroll + weight * $maxScrollTop scroll.update(state => ({ @@ -92,7 +86,7 @@ // H scrollbar drag handlers const startHDragging = e => { e.preventDefault() - initialMouse = getLocation(e).x + initialMouse = parseEventLocation(e).x initialScroll = $scrollLeft document.addEventListener("mousemove", moveHDragging) document.addEventListener("touchmove", moveHDragging) @@ -102,7 +96,7 @@ closeMenu() } const moveHDragging = domDebounce(e => { - const delta = getLocation(e).x - initialMouse + const delta = parseEventLocation(e).x - initialMouse const weight = delta / availWidth const newScrollLeft = initialScroll + weight * $maxScrollLeft scroll.update(state => ({ diff --git a/packages/frontend-core/src/components/grid/stores/reorder.js b/packages/frontend-core/src/components/grid/stores/reorder.js index 30a5c144de..84b297c461 100644 --- a/packages/frontend-core/src/components/grid/stores/reorder.js +++ b/packages/frontend-core/src/components/grid/stores/reorder.js @@ -1,4 +1,5 @@ import { get, writable, derived } from "svelte/store" +import { parseEventLocation } from "../lib/utils" const reorderInitialState = { sourceColumn: null, @@ -85,7 +86,7 @@ export const createActions = context => { // Callback when moving the mouse when reordering columns const onReorderMouseMove = e => { // Immediately handle the current position - const x = e.clientX ?? e.touches?.[0]?.clientX + const { x } = parseEventLocation(e) reorder.update(state => ({ ...state, latestX: x, diff --git a/packages/frontend-core/src/components/grid/stores/resize.js b/packages/frontend-core/src/components/grid/stores/resize.js index 2dc9e0784c..87b3912848 100644 --- a/packages/frontend-core/src/components/grid/stores/resize.js +++ b/packages/frontend-core/src/components/grid/stores/resize.js @@ -1,5 +1,6 @@ import { writable, get, derived } from "svelte/store" import { MinColumnWidth, DefaultColumnWidth } from "../lib/constants" +import { parseEventLocation } from "../lib/utils" const initialState = { initialMouseX: null, @@ -24,6 +25,8 @@ export const createActions = context => { // Starts resizing a certain column const startResizing = (column, e) => { + const { x } = parseEventLocation(e) + // Prevent propagation to stop reordering triggering e.stopPropagation() ui.actions.blur() @@ -39,7 +42,7 @@ export const createActions = context => { width: column.width, left: column.left, initialWidth: column.width, - initialMouseX: e.clientX, + initialMouseX: x, column: column.name, columnIdx, }) @@ -47,12 +50,16 @@ export const createActions = context => { // Add mouse event listeners to handle resizing document.addEventListener("mousemove", onResizeMouseMove) document.addEventListener("mouseup", stopResizing) + document.addEventListener("touchmove", onResizeMouseMove) + document.addEventListener("touchend", stopResizing) + document.addEventListener("touchcancel", stopResizing) } // Handler for moving the mouse to resize columns const onResizeMouseMove = e => { const { initialMouseX, initialWidth, width, columnIdx } = get(resize) - const dx = e.clientX - initialMouseX + const { x } = parseEventLocation(e) + const dx = x - initialMouseX const newWidth = Math.round(Math.max(MinColumnWidth, initialWidth + dx)) // Ignore small changes @@ -87,6 +94,9 @@ export const createActions = context => { resize.set(initialState) document.removeEventListener("mousemove", onResizeMouseMove) document.removeEventListener("mouseup", stopResizing) + document.removeEventListener("touchmove", onResizeMouseMove) + document.removeEventListener("touchend", stopResizing) + document.removeEventListener("touchcancel", stopResizing) // Persist width if it changed if ($resize.width !== $resize.initialWidth) { From 27f7b1cc15b90ce8e1e03dce291708e1957a2aea Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 14:32:57 +0100 Subject: [PATCH 043/338] Add multiple improvements for touch events to grids --- .../src/components/grid/cells/HeaderCell.svelte | 5 +++-- packages/frontend-core/src/components/grid/stores/reorder.js | 3 ++- packages/frontend-core/src/components/grid/stores/resize.js | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index f0ba29b499..8a10556da9 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -119,9 +119,10 @@ } } - const onMouseUp = e => { - if ((e.touches?.length || e.button === 0) && orderable) { + const onMouseUp = () => { + if (timeout) { clearTimeout(timeout) + timeout = null } } diff --git a/packages/frontend-core/src/components/grid/stores/reorder.js b/packages/frontend-core/src/components/grid/stores/reorder.js index 84b297c461..c068f82cba 100644 --- a/packages/frontend-core/src/components/grid/stores/reorder.js +++ b/packages/frontend-core/src/components/grid/stores/reorder.js @@ -34,6 +34,7 @@ export const createActions = context => { stickyColumn, ui, maxScrollLeft, + width, } = context let autoScrollInterval @@ -95,7 +96,7 @@ export const createActions = context => { // Check if we need to start auto-scrolling const $reorder = get(reorder) - const proximityCutoff = 140 + const proximityCutoff = Math.min(140, get(width) / 6) const speedFactor = 8 const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x) const leftProximity = Math.max(0, x - $reorder.gridLeft) diff --git a/packages/frontend-core/src/components/grid/stores/resize.js b/packages/frontend-core/src/components/grid/stores/resize.js index 87b3912848..157465e838 100644 --- a/packages/frontend-core/src/components/grid/stores/resize.js +++ b/packages/frontend-core/src/components/grid/stores/resize.js @@ -29,6 +29,7 @@ export const createActions = context => { // Prevent propagation to stop reordering triggering e.stopPropagation() + e.preventDefault() ui.actions.blur() // Find and cache index From 58b23a736fc080c997bba37fedb0e6365f060179 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 14:44:52 +0100 Subject: [PATCH 044/338] Remove add new row tooltip when caused by inline filters --- .../frontend-core/src/components/grid/layout/NewRow.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/layout/NewRow.svelte b/packages/frontend-core/src/components/grid/layout/NewRow.svelte index 66c42e5303..f5de870f7e 100644 --- a/packages/frontend-core/src/components/grid/layout/NewRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/NewRow.svelte @@ -30,6 +30,7 @@ refreshing, config, filter, + inlineFilters, columnRenderMap, } = getContext("grid") @@ -157,7 +158,11 @@ {#if !visible && !selectedRowCount && $config.canAddRows} From 0f7e576f19981c3f93b3e856f9953b3849c8478d Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 14:52:23 +0100 Subject: [PATCH 045/338] Revert unnecessary changes --- .../src/components/grid/stores/columns.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index 8ceaae105f..551eeb364f 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -48,28 +48,22 @@ export const createStores = () => { export const deriveStores = context => { const { columns, stickyColumn } = context - // Quick access to all columns - const allColumns = derived( + // Derive if we have any normal columns + const hasNonAutoColumn = derived( [columns, stickyColumn], ([$columns, $stickyColumn]) => { let allCols = $columns || [] if ($stickyColumn) { allCols = [...allCols, $stickyColumn] } - return allCols + const normalCols = allCols.filter(column => { + return !column.schema?.autocolumn + }) + return normalCols.length > 0 } ) - // Derive if we have any normal columns - const hasNonAutoColumn = derived(allColumns, $allColumns => { - const normalCols = $allColumns.filter(column => { - return !column.schema?.autocolumn - }) - return normalCols.length > 0 - }) - return { - allColumns, hasNonAutoColumn, } } From 5df34310d4f2018569be040beb9f5f29359d07d2 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Apr 2024 14:53:27 +0100 Subject: [PATCH 046/338] Redo changes --- .../src/components/grid/stores/columns.js | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/columns.js b/packages/frontend-core/src/components/grid/stores/columns.js index 551eeb364f..8ceaae105f 100644 --- a/packages/frontend-core/src/components/grid/stores/columns.js +++ b/packages/frontend-core/src/components/grid/stores/columns.js @@ -48,22 +48,28 @@ export const createStores = () => { export const deriveStores = context => { const { columns, stickyColumn } = context - // Derive if we have any normal columns - const hasNonAutoColumn = derived( + // Quick access to all columns + const allColumns = derived( [columns, stickyColumn], ([$columns, $stickyColumn]) => { let allCols = $columns || [] if ($stickyColumn) { allCols = [...allCols, $stickyColumn] } - const normalCols = allCols.filter(column => { - return !column.schema?.autocolumn - }) - return normalCols.length > 0 + return allCols } ) + // Derive if we have any normal columns + const hasNonAutoColumn = derived(allColumns, $allColumns => { + const normalCols = $allColumns.filter(column => { + return !column.schema?.autocolumn + }) + return normalCols.length > 0 + }) + return { + allColumns, hasNonAutoColumn, } } From ad10679115b22f07e055a21e96d4ff2ee4aa0a75 Mon Sep 17 00:00:00 2001 From: mikesealey Date: Wed, 17 Apr 2024 16:27:23 +0100 Subject: [PATCH 047/338] saving progress based on review reccomendations --- packages/client/manifest.json | 11 +++++------ packages/client/src/components/app/Layout.svelte | 2 +- packages/client/src/components/app/SidePanel.svelte | 4 ++-- packages/client/src/stores/sidePanel.js | 9 ++++----- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index c9e28e202b..cb85768adc 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -6724,18 +6724,17 @@ "showEmptyState": false, "draggable": false, "info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action.", - "sendEvents": true, "settings": [ { "type": "boolean", - "key": "clickOutsideToClose", - "label": "Click outside to close", - "defaultValue": true + "key": "ignoreClicksOutside", + "label": "Ignore clicks outside", + "defaultValue": false }, { "type": "event", - "key": "onSidePanelClose", - "label": "On side panel close" + "key": "onClose", + "label": "On close" } ] }, diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index bae2bd0faf..617658c754 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -76,7 +76,7 @@ $: autoCloseSidePanel = !$builderStore.inBuilder && $sidePanelStore.open && - $sidePanelStore.clickOutsideToClose + $sidePanelStore.ignoreClicksOutside $: screenId = $builderStore.inBuilder ? `${$builderStore.screen?._id}-screen` : "screen" diff --git a/packages/client/src/components/app/SidePanel.svelte b/packages/client/src/components/app/SidePanel.svelte index 624617ad69..8b0b395649 100644 --- a/packages/client/src/components/app/SidePanel.svelte +++ b/packages/client/src/components/app/SidePanel.svelte @@ -6,7 +6,7 @@ getContext("sdk") export let sidePanelClose - export let clickOutsideToClose + 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 @@ -30,7 +30,7 @@ } $: { - sidePanelStore.actions.setSidepanelState(clickOutsideToClose) + sidePanelStore.actions.setIgnoreClicksOutside(ignoreClicksOutside) } // Derive visibility diff --git a/packages/client/src/stores/sidePanel.js b/packages/client/src/stores/sidePanel.js index df66eca01c..b25914c484 100644 --- a/packages/client/src/stores/sidePanel.js +++ b/packages/client/src/stores/sidePanel.js @@ -3,7 +3,7 @@ import { writable, derived } from "svelte/store" export const createSidePanelStore = () => { const initialState = { contentId: null, - clickOutsideToClose: true, + ignoreClicksOutside: true, } const store = writable(initialState) const derivedStore = derived(store, $store => { @@ -33,10 +33,9 @@ export const createSidePanelStore = () => { }, 50) } - const setSidepanelState = bool => { - clearTimeout(timeout) + const setIgnoreClicksOutside = bool => { store.update(state => { - state.clickOutsideToClose = bool + state.ignoreClicksOutside = bool return state }) } @@ -45,7 +44,7 @@ export const createSidePanelStore = () => { actions: { open, close, - setSidepanelState, + setIgnoreClicksOutside, }, } } From 6c38d32549a79f04ea00f10e21c84f2c540796ff Mon Sep 17 00:00:00 2001 From: mikesealey Date: Thu, 18 Apr 2024 10:08:48 +0100 Subject: [PATCH 048/338] reinstates actions running when sidepanel closes --- packages/client/src/components/app/SidePanel.svelte | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/client/src/components/app/SidePanel.svelte b/packages/client/src/components/app/SidePanel.svelte index 8b0b395649..827ed2ab95 100644 --- a/packages/client/src/components/app/SidePanel.svelte +++ b/packages/client/src/components/app/SidePanel.svelte @@ -5,7 +5,7 @@ const { styleable, sidePanelStore, builderStore, dndIsDragging } = getContext("sdk") - export let sidePanelClose + export let onClose export let ignoreClicksOutside // Automatically show and hide the side panel when inside the builder. @@ -48,8 +48,8 @@ } const handleSidePanelClose = async () => { - if (sidePanelClose) { - await sidePanelClose() + if (onClose) { + await onClose() } } From 088c210de85e260c946d2b105687cd155c16d298 Mon Sep 17 00:00:00 2001 From: mikesealey Date: Thu, 18 Apr 2024 12:11:45 +0100 Subject: [PATCH 049/338] sets ignoreClickOutside to each side panel --- packages/client/src/components/app/Layout.svelte | 2 +- packages/client/src/components/app/SidePanel.svelte | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index 617658c754..bfbac8f4f1 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -76,7 +76,7 @@ $: autoCloseSidePanel = !$builderStore.inBuilder && $sidePanelStore.open && - $sidePanelStore.ignoreClicksOutside + !$sidePanelStore.ignoreClicksOutside $: screenId = $builderStore.inBuilder ? `${$builderStore.screen?._id}-screen` : "screen" diff --git a/packages/client/src/components/app/SidePanel.svelte b/packages/client/src/components/app/SidePanel.svelte index 827ed2ab95..bff5a78837 100644 --- a/packages/client/src/components/app/SidePanel.svelte +++ b/packages/client/src/components/app/SidePanel.svelte @@ -29,9 +29,9 @@ } } - $: { - sidePanelStore.actions.setIgnoreClicksOutside(ignoreClicksOutside) - } + // $: { + + // } // Derive visibility $: open = $sidePanelStore.contentId === $component.id @@ -43,6 +43,7 @@ let renderKey = null $: { if (open) { + sidePanelStore.actions.setIgnoreClicksOutside(ignoreClicksOutside) renderKey = Math.random() } } From 6bbdf0e4744e2a42283eeeb36d2c4cd0e9d0f2bc Mon Sep 17 00:00:00 2001 From: Dean Date: Thu, 18 Apr 2024 17:04:26 +0100 Subject: [PATCH 050/338] Bindings support for views and table row searches --- .../buttons/TableFilterButton.svelte | 71 ++++++++++++------- .../controls/FilterEditor/FilterDrawer.svelte | 1 + .../controls/FilterEditor/FilterUsers.svelte | 21 +++--- .../server/src/api/controllers/row/index.ts | 15 +++- .../src/api/controllers/row/utils/utils.ts | 41 ++++++++++- .../server/src/api/controllers/row/views.ts | 14 +++- 6 files changed, 122 insertions(+), 41 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte index 91456da655..26b6624160 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte @@ -1,7 +1,9 @@ - + {text} - - dispatch("change", tempValue)} - > -
- (tempValue = e.detail)} - /> -
-
-
- + + + (tempValue = e.detail)} + {bindings} + /> + diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte index 7f1ee8010d..74c081cd5b 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte @@ -304,6 +304,7 @@ OperatorOptions.ContainsAny.value, ].includes(filter.operator)} disabled={filter.noValue} + type={filter.valueType} /> {:else} diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterUsers.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterUsers.svelte index 88383ba170..4613b8c40f 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterUsers.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterUsers.svelte @@ -1,7 +1,6 @@ - option.email} - getOptionValue={option => option._id} - {disabled} -/> +
+ option.email} + getOptionValue={option => option._id} + {disabled} + /> +
diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index c3d1f2cb47..7f99105ea4 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -2,7 +2,7 @@ import stream from "stream" import archiver from "archiver" import { quotas } from "@budibase/pro" -import { objectStore } from "@budibase/backend-core" +import { objectStore, context } from "@budibase/backend-core" import * as internal from "./internal" import * as external from "./external" import { isExternalTableID } from "../../../integrations/utils" @@ -198,8 +198,21 @@ export async function destroy(ctx: UserCtx) { export async function search(ctx: Ctx) { const tableId = utils.getTableId(ctx) + // Current user context for bindable search + const { _id, _rev, firstName, lastName, email, status, roleId } = ctx.user + + await context.ensureSnippetContext() + + const enrichedQuery = await utils.enrichSearchContext( + { ...ctx.request.body.query }, + { + user: { _id, _rev, firstName, lastName, email, status, roleId }, + } + ) + const searchParams: RowSearchParams = { ...ctx.request.body, + query: enrichedQuery, tableId, } diff --git a/packages/server/src/api/controllers/row/utils/utils.ts b/packages/server/src/api/controllers/row/utils/utils.ts index f387a468cf..503f139783 100644 --- a/packages/server/src/api/controllers/row/utils/utils.ts +++ b/packages/server/src/api/controllers/row/utils/utils.ts @@ -7,6 +7,8 @@ import { FieldType, RelationshipsJson, Row, + SearchRowRequest, + SearchRowResponse, Table, UserCtx, } from "@budibase/types" @@ -22,7 +24,7 @@ import { getInternalRowId, } from "./basic" import sdk from "../../../../sdk" - +import { processStringSync } from "@budibase/string-templates" import validateJs from "validate.js" validateJs.extend(validateJs.validators.datetime, { @@ -187,3 +189,40 @@ export async function sqlOutputProcessing( export function isUserMetadataTable(tableId: string) { return tableId === InternalTables.USER_METADATA } + +export async function enrichSearchContext( + fields: Record, + inputs = {}, + helpers = true +): Promise> { + const enrichedQuery: Record = {} + if (!fields || !inputs) { + return enrichedQuery + } + const parameters = { ...inputs } + // enrich the fields with dynamic parameters + for (let key of Object.keys(fields)) { + if (fields[key] == null) { + continue + } + if (typeof fields[key] === "object") { + // enrich nested fields object + enrichedQuery[key] = await enrichSearchContext( + fields[key], + parameters, + helpers + ) + } else if (typeof fields[key] === "string") { + // enrich string value as normal + enrichedQuery[key] = processStringSync(fields[key], parameters, { + noEscaping: true, + noHelpers: !helpers, + escapeNewlines: true, + }) + } else { + enrichedQuery[key] = fields[key] + } + } + + return enrichedQuery +} diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index 2644446d82..18953ebe88 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -9,7 +9,8 @@ import { } from "@budibase/types" import { dataFilters } from "@budibase/shared-core" import sdk from "../../../sdk" -import { db } from "@budibase/backend-core" +import { db, context } from "@budibase/backend-core" +import { enrichSearchContext, userSearchFromContext } from "./utils" export async function searchView( ctx: UserCtx @@ -56,10 +57,19 @@ export async function searchView( }) } + // Current user search context. + const { _id, _rev, firstName, lastName, email, status, roleId } = ctx.user + + await context.ensureSnippetContext() + + const enrichedQuery = await enrichSearchContext(query, { + user: { _id, _rev, firstName, lastName, email, status, roleId }, + }) + const searchOptions: RequiredKeys & RequiredKeys> = { tableId: view.tableId, - query, + query: enrichedQuery, fields: viewFields, ...getSortOptions(body, view), limit: body.limit, From 66381ded2f6aa7cd8667043c409c69d6aa79248d Mon Sep 17 00:00:00 2001 From: theodelaporte Date: Fri, 19 Apr 2024 10:37:51 +0200 Subject: [PATCH 051/338] :memo: Add portuguese version of readme --- i18n/README.por.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 i18n/README.por.md diff --git a/i18n/README.por.md b/i18n/README.por.md new file mode 100644 index 0000000000..b23988cac5 --- /dev/null +++ b/i18n/README.por.md @@ -0,0 +1,88 @@ +

+ + Budibase + +

+

+ Budibase +

+ +

+ A plataforma low-code que você vai adorar usar +

+

+ Budibase é uma plataforma low-code open source e é a maneira mais fácil de criar ferramentas internas que aumentam a produtividade. +

+ +

+ 🤖 🎨 🚀 +

+
+ +

+ Budibase design ui +

+ +

+ + Todas as releases do GitHub + + + Release do GitHub (em ordem cronológica) + + + Siga @budibase + + Código de conduta + + + +

+ +

+ Começar + · + Documentação + · + Solicitações de melhoria + · + Reportar um bug + · + Suporte: Discussões +

+ +

+ +## ✨ Funcionalidades + +### Construa e implante um verdadeiro software +Ao contrário de outras plataformas, com Budibase você constrói e implanta aplicativos de página única. Os aplicativos Budibase são altamente performáticos e podem ser designados de forma responsiva, proporcionando assim aos seus usuários uma experiência excepcional. +

+ +### Fonte livre e extensível +Budibase é software livre - sob licença GPL v3. Isso deve lhe dar tranquilidade de que Budibase estará sempre por aqui. Você também pode codificar no Budibase ou bifurcá-lo e fazer alterações como quiser, tornando-o uma experiência amigável para desenvolvedores. +

+ +### Importar dados ou começar do zero +Budibase pode extrair dados de várias fontes, incluindo MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB ou uma API REST. E ao contrário de outras plataformas, com Budibase, você pode começar do zero e criar aplicativos de negócios sem nenhuma fonte de dados. [Solicitar uma nova fonte de dados](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Dados do Budibase +

+

+ +### Design e criação de aplicativos usando componentes pré-definidos. + +Budibase vem com componentes belamente projetados e poderosos que você pode usar como blocos de construção para construir sua interface do usuário. Também expomos muitas de suas opções de estilo CSS favoritas para que você possa ser mais criativo. [Solicitar um novo componente](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Design do Budibase +

+

+ +### Automatize processos, integre outras ferramentas e se conecte a webhooks +Economize tempo automatizando processos manuais e fluxos de trabalho. Seja conectando-se a webhooks ou automatizando e-mails, basta dizer ao Budibase o que fazer e deixá-lo trabalhar para você. Você pode facilmente [criar uma nova automação para o Budibase aqui](https://github.com/Budibase/automations) ou [Solicitar uma nova automação](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Automações do Budibase +

From a56fd679112f051cdd01ea275848ef575c963233 Mon Sep 17 00:00:00 2001 From: theodelaporte Date: Fri, 19 Apr 2024 10:38:52 +0200 Subject: [PATCH 052/338] :memo: add italian version of readme --- i18n/README.it.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 i18n/README.it.md diff --git a/i18n/README.it.md b/i18n/README.it.md new file mode 100644 index 0000000000..b9cca5a61d --- /dev/null +++ b/i18n/README.it.md @@ -0,0 +1,74 @@ +

+ + Budibase + +

+

+ Budibase +

+ +

+ La piattaforma low-code che amerai usare +

+

+ Budibase è una piattaforma low-code open source ed è il modo più facile per creare strumenti interni che aumentano la produttività. +

+ +

+ 🤖 🎨 🚀 +

+
+ +

+ Budibase design ui +

+ +

+ + Tutti i download da GitHub + + + Release di GitHub (in ordine cronologico) + + + Segui @budibase + + Codice di condotta + + + +

+ +

+ Iniziare + · + Documentazione + · + Richieste di miglioramento + · + Segnalare un bug + · + Supporto: Discussioni +

+ +

+ +## ✨ Funzionalità + +### Costruisci e distribuisci un vero software +A differenza di altre piattaforme, con Budibase si costruiscono e distribuiscono applicazioni single-page. Le applicazioni Budibase sono altamente performanti e possono essere progettate in modo responsivo, offrendo agli utenti un'esperienza eccezionale. +

+ +### Sorgente libero ed estensibile +Budibase è software libero - sotto licenza GPL v3. Questo dovrebbe darti la tranquillità che Budibase sarà sempre qui. Puoi anche codificare in Budibase o fare un fork e apportare modifiche come desideri, rendendolo un'esperienza amichevole per gli sviluppatori. +

+ +### Importare dati o partire da zero +Budibase può estrarre dati da varie fonti, tra cui MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB o un'API REST. E a differenza di altre piattaforme, con Budibase puoi partire da zero e creare applicazioni di business senza alcuna fonte dati. [Richiedi una nuova fonte di dati](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Dati di Budibase +

+

+ +### Progettare e creare applicazioni utilizzando component From 7c9c3b719675e1eb6cac64a804b0e71b9ba23e04 Mon Sep 17 00:00:00 2001 From: theodelaporte Date: Fri, 19 Apr 2024 10:39:44 +0200 Subject: [PATCH 053/338] :memo: add russian version of readme --- i18n/README.ru.md | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 i18n/README.ru.md diff --git a/i18n/README.ru.md b/i18n/README.ru.md new file mode 100644 index 0000000000..61e5a00550 --- /dev/null +++ b/i18n/README.ru.md @@ -0,0 +1,83 @@ +

+ + Budibase + +

+

+ Budibase +

+ +

+ Платформа low-code, которую вы будете любить использовать +

+

+ Budibase - это open source платформа low-code, которая является самым простым способом создания внутренних инструментов, повышающих производительность. +

+ +

+ 🤖 🎨 🚀 +

+
+ +

+ Budibase design ui +

+ +

+ + Все скачивания с GitHub + + + Релизы GitHub (в хронологическом порядке) + + + Подписаться на @budibase + + Кодекс поведения + + + +

+ +

+ Начать + · + Документация + · + Запросы на улучшение + · + Сообщить об ошибке + · + Поддержка: Обсуждения +

+ +

+ +## ✨ Функциональности + +### Создание и развертывание настоящего программного обеспечения +В отличие от других платформ, с Budibase вы создаете и развертываете одностраничные приложения. Приложения Budibase обладают высокой производительностью и могут быть разработаны с адаптивным дизайном, обеспечивая пользователям исключительный опыт. +

+ +### Свободный и расширяемый исходный код +Budibase - это свободное программное обеспечение под лицензией GPL v3. Это должно дать вам уверенность в том, что Budibase всегда будет доступен. Вы также можете кодировать в Budibase или создать его форк и вносить изменения по своему усмотрению, что делает его дружественным для разработчиков. +

+ +### Импорт данных или начало с нуля +Budibase может извлекать данные из различных источников, включая MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB или REST API. И в отличие от других платформ, с Budibase вы можете начать с нуля и создавать деловые приложения без каких-либо источников данных. [Запросить новый источник данных](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Данные Budibase +

+

+ +### Проектирование и создание приложений с помощью предварительно определенных компонентов +Budibase поставляется с красиво спроектированными и мощными компонентами, которые можно использовать как строительные блоки для создания вашего пользовательского интерфейса. Мы также предоставляем множество ваших любимых опций стилей CSS, чтобы вы могли проявить свою творческую мысль. [Запросить новый компонент](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Дизайн Budibase +

+

+ +### Автоматизация процессов, интеграция с другими инструментами и подключение веб-хуков +Экономьте время, автоматизируя ручные процессы и рабочие пот From 03d3151a65f20b68316fc76a5de1563f199b99d1 Mon Sep 17 00:00:00 2001 From: theodelaporte Date: Fri, 19 Apr 2024 10:41:54 +0200 Subject: [PATCH 054/338] :memo: match readme to each other --- i18n/README.it.md | 169 +++++++++++++++++++++++++++++++++++++++++---- i18n/README.por.md | 161 +++++++++++++++++++++++++++++++++++++----- i18n/README.ru.md | 160 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 438 insertions(+), 52 deletions(-) diff --git a/i18n/README.it.md b/i18n/README.it.md index b9cca5a61d..8972fcdb18 100644 --- a/i18n/README.it.md +++ b/i18n/README.it.md @@ -8,10 +8,10 @@

- La piattaforma low-code che amerai usare + La piattaforma low-code che amerai utilizzare

- Budibase è una piattaforma low-code open source ed è il modo più facile per creare strumenti interni che aumentano la produttività. + Budibase è una piattaforma low-code open source ed è il modo più semplice per creare strumenti interni che migliorano la produttività.

@@ -25,10 +25,10 @@

- Tutti i download da GitHub + GitHub tutte le release - Release di GitHub (in ordine cronologico) + GitHub release (ordine cronologico) Segui @budibase @@ -40,35 +40,174 @@

- Iniziare + Inizia · Documentazione · Richieste di miglioramento · - Segnalare un bug + Segnala un bug · Supporto: Discussioni



- ## ✨ Funzionalità -### Costruisci e distribuisci un vero software -A differenza di altre piattaforme, con Budibase si costruiscono e distribuiscono applicazioni single-page. Le applicazioni Budibase sono altamente performanti e possono essere progettate in modo responsivo, offrendo agli utenti un'esperienza eccezionale. +### Costruisci e distribuisci software reale +A differenza di altre piattaforme, con Budibase puoi costruire e distribuire applicazioni one-page. Le applicazioni Budibase sono altamente performanti e possono essere progettate in modo responsive, offrendo ai tuoi utenti un'esperienza eccezionale.

-### Sorgente libero ed estensibile -Budibase è software libero - sotto licenza GPL v3. Questo dovrebbe darti la tranquillità che Budibase sarà sempre qui. Puoi anche codificare in Budibase o fare un fork e apportare modifiche come desideri, rendendolo un'esperienza amichevole per gli sviluppatori. +### Sorgente aperto ed estensibile +Budibase è software open source - sotto licenza GPL v3. Questo dovrebbe rassicurarti sul fatto che Budibase sarà sempre lì. Puoi anche codificare in Budibase o fare fork e apportare modifiche a tuo piacimento, rendendolo un'esperienza amichevole per gli sviluppatori.

-### Importare dati o partire da zero -Budibase può estrarre dati da varie fonti, tra cui MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB o un'API REST. E a differenza di altre piattaforme, con Budibase puoi partire da zero e creare applicazioni di business senza alcuna fonte dati. [Richiedi una nuova fonte di dati](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). +### Importa dati o inizia da zero +Budibase può estrarre i suoi dati da diverse fonti, tra cui MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB o un'API REST. E a differenza di altre piattaforme, con Budibase puoi partire da zero e creare applicazioni aziendali senza alcuna fonte di dati. [Richiedi una nuova fonte di dati](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).

- Dati di Budibase + Dati Budibase



-### Progettare e creare applicazioni utilizzando component +### Progetta e crea applicazioni utilizzando componenti predefiniti. + +Budibase è dotato di componenti predefiniti belli e potenti che puoi utilizzare come mattoni per costruire la tua interfaccia utente. Esporremo anche molte delle tue opzioni di stile CSS preferite in modo che tu possa esprimere una creatività maggiore. [Richiedi un nuovo componente](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Design Budibase +

+

+ +### Automatizza processi, integra altri strumenti e collegati a webhook +Risparmia tempo automatizzando processi manuali e flussi di lavoro. Che si tratti di connettersi a webhook o automatizzare email, basta dire a Budibase cosa fare e lasciarlo lavorare per te. Puoi facilmente [creare una nuova automazione per Budibase qui](https://github.com/Budibase/automations) o [Richiedere una nuova automazione](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Automazioni Budibase +

+

+ +### Integrazione con i tuoi strumenti preferiti +Budibase si integra con vari strumenti popolari, consentendoti di creare applicazioni che si adattano perfettamente alla tua stack tecnologica. + +

+ Integrazioni Budibase +

+

+ +### Paradiso degli amministratori +Budibase è progettato per crescere. Con Budibase, puoi auto-ospitarti sulla tua infrastruttura e gestire globalmente utenti, home, SMTP, applicazioni, gruppi, aspetto e altro ancora. Puoi anche fornire agli utenti/gruppi un portale delle applicazioni e affidare la gestione degli utenti al responsabile del gruppo. + +- Guarda il video promozionale: https://youtu.be/xoljVpty_Kw + +


+ +## 🏁 Inizio + + + +Implementa Budibase self-hosted nella tua infrastruttura esistente, utilizzando Docker, Kubernetes e Digital Ocean. +Oppure utilizza Budibase Cloud se non hai bisogno di auto-ospitare e desideri iniziare rapidamente. + +### [Inizia con Budibase](https://budibase.com) + + +

+ +## 🎓 Imparare Budibase + +La documentazione Budibase [è qui](https://docs.budibase.com). +
+ + +

+ +## 💬 Comunità + +Se hai domande o vuoi discutere con altri utenti di Budibase e unirti alla nostra comunità, vai su: [Discussioni Github](https://github.com/Budibase/budibase/discussions) + +


+ + +## ❗ Codice di condotta + +Budibase si impegna a offrire a tutti un'esperienza accogliente, diversificata e priva di molestie. Ci aspettiamo che tutti i membri della comunità Budibase rispettino i principi del nostro [**Codice di condotta**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Grazie per la tua attenzione. +
+ + +

+ + +## 🙌 Contribuire a Budibase + +Che tu stia aprendo un rapporto di bug o creando una Pull request, ogni contributo è apprezzato e benvenuto. Se stai pensando di implementare una nuova funzionalità o modificare l'API, crea prima un Issue. In questo modo possiamo assicurarci che il tuo lavoro non sia inutile. + +### Non sai da dove cominciare ? +Un buon punto di partenza per contribuire è qui: [Progetti in corso](https://github.com/Budibase/budibase/projects/22). + +### Come è organizzato il repo ? +Budibase è un monorepo gestito da lerna. Lerna gestisce la costruzione e la pubblicazione dei pacchetti di Budibase. Ecco, a grandi linee, i pacchetti che compongono Budibase. + +- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - contiene il codice per l'applicazione svelte lato client di budibase builder. + +- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - Un modulo che viene eseguito nel browser e che è responsabile della lettura delle definizioni JSON e della creazione di applicazioni web viventi da esse. + +- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - Il server budibase. Questa applicazione Koa è responsabile del servizio del JS per le applicazioni builder e budibase, oltre a fornire l'API per l'interazione con il database e il filesystem. + +Per ulteriori informazioni, vedere [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md) + +

+ + +## 📝 Licenza + +Budibase è open source, con licenza [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). Le librerie client e dei componenti sono con licenza [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - quindi le applicazioni che crei possono essere utilizzate con licenza come desideri. + +

+ +## ⭐ Stargazers nel tempo + +[![Stargazers nel tempo](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase) + +Se riscontri problemi tra gli aggiornamenti del builder, utilizza la seguente guida [qui](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) per pulire il tuo ambiente. + +

+ +## Contributeurs ✨ + +Grazie a queste meravigliose persone ([emoji key](https://allcontributors.org/docs/en/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + +

Martin McKeaveney

💻 📖 ⚠️ 🚇

Michael Drury

📖 💻 ⚠️ 🚇

Andrew Kingston

📖 💻 ⚠️ 🎨

Michael Shanks

📖 💻 ⚠️

Kevin Åberg Kultalahti

📖 💻 ⚠️

Joe

📖 💻 🖋 🎨

Rory Powell

💻 📖 ⚠️

Peter Clement

💻 📖 ⚠️

Conor Mack

💻 📖

Dominic Cave

💻 📖

Rabonaire

💻 📖 🐛

Alex Yeung

📖 💻

Sam Woods

📖 💻 🚇

ScooterSwope

💻 📖
+ + + + + +Questo progetto segue il [convenant del contribuente](https://github.com/Budibase/budibase/blob/master/CODE_OF_CONDUCT.md). Ogni contributo è il benvenuto! + + + + + diff --git a/i18n/README.por.md b/i18n/README.por.md index b23988cac5..b8954dbe22 100644 --- a/i18n/README.por.md +++ b/i18n/README.por.md @@ -11,7 +11,7 @@ A plataforma low-code que você vai adorar usar

- Budibase é uma plataforma low-code open source e é a maneira mais fácil de criar ferramentas internas que aumentam a produtividade. + Budibase é uma plataforma low-code de código aberto e é a maneira mais fácil de criar ferramentas internas que melhoram a produtividade.

@@ -25,15 +25,15 @@

- Todas as releases do GitHub + GitHub todos os releases - Release do GitHub (em ordem cronológica) + GitHub release (por ordem cronológica) Siga @budibase - Código de conduta + Código de Conduta @@ -44,7 +44,7 @@ · Documentação · - Solicitações de melhoria + Solicitar melhorias · Reportar um bug · @@ -52,37 +52,160 @@



+## ✨ Recursos -## ✨ Funcionalidades - -### Construa e implante um verdadeiro software -Ao contrário de outras plataformas, com Budibase você constrói e implanta aplicativos de página única. Os aplicativos Budibase são altamente performáticos e podem ser designados de forma responsiva, proporcionando assim aos seus usuários uma experiência excepcional. +### Construa e implante um software real +Ao contrário de outras plataformas, com o Budibase você constrói e implanta aplicativos de uma página. Os aplicativos Budibase são altamente performáticos e podem ser designados de forma responsiva, proporcionando uma experiência excepcional aos seus usuários.

-### Fonte livre e extensível -Budibase é software livre - sob licença GPL v3. Isso deve lhe dar tranquilidade de que Budibase estará sempre por aqui. Você também pode codificar no Budibase ou bifurcá-lo e fazer alterações como quiser, tornando-o uma experiência amigável para desenvolvedores. +### Código-fonte livre e extensível +Budibase é software livre - sob a licença GPL v3. Isso deve lhe dar confiança de que o Budibase estará sempre disponível. Você também pode codificar no Budibase ou bifurcá-lo e fazer alterações conforme desejar, tornando-o amigável para desenvolvedores.

### Importar dados ou começar do zero -Budibase pode extrair dados de várias fontes, incluindo MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB ou uma API REST. E ao contrário de outras plataformas, com Budibase, você pode começar do zero e criar aplicativos de negócios sem nenhuma fonte de dados. [Solicitar uma nova fonte de dados](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). +Budibase pode extrair dados de várias fontes, incluindo MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB ou uma API REST. E ao contrário de outras plataformas, com o Budibase você pode começar do zero e criar aplicativos de negócios sem nenhuma fonte de dados. [Solicitar uma nova fonte de dados](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).

- Dados do Budibase + Dados Budibase



-### Design e criação de aplicativos usando componentes pré-definidos. - -Budibase vem com componentes belamente projetados e poderosos que você pode usar como blocos de construção para construir sua interface do usuário. Também expomos muitas de suas opções de estilo CSS favoritas para que você possa ser mais criativo. [Solicitar um novo componente](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). +### Projetar e criar aplicativos usando componentes pré-definidos +O Budibase vem com componentes lindamente projetados e poderosos que você pode usar como blocos de construção para criar sua interface do usuário. Também oferecemos muitas das suas opções de estilo CSS favoritas para que você possa mostrar sua criatividade. [Solicitar um novo componente](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).

- Design do Budibase + Design Budibase



-### Automatize processos, integre outras ferramentas e se conecte a webhooks +### Automatizar processos, integrar outras ferramentas e conectar webhooks Economize tempo automatizando processos manuais e fluxos de trabalho. Seja conectando-se a webhooks ou automatizando e-mails, basta dizer ao Budibase o que fazer e deixá-lo trabalhar para você. Você pode facilmente [criar uma nova automação para o Budibase aqui](https://github.com/Budibase/automations) ou [Solicitar uma nova automação](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).

- Automações do Budibase + Automações Budibase +

+

+ +### Integração com suas ferramentas favoritas +O Budibase se integra a várias ferramentas populares, permitindo que você crie aplicativos que se encaixam perfeitamente em sua pilha tecnológica. + +

+ Integrações Budibase +

+

+ +### Paraíso dos administradores +O Budibase é projetado para escalar. Com o Budibase, você pode se auto-hospedar em sua própria infraestrutura e gerenciar globalmente usuários, home, SMTP, aplicativos, grupos, aparência e muito mais. Você também pode fornecer aos usuários/grupos um portal de aplicativos e delegar o gerenciamento de usuários ao líder do grupo. + +- Assista ao vídeo promocional: https://youtu.be/xoljVpty_Kw + +


+ +## 🏁 Começar + + + +Implante o Budibase em auto-hospedagem em sua infraestrutura existente, usando Docker, Kubernetes e Digital Ocean. +Ou use o Budibase Cloud se você não precisar se auto-hospedar e quiser começar rapidamente. + +### [Começar com o Budibase](https://budibase.com) + + +

+ +## 🎓 Aprenda Budibase + +A documentação Budibase [está aqui](https://docs.budibase.com). +
+ + +

+ +## 💬 Comunidade + +Se você tiver alguma dúvida ou quiser conversar com outros usuários do Budibase e se juntar à nossa comunidade, visite [Discussões do Github](https://github.com/Budibase/budibase/discussions) + +


+ + +## ❗ Código de Conduta + +O Budibase está comprometido em oferecer a todos uma experiência acolhedora, diversificada e livre de assédio. Esperamos que todos os membros da comunidade Budibase sigam os princípios do nosso [**Código de Conduta**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Obrigado por ler. +
+ + +

+ + +## 🙌 Contribuindo para o Budibase + +Seja abrindo uma issue ou criando um pull request, toda contribuição é apreciada e bem-vinda. Se você está pensando em implementar uma nova funcionalidade ou alterar a API, por favor, crie primeiro uma Issue. Assim, podemos garantir que seu trabalho não seja em vão. + +### Não sabe por onde começar? +Um bom lugar para começar a contribuir é aqui: [Projetos em andamento](https://github.com/Budibase/budibase/projects/22). + +### Como o repositório está organizado? +O Budibase é um monorepo gerenciado pelo lerna. O Lerna cuida da construção e publicação dos pacotes do Budibase. Aqui estão, em alto nível, os pacotes que compõem o Budibase. + +- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - contém o código para o aplicativo svelte do lado do cliente do budibase builder. + +- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - Um módulo que roda no navegador e é responsável por ler definições JSON e criar aplicativos web dinâmicos a partir delas. + +- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - O servidor budibase. Este aplicativo Koa é responsável por servir o JS para os aplicativos builder e budibase, bem como fornecer a API para interagir com o banco de dados e o sistema de arquivos. + +Para mais informações, veja [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md) + +

+ + +## 📝 Licença + +O Budibase é open source, sob a licença [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). As bibliotecas do cliente e dos componentes estão licenciadas sob [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - para que os aplicativos que você cria possam ser usados sob licença como você desejar. + +

+ +## ⭐ Stargazers ao longo do tempo + +[![Stargazers ao longo do tempo](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase) + +Se você tiver problemas entre as atualizações do builder, por favor, use o guia a seguir [aqui](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) para limpar seu ambiente. + +

+ +## Contribuidores ✨ + +Agradecimentos a estas pessoas maravilhosas ([chave de emoji](https://allcontributors.org/docs/fr/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + +

Martin McKeaveney

💻 📖 ⚠️ 🚇

Michael Drury

📖 💻 ⚠️ 🚇

Andrew Kingston

📖 💻 ⚠️ 🎨

Michael Shanks

📖 💻 ⚠️

Kevin Åberg Kultalahti

📖 💻 ⚠️

Joe

📖 💻 🖋 🎨

Rory Powell

💻 📖 ⚠️

Peter Clement

💻 📖 ⚠️

Conor Mack

💻 📖 ⚠️

Grays-world

💻

Sylvain Galand

💻 📖

John Mullins

💻

Jakeboyd

💻

Steve Bridle

💻
+ + + +

+ + +## Licença + +Distribuído sob a licença GPL v3.0. Veja `LICENSE` para mais informações. +

diff --git a/i18n/README.ru.md b/i18n/README.ru.md index 61e5a00550..b235828a40 100644 --- a/i18n/README.ru.md +++ b/i18n/README.ru.md @@ -8,10 +8,10 @@

- Платформа low-code, которую вы будете любить использовать + Низкокодовая платформа, которую вы полюбите использовать

- Budibase - это open source платформа low-code, которая является самым простым способом создания внутренних инструментов, повышающих производительность. + Budibase - это открытая низкокодовая платформа, которая представляет собой самый простой способ создания внутренних инструментов, повышающих производительность.

@@ -25,10 +25,10 @@

- Все скачивания с GitHub + GitHub все релизы - Релизы GitHub (в хронологическом порядке) + GitHub релизы (в хронологическом порядке) Подписаться на @budibase @@ -44,7 +44,7 @@ · Документация · - Запросы на улучшение + Запросы на улучшения · Сообщить об ошибке · @@ -52,32 +52,156 @@



+## ✨ Функциональные возможности -## ✨ Функциональности - -### Создание и развертывание настоящего программного обеспечения -В отличие от других платформ, с Budibase вы создаете и развертываете одностраничные приложения. Приложения Budibase обладают высокой производительностью и могут быть разработаны с адаптивным дизайном, обеспечивая пользователям исключительный опыт. +### Строим и развертываем настоящее программное обеспечение +В отличие от других платформ, с помощью Budibase вы создаете и развертываете одностраничные приложения. Приложения Budibase имеют высокую производительность и могут быть адаптированы для разных устройств, обеспечивая вашим пользователям удивительный опыт.

-### Свободный и расширяемый исходный код -Budibase - это свободное программное обеспечение под лицензией GPL v3. Это должно дать вам уверенность в том, что Budibase всегда будет доступен. Вы также можете кодировать в Budibase или создать его форк и вносить изменения по своему усмотрению, что делает его дружественным для разработчиков. +### Открытый и расширяемый исходный код +Budibase - это свободное программное обеспечение под лицензией GPL v3. Это должно вас уверить в том, что Budibase всегда будет здесь. Вы также можете писать код в Budibase или форкнуть его и вносить изменения по своему усмотрению, что сделает его дружелюбным для разработчиков.

### Импорт данных или начало с нуля -Budibase может извлекать данные из различных источников, включая MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB или REST API. И в отличие от других платформ, с Budibase вы можете начать с нуля и создавать деловые приложения без каких-либо источников данных. [Запросить новый источник данных](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). +Budibase может получать данные из различных источников, включая MongoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB или REST API. И в отличие от других платформ, с помощью Budibase вы можете начать с нуля и создавать бизнес-приложения без каких-либо источников данных. [Запросить новый источник данных](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).

- Данные Budibase + Budibase data



-### Проектирование и создание приложений с помощью предварительно определенных компонентов -Budibase поставляется с красиво спроектированными и мощными компонентами, которые можно использовать как строительные блоки для создания вашего пользовательского интерфейса. Мы также предоставляем множество ваших любимых опций стилей CSS, чтобы вы могли проявить свою творческую мысль. [Запросить новый компонент](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). +### Проектирование и создание приложений с использованием предварительно определенных компонентов. + +Budibase поставляется с красиво оформленными и мощными компонентами, которые вы можете использовать как строительные блоки для создания вашего пользовательского интерфейса. Мы также предоставляем множество ваших любимых опций стилей CSS, чтобы вы могли проявить больше креативности. [Запросить новый компонент](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).

- Дизайн Budibase + Budibase design



-### Автоматизация процессов, интеграция с другими инструментами и подключение веб-хуков -Экономьте время, автоматизируя ручные процессы и рабочие пот +### Автоматизация процессов, интеграция с другими инструментами и подключение к вебхукам +Экономьте время, автоматизируя ручные процессы и рабочие потоки. Будь то подключение к вебхукам или автоматизация отправки электронных писем, просто скажите Budibase, что он должен делать, и позвольте ему работать за вас. Вы можете легко [создать новую автоматизацию для Budibase здесь](https://github.com/Budibase/automations) или [Запросить новую автоматизацию](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas). + +

+ Budibase automations +

+

+ +### Интеграция с вашими любимыми инструментами +Budibase интегрируется с рядом популярных инструментов, что позволяет вам создавать приложения, которые идеально вписываются в вашу технологическую стопку. + +

+ Budibase integrations +

+

+ +### Рай для админов +Budibase разработан для масштабирования. С Budibase вы можете самостоятельно размещать его на своей собственной инфраструктуре и глобально управлять пользователями, доменами, SMTP, приложениями, группами, внешним видом и многим другим. Вы также можете предоставить пользователям/группам портал приложений и поручить управление пользователями руководителю группы. + +- Смотрите промо-видео: https://youtu.be/xoljVpty_Kw + +


+ +## 🏁 Начало работы + + + +Разверните Budibase на своей собственной инфраструктуре с использованием Docker, Kubernetes и Digital Ocean. +Или используйте Budibase Cloud, если вам не нужно самостоятельно размещаться, и вы хотите быстро начать. + +### [Начать работу с Budibase](https://budibase.com) + + +

+ +## 🎓 Изучение Budibase + +Документация Budibase [здесь](https://docs.budibase.com). +
+ + +

+ +## 💬 Сообщество + +Если у вас есть вопросы или вы хотите обсудить что-то с другими пользователями Budibase и присоединиться к нашему сообществу, пожалуйста, перейдите по следующей ссылке: [Обсуждения на GitHub](https://github.com/Budibase/budibase/discussions) + +


+ + +## ❗ Кодекс поведения + +Budibase обязуется обеспечить каждому дружелюбный, разнообразный и безопасный опыт. Мы ожидаем, что все члены сообщества Budibase будут следовать принципам нашего [**Кодекса поведения**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Спасибо за внимание. +
+ + +

+ + +## 🙌 Вклад в Budibase + +Будь то открытие ошибки или создание запроса на включение изменений, любой вклад приветствуется и приветствуется. Если вы планируете реализовать новую функциональность или изменить API, сначала создайте Issue. Так мы сможем убедиться, что ваша работа не напрасна. + +### Не знаете, с чего начать? +Хорошее место для начала вклада - это здесь: [Текущие проекты](https://github.com/Budibase/budibase/projects/22). + +### Как организован репозиторий? +Budibase - это монорепозиторий, управляемый с помощью lerna. Lerna управляет сборкой и публикацией пакетов Budibase. Вот, в общих чертах, пакеты, из которых состоит Budibase. + +- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - содержит код клиентского приложения Svelte для Budibase builder. + +- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - Модуль, который запускается в браузере и отвечает за чтение JSON-определений и создание веб-приложений из них. + +- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - Сервер Budibase. Это приложение Koa отвечает за предоставление JS для строителей и приложений Budibase, а также предоставляет API для взаимодействия с базой данных и файловой системой. + +Для получения дополнительной информации см. [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md) + +

+ + +## 📝 Лицензия + +Budibase является проектом с открытым исходным кодом, лицензированным по [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). Клиентские библиотеки и компоненты лицензируются по [MPL](https://directory.fsf.org/wiki/License:MPL-2.0), так что приложения, которые вы создаете, могут использоваться под любой лицензией, как вам угодно. + +

+ +## ⭐ Старгейзеры во времени + +[![Stargazers во времени](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase) + +Если у вас возникли проблемы между обновлениями билдера, пожалуйста, используйте следующее руководство [здесь](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting), чтобы очистить ваше окружение. + +

+ +## Участники ✨ + +Благодарим этих замечательных людей ([ключи эмодзи](https://allcontributors.org/docs/ru/emoji-key)): + + + + + + + + + + + + + + + + + + + + + + + +

Martin McKeaveney

💻 📖 ⚠️ 🚇

Michael Drury

📖 💻 ⚠️ 🚇

Andrew Kingston

📖 💻 ⚠️ 🎨

Michael Shanks

📖 💻 ⚠️

Kevin Åberg Kultalahti

📖 💻 ⚠️

Joe

📓

Nico Kleynhans

🎨

Keith Lee

🎨

Ben-Shabs

📓

Reece King

🎨

Gunjan Chhabra

📓

Stavros Liaskos

🎨

theshu8

📓

Kleebster

📓
+ + + + +

From 80a6afd54f03a6dfb37a1b3f0cce763fcace349f Mon Sep 17 00:00:00 2001 From: mikesealey Date: Fri, 19 Apr 2024 10:41:39 +0100 Subject: [PATCH 055/338] closes side panel when navigating away regardless of ignoreClicksOutside --- packages/client/src/components/app/Layout.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index bfbac8f4f1..dccb061254 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -284,7 +284,9 @@ url={navItem.url} subLinks={navItem.subLinks} internalLink={navItem.internalLink} - on:clickLink={() => (mobileOpen = false)} + on:clickLink={(() => (mobileOpen = false), + console.log("287"), + sidePanelStore.actions.close)} leftNav={navigation === "Left"} {mobile} {navStateStore} From 237bc707581900f3f9bbcfff597d88d90a344454 Mon Sep 17 00:00:00 2001 From: mikesealey Date: Fri, 19 Apr 2024 10:58:25 +0100 Subject: [PATCH 056/338] removes console.log() --- packages/client/src/components/app/Layout.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index dccb061254..363a464143 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -285,7 +285,6 @@ subLinks={navItem.subLinks} internalLink={navItem.internalLink} on:clickLink={(() => (mobileOpen = false), - console.log("287"), sidePanelStore.actions.close)} leftNav={navigation === "Left"} {mobile} From bdf15b21b1f62cea09a3c03d5e0b8c7fc9260d66 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 19 Apr 2024 11:49:20 +0100 Subject: [PATCH 057/338] Fixes for filter drawer padding --- .../buttons/TableFilterButton.svelte | 19 ++++++++++--------- .../src/components/FilterBuilder.svelte | 1 - 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte index 0fc3fd505e..140cac1533 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/TableFilterButton.svelte @@ -1,6 +1,6 @@ @@ -284,8 +289,7 @@ url={navItem.url} subLinks={navItem.subLinks} internalLink={navItem.internalLink} - on:clickLink={(() => (mobileOpen = false), - sidePanelStore.actions.close)} + on:clickLink={handleClickLink} leftNav={navigation === "Left"} {mobile} {navStateStore} From 4c0d3ed5f38781a5db5e024993b3e7de7e6692ad Mon Sep 17 00:00:00 2001 From: mikesealey Date: Fri, 19 Apr 2024 14:45:57 +0100 Subject: [PATCH 059/338] runs the closeSidePanel function when navigating away using a button-action --- packages/client/src/utils/buttonActions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index d883ee1b55..4ab7490ae7 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 => { From b2f81276cdf17ab43118595e0ae9c79c626a7b46 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Mon, 22 Apr 2024 14:14:24 +0000 Subject: [PATCH 060/338] Bump version to 2.23.11 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index e8bcd4429c..728cddc194 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.23.10", + "version": "2.23.11", "npmClient": "yarn", "packages": [ "packages/*", From a4c0328c53610fab0209be6e25af502609d9e38d Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Mon, 22 Apr 2024 16:30:57 +0100 Subject: [PATCH 061/338] REST file handling and SMTP automation block attachments (#13403) * handle files in rest connector * fetch presigned url and return * further updates to handle files in rest connector * remove unused important and fix extension bug * wrong expiry param * tests * add const for temp bucket * handle ttl on bucket * more bucket ttl work * split out fileresponse and xmlresponse into utils * lint * remove log * fix tests * some pr comments * update function naming and lint * adding back needed response for frontend * use fsp * handle different content-disposition and potential path traversal * add test container for s3 / minio * add test case for filename* and ascii filenames * move tests into separate describe * remove log * up timeout * switch to minio image instead of localstack * use minio image instead of s3 for testing * stream file upload instead * use streamUpload and update signatures * update bucketcreate return * throw real error * tidy up * pro * pro ref fix? * pro fix * pro fix? * move minio test provider to backend-core * update email builder to allow attachments * testing for sending files via smtp * use backend-core minio test container in server * handle different types of url * fix minio test provider * test with container host * lint * try different hostname? * Revert "try different hostname?" This reverts commit cfefdb8ded2b49462604053cf140e7292771c651. * fix issue with fetching of signed url with test minio * update autoamtion attachments to take filename and url * fix tests * pro ref * fix parsing of url object * pr comments and linting * pro ref * fix pro again * fix pro * account-portal * fix null issue * fix ref * ref * When sending a file attachment in email fetch it directly from our object store * add more checks to ensure we're working with a signed url * update test to account for direct object store read * formatting * fix time issues within test * update bucket and path extraction to regex * use const in regex * pro * Updating TTL handling in upload functions (#13539) * Updating TTL handling in upload functions * describe ttl type * account for ttl creation in existing buckets and update types * fix tests * pro * pro --- packages/backend-core/src/environment.ts | 2 + .../src/objectStore/objectStore.ts | 121 ++++++++++++++---- .../backend-core/src/objectStore/utils.ts | 26 ++++ .../tests/core/utilities/index.ts | 3 + .../tests/core/utilities/minio.ts | 34 +++++ .../SetupPanel/AutomationBlockSetup.svelte | 56 +++++++- .../integration/KeyValueBuilder.svelte | 29 +++-- packages/cli/src/backups/objectStore.ts | 4 +- packages/pro | 2 +- packages/server/package.json | 3 + .../src/automations/steps/sendSmtpEmail.ts | 12 +- .../automations/tests/sendSmtpEmail.spec.ts | 8 ++ packages/server/src/integrations/rest.ts | 68 +++++----- .../src/integrations/tests/rest.spec.ts | 116 ++++++++++++++++- .../server/src/integrations/utils/utils.ts | 78 ++++++++++- .../src/utilities/fileSystem/clientLibrary.ts | 28 ++-- .../server/src/utilities/workerRequests.ts | 5 +- .../types/src/documents/app/automation.ts | 8 ++ .../src/api/controllers/global/email.ts | 2 + .../api/routes/global/tests/realEmail.spec.ts | 47 ++++++- packages/worker/src/tests/api/email.ts | 4 +- packages/worker/src/tests/jestEnv.ts | 4 +- .../worker/src/tests/structures/configs.ts | 4 +- packages/worker/src/utilities/email.ts | 40 +++++- yarn.lock | 19 ++- 25 files changed, 619 insertions(+), 104 deletions(-) create mode 100644 packages/backend-core/tests/core/utilities/minio.ts diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 8dbc904643..9ade81b9d7 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -29,6 +29,7 @@ const DefaultBucketName = { TEMPLATES: "templates", GLOBAL: "global", PLUGINS: "plugins", + TEMP: "tmp-file-attachments", } const selfHosted = !!parseInt(process.env.SELF_HOSTED || "") @@ -146,6 +147,7 @@ const environment = { process.env.GLOBAL_BUCKET_NAME || DefaultBucketName.GLOBAL, PLUGIN_BUCKET_NAME: process.env.PLUGIN_BUCKET_NAME || DefaultBucketName.PLUGINS, + TEMP_BUCKET_NAME: process.env.TEMP_BUCKET_NAME || DefaultBucketName.TEMP, USE_COUCH: process.env.USE_COUCH || true, MOCK_REDIS: process.env.MOCK_REDIS, DEFAULT_LICENSE: process.env.DEFAULT_LICENSE, diff --git a/packages/backend-core/src/objectStore/objectStore.ts b/packages/backend-core/src/objectStore/objectStore.ts index 8d18fb97fd..aa5365c5c3 100644 --- a/packages/backend-core/src/objectStore/objectStore.ts +++ b/packages/backend-core/src/objectStore/objectStore.ts @@ -7,31 +7,41 @@ import tar from "tar-fs" import zlib from "zlib" import { promisify } from "util" import { join } from "path" -import fs, { ReadStream } from "fs" +import fs, { PathLike, ReadStream } from "fs" import env from "../environment" -import { budibaseTempDir } from "./utils" +import { bucketTTLConfig, budibaseTempDir } from "./utils" import { v4 } from "uuid" import { APP_PREFIX, APP_DEV_PREFIX } from "../db" +import fsp from "fs/promises" const streamPipeline = promisify(stream.pipeline) // use this as a temporary store of buckets that are being created const STATE = { bucketCreationPromises: {}, } +const signedFilePrefix = "/files/signed" type ListParams = { ContinuationToken?: string } -type UploadParams = { +type BaseUploadParams = { bucket: string filename: string - path: string type?: string | null - // can be undefined, we will remove it - metadata?: { - [key: string]: string | undefined - } + metadata?: { [key: string]: string | undefined } + body?: ReadableStream | Buffer + ttl?: number + addTTL?: boolean + extra?: any +} + +type UploadParams = BaseUploadParams & { + path?: string | PathLike +} + +type StreamUploadParams = BaseUploadParams & { + stream: ReadStream } const CONTENT_TYPE_MAP: any = { @@ -41,6 +51,8 @@ const CONTENT_TYPE_MAP: any = { js: "application/javascript", json: "application/json", gz: "application/gzip", + svg: "image/svg+xml", + form: "multipart/form-data", } const STRING_CONTENT_TYPES = [ @@ -105,7 +117,10 @@ export function ObjectStore( * Given an object store and a bucket name this will make sure the bucket exists, * if it does not exist then it will create it. */ -export async function makeSureBucketExists(client: any, bucketName: string) { +export async function createBucketIfNotExists( + client: any, + bucketName: string +): Promise<{ created: boolean; exists: boolean }> { bucketName = sanitizeBucket(bucketName) try { await client @@ -113,15 +128,16 @@ export async function makeSureBucketExists(client: any, bucketName: string) { Bucket: bucketName, }) .promise() + return { created: false, exists: true } } catch (err: any) { const promises: any = STATE.bucketCreationPromises const doesntExist = err.statusCode === 404, noAccess = err.statusCode === 403 if (promises[bucketName]) { await promises[bucketName] + return { created: false, exists: true } } else if (doesntExist || noAccess) { if (doesntExist) { - // bucket doesn't exist create it promises[bucketName] = client .createBucket({ Bucket: bucketName, @@ -129,13 +145,15 @@ export async function makeSureBucketExists(client: any, bucketName: string) { .promise() await promises[bucketName] delete promises[bucketName] + return { created: true, exists: false } + } else { + throw new Error("Access denied to object store bucket." + err) } } else { throw new Error("Unable to write to object store bucket.") } } } - /** * Uploads the contents of a file given the required parameters, useful when * temp files in use (for example file uploaded as an attachment). @@ -146,12 +164,22 @@ export async function upload({ path, type, metadata, + body, + ttl, }: UploadParams) { const extension = filename.split(".").pop() - const fileBytes = fs.readFileSync(path) + + const fileBytes = path ? (await fsp.open(path)).createReadStream() : body const objectStore = ObjectStore(bucketName) - await makeSureBucketExists(objectStore, bucketName) + const bucketCreated = await createBucketIfNotExists(objectStore, bucketName) + + if (ttl && (bucketCreated.created || bucketCreated.exists)) { + let ttlConfig = bucketTTLConfig(bucketName, ttl) + if (objectStore.putBucketLifecycleConfiguration) { + await objectStore.putBucketLifecycleConfiguration(ttlConfig).promise() + } + } let contentType = type if (!contentType) { @@ -174,6 +202,7 @@ export async function upload({ } config.Metadata = metadata } + return objectStore.upload(config).promise() } @@ -181,14 +210,24 @@ export async function upload({ * Similar to the upload function but can be used to send a file stream * through to the object store. */ -export async function streamUpload( - bucketName: string, - filename: string, - stream: ReadStream | ReadableStream, - extra = {} -) { +export async function streamUpload({ + bucket: bucketName, + stream, + filename, + type, + extra, + ttl, +}: StreamUploadParams) { + const extension = filename.split(".").pop() const objectStore = ObjectStore(bucketName) - await makeSureBucketExists(objectStore, bucketName) + const bucketCreated = await createBucketIfNotExists(objectStore, bucketName) + + if (ttl && (bucketCreated.created || bucketCreated.exists)) { + let ttlConfig = bucketTTLConfig(bucketName, ttl) + if (objectStore.putBucketLifecycleConfiguration) { + await objectStore.putBucketLifecycleConfiguration(ttlConfig).promise() + } + } // Set content type for certain known extensions if (filename?.endsWith(".js")) { @@ -203,10 +242,18 @@ export async function streamUpload( } } + let contentType = type + if (!contentType) { + contentType = extension + ? CONTENT_TYPE_MAP[extension.toLowerCase()] + : CONTENT_TYPE_MAP.txt + } + const params = { Bucket: sanitizeBucket(bucketName), Key: sanitizeKey(filename), Body: stream, + ContentType: contentType, ...extra, } return objectStore.upload(params).promise() @@ -286,7 +333,7 @@ export function getPresignedUrl( const signedUrl = new URL(url) const path = signedUrl.pathname const query = signedUrl.search - return `/files/signed${path}${query}` + return `${signedFilePrefix}${path}${query}` } } @@ -341,7 +388,7 @@ export async function retrieveDirectory(bucketName: string, path: string) { */ export async function deleteFile(bucketName: string, filepath: string) { const objectStore = ObjectStore(bucketName) - await makeSureBucketExists(objectStore, bucketName) + await createBucketIfNotExists(objectStore, bucketName) const params = { Bucket: bucketName, Key: sanitizeKey(filepath), @@ -351,7 +398,7 @@ export async function deleteFile(bucketName: string, filepath: string) { export async function deleteFiles(bucketName: string, filepaths: string[]) { const objectStore = ObjectStore(bucketName) - await makeSureBucketExists(objectStore, bucketName) + await createBucketIfNotExists(objectStore, bucketName) const params = { Bucket: bucketName, Delete: { @@ -412,7 +459,13 @@ export async function uploadDirectory( if (file.isDirectory()) { uploads.push(uploadDirectory(bucketName, local, path)) } else { - uploads.push(streamUpload(bucketName, path, fs.createReadStream(local))) + uploads.push( + streamUpload({ + bucket: bucketName, + filename: path, + stream: fs.createReadStream(local), + }) + ) } } await Promise.all(uploads) @@ -467,3 +520,23 @@ export async function getReadStream( } return client.getObject(params).createReadStream() } + +/* +Given a signed url like '/files/signed/tmp-files-attachments/app_123456/myfile.txt' extract +the bucket and the path from it +*/ +export function extractBucketAndPath( + url: string +): { bucket: string; path: string } | null { + const baseUrl = url.split("?")[0] + + const regex = new RegExp(`^${signedFilePrefix}/(?[^/]+)/(?.+)$`) + const match = baseUrl.match(regex) + + if (match && match.groups) { + const { bucket, path } = match.groups + return { bucket, path } + } + + return null +} diff --git a/packages/backend-core/src/objectStore/utils.ts b/packages/backend-core/src/objectStore/utils.ts index 4c3a84ba91..08b5238ff6 100644 --- a/packages/backend-core/src/objectStore/utils.ts +++ b/packages/backend-core/src/objectStore/utils.ts @@ -2,6 +2,7 @@ import { join } from "path" import { tmpdir } from "os" import fs from "fs" import env from "../environment" +import { PutBucketLifecycleConfigurationRequest } from "aws-sdk/clients/s3" /**************************************************** * NOTE: When adding a new bucket - name * @@ -15,6 +16,7 @@ export const ObjectStoreBuckets = { TEMPLATES: env.TEMPLATES_BUCKET_NAME, GLOBAL: env.GLOBAL_BUCKET_NAME, PLUGINS: env.PLUGIN_BUCKET_NAME, + TEMP: env.TEMP_BUCKET_NAME, } const bbTmp = join(tmpdir(), ".budibase") @@ -29,3 +31,27 @@ try { export function budibaseTempDir() { return bbTmp } + +export const bucketTTLConfig = ( + bucketName: string, + days: number +): PutBucketLifecycleConfigurationRequest => { + const lifecycleRule = { + ID: `${bucketName}-ExpireAfter${days}days`, + Prefix: "", + Status: "Enabled", + Expiration: { + Days: days, + }, + } + const lifecycleConfiguration = { + Rules: [lifecycleRule], + } + + const params = { + Bucket: bucketName, + LifecycleConfiguration: lifecycleConfiguration, + } + + return params +} diff --git a/packages/backend-core/tests/core/utilities/index.ts b/packages/backend-core/tests/core/utilities/index.ts index 787d69be2c..b2f19a0286 100644 --- a/packages/backend-core/tests/core/utilities/index.ts +++ b/packages/backend-core/tests/core/utilities/index.ts @@ -4,3 +4,6 @@ export { generator } from "./structures" export * as testContainerUtils from "./testContainerUtils" export * as utils from "./utils" export * from "./jestUtils" +import * as minio from "./minio" + +export const objectStoreTestProviders = { minio } diff --git a/packages/backend-core/tests/core/utilities/minio.ts b/packages/backend-core/tests/core/utilities/minio.ts new file mode 100644 index 0000000000..cef33daa91 --- /dev/null +++ b/packages/backend-core/tests/core/utilities/minio.ts @@ -0,0 +1,34 @@ +import { GenericContainer, Wait, StartedTestContainer } from "testcontainers" +import { AbstractWaitStrategy } from "testcontainers/build/wait-strategies/wait-strategy" +import env from "../../../src/environment" + +let container: StartedTestContainer | undefined + +class ObjectStoreWaitStrategy extends AbstractWaitStrategy { + async waitUntilReady(container: any, boundPorts: any, startTime?: Date) { + const logs = Wait.forListeningPorts() + await logs.waitUntilReady(container, boundPorts, startTime) + } +} + +export async function start(): Promise { + container = await new GenericContainer("minio/minio") + .withExposedPorts(9000) + .withCommand(["server", "/data"]) + .withEnvironment({ + MINIO_ACCESS_KEY: "budibase", + MINIO_SECRET_KEY: "budibase", + }) + .withWaitStrategy(new ObjectStoreWaitStrategy().withStartupTimeout(30000)) + .start() + + const port = container.getMappedPort(9000) + env._set("MINIO_URL", `http://0.0.0.0:${port}`) +} + +export async function stop() { + if (container) { + await container.stop() + container = undefined + } +} diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 6434c7710d..2d2022299c 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -32,6 +32,7 @@ import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte" import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte" import BindingSidePanel from "components/common/bindings/BindingSidePanel.svelte" + import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte" import { BindingHelpers, BindingType } from "components/common/bindings/utils" import { bindingsToCompletions, @@ -356,7 +357,8 @@ value.customType !== "queryParams" && value.customType !== "cron" && value.customType !== "triggerSchema" && - value.customType !== "automationFields" + value.customType !== "automationFields" && + value.type !== "attachment" ) } @@ -372,6 +374,15 @@ console.error(error) } }) + const handleAttachmentParams = keyValuObj => { + let params = {} + if (keyValuObj?.length) { + for (let param of keyValuObj) { + params[param.url] = param.filename + } + } + return params + }
@@ -437,6 +448,33 @@ value={inputData[key]} options={Object.keys(table?.schema || {})} /> + {:else if value.type === "attachment"} +
+
+ +
+
+ + onChange( + { + detail: e.detail.map(({ name, value }) => ({ + url: name, + filename: value, + })), + }, + key + )} + object={handleAttachmentParams(inputData[key])} + allowJS + {bindings} + keyBindings + customButtonText={"Add attachment"} + keyPlaceholder={"URL"} + valuePlaceholder={"Filename"} + /> +
+
{:else if value.customType === "filters"} Define filters @@ -651,14 +689,22 @@ } .block-field { - display: flex; /* Use Flexbox */ + display: flex; justify-content: space-between; - flex-direction: row; /* Arrange label and field side by side */ - align-items: center; /* Align vertically in the center */ - gap: 10px; /* Add some space between label and field */ + flex-direction: row; + align-items: center; + gap: 10px; flex: 1; } + .attachment-field-width { + margin-top: var(--spacing-xs); + } + + .label-wrapper { + margin-top: var(--spacing-s); + } + .test :global(.drawer) { width: 10000px !important; } diff --git a/packages/builder/src/components/integration/KeyValueBuilder.svelte b/packages/builder/src/components/integration/KeyValueBuilder.svelte index 74636fc50c..5ed18a970a 100644 --- a/packages/builder/src/components/integration/KeyValueBuilder.svelte +++ b/packages/builder/src/components/integration/KeyValueBuilder.svelte @@ -35,6 +35,8 @@ export let bindingDrawerLeft export let allowHelpers = true export let customButtonText = null + export let keyBindings = false + export let allowJS = false export let compare = (option, value) => option === value let fields = Object.entries(object || {}).map(([name, value]) => ({ @@ -116,12 +118,23 @@ class:readOnly-menu={readOnly && showMenu} > {#each fields as field, idx} - + {#if keyBindings} + { + field.name = e.detail + changed() + }} + disabled={readOnly} + value={field.name} + {allowJS} + {allowHelpers} + drawerLeft={bindingDrawerLeft} + /> + {:else} + + {/if} {#if isJsonArray(field.value)} (value = e.detail)} + /> +
diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index 1c5722eb40..e23e19704c 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -29,6 +29,7 @@ export { default as image } from "./Image.svelte" export { default as embed } from "./Embed.svelte" export { default as icon } from "./Icon.svelte" export { default as backgroundimage } from "./BackgroundImage.svelte" +export { default as daterangepicker } from "./DateRangePicker.svelte" export { default as cardstat } from "./CardStat.svelte" export { default as spectrumcard } from "./SpectrumCard.svelte" export { default as tag } from "./Tag.svelte" From f0102286295bb5f788d8db42ebbdf827b7ec5026 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 23 Apr 2024 11:59:14 +0100 Subject: [PATCH 066/338] Respect app custom colour choices where possible --- packages/bbui/src/Form/Core/DatePicker/Calendar.svelte | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte index b073ac78f9..83692ff898 100644 --- a/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/Calendar.svelte @@ -198,10 +198,13 @@ } .spectrum-Calendar-date.is-today.is-selected, .spectrum-Calendar-date.is-today.is-selected::before { - border-color: var(--spectrum-global-color-blue-700); + border-color: var( + --primaryColorHover, + var(--spectrum-global-color-blue-700) + ); } .spectrum-Calendar-date.is-selected:not(.is-range-selection) { - background: var(--spectrum-global-color-blue-400); + background: var(--primaryColor, var(--spectrum-global-color-blue-400)); } .spectrum-Calendar tr { box-sizing: content-box; From a99c15fb26ec1d48c3fe55deff990083c77a0e2a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 23 Apr 2024 12:05:30 +0100 Subject: [PATCH 067/338] Fix wrong class name being used to ignore grid keypresses when editing dates in modals --- .../src/components/grid/overlays/KeyboardManager.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte index 4d2ff87b5c..cef4144833 100644 --- a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte +++ b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte @@ -20,7 +20,7 @@ const ignoredOriginSelectors = [ ".spectrum-Modal", - ".spectrum-Calendar", + ".date-time-popover", "#builder-side-panel-container", "[data-grid-ignore]", ] From 203acc0efdd1df43e2d5f05e523c81d86213b4ba Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 23 Apr 2024 12:40:57 +0100 Subject: [PATCH 068/338] Fix for regression in api initialisation --- packages/frontend-core/src/components/FilterUsers.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/FilterUsers.svelte b/packages/frontend-core/src/components/FilterUsers.svelte index 4613b8c40f..489426df1e 100644 --- a/packages/frontend-core/src/components/FilterUsers.svelte +++ b/packages/frontend-core/src/components/FilterUsers.svelte @@ -1,7 +1,9 @@ + +
+ {#if showCalendar} + handleChange(e.detail)} + bind:this={calendar} + /> + {/if} + +
+ + diff --git a/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte b/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte index 91dd95ff5f..dc4886d28d 100644 --- a/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/NumberInput.svelte @@ -33,7 +33,7 @@ font-weight: bold; font-family: var(--font-sans); -webkit-font-smoothing: antialiased; - box-sizing: content-box; + box-sizing: content-box !important; } input:focus, input:hover { diff --git a/packages/bbui/src/Form/Core/DatePicker/TimePicker.svelte b/packages/bbui/src/Form/Core/DatePicker/TimePicker.svelte index adf2a5e87a..047e5a4f08 100644 --- a/packages/bbui/src/Form/Core/DatePicker/TimePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker/TimePicker.svelte @@ -2,18 +2,20 @@ import { cleanInput } from "./utils" import dayjs from "dayjs" import NumberInput from "./NumberInput.svelte" + import { createEventDispatcher } from "svelte" export let value - export let onChange + + const dispatch = createEventDispatcher() $: displayValue = value || dayjs() const handleHourChange = e => { - onChange(displayValue.hour(parseInt(e.target.value))) + dispatch("change", displayValue.hour(parseInt(e.target.value))) } const handleMinuteChange = e => { - onChange(displayValue.minute(parseInt(e.target.value))) + dispatch("change", displayValue.minute(parseInt(e.target.value))) } const cleanHour = cleanInput({ max: 23, pad: 2, fallback: "00" }) @@ -51,7 +53,7 @@ .time-picker span { font-weight: bold; font-size: 18px; - z-index: -1; + z-index: 0; margin-bottom: 1px; } diff --git a/packages/bbui/src/Form/Core/index.js b/packages/bbui/src/Form/Core/index.js index 533a1956c5..7117b90081 100644 --- a/packages/bbui/src/Form/Core/index.js +++ b/packages/bbui/src/Form/Core/index.js @@ -9,6 +9,7 @@ export { default as CoreCombobox } from "./Combobox.svelte" export { default as CoreSwitch } from "./Switch.svelte" export { default as CoreSearch } from "./Search.svelte" export { default as CoreDatePicker } from "./DatePicker/DatePicker.svelte" +export { default as CoreDatePickerPopoverContents } from "./DatePicker/DatePickerPopoverContents.svelte" export { default as CoreDateRangePicker } from "./DateRangePicker.svelte" export { default as CoreDropzone } from "./Dropzone.svelte" export { default as CoreStepper } from "./Stepper.svelte" diff --git a/packages/bbui/src/helpers.js b/packages/bbui/src/helpers.js index c98ebad2c7..4448527fea 100644 --- a/packages/bbui/src/helpers.js +++ b/packages/bbui/src/helpers.js @@ -117,7 +117,9 @@ export const copyToClipboard = value => { }) } -export const parseDate = (value, { dateOnly } = {}) => { +// Parsed a date value. This is usually an ISO string, but can be a +// bunch of different formats and shapes depending on schema flags. +export const parseDate = (value, { enableTime = true }) => { // If empty then invalid if (!value) { return null @@ -131,7 +133,7 @@ export const parseDate = (value, { dateOnly } = {}) => { } // If date only, check for cases where we received a UTC string - else if (dateOnly && value.endsWith("Z")) { + else if (!enableTime && value.endsWith("Z")) { value = value.split("Z")[0] } } @@ -148,7 +150,42 @@ export const parseDate = (value, { dateOnly } = {}) => { return dayjs(Math.floor(parsedDate.valueOf() / 1000) * 1000) } -export const getDateDisplayValue = (value, { enableTime, timeOnly }) => { +// Stringifies a dayjs object to create an ISO string that respects the various +// schema flags +export const stringifyDate = ( + value, + { enableTime = true, timeOnly = false, ignoreTimezones = false } +) => { + if (!value) { + return null + } + + // Time only fields always ignore timezones, otherwise they make no sense. + // For non-timezone-aware fields, create an ISO 8601 timestamp of the exact + // time picked, without timezone + const offsetForTimezone = (enableTime && ignoreTimezones) || timeOnly + if (offsetForTimezone) { + // Ensure we use the correct offset for the date + const referenceDate = timeOnly ? new Date() : value.toDate() + const offset = referenceDate.getTimezoneOffset() * 60000 + return new Date(value.valueOf() - offset).toISOString().slice(0, -1) + } + + // For date-only fields, construct a manual timestamp string without a time + // or time zone + else if (!enableTime) { + const year = value.year() + const month = `${value.month() + 1}`.padStart(2, "0") + const day = `${value.date()}`.padStart(2, "0") + return `${year}-${month}-${day}T00:00:00.000` + } +} + +// Formats a dayjs date according to schema flags +export const getDateDisplayValue = ( + value, + { enableTime = true, timeOnly = false } +) => { if (!value?.isValid()) { return "" } diff --git a/packages/frontend-core/src/components/grid/cells/DateCell.svelte b/packages/frontend-core/src/components/grid/cells/DateCell.svelte index 019a0db9c2..394b6e1f43 100644 --- a/packages/frontend-core/src/components/grid/cells/DateCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DateCell.svelte @@ -1,6 +1,13 @@ -
+ + +
{displayValue}
@@ -55,17 +107,14 @@ {/if}
-{#if editable} -
- + onChange(e.detail)} - enableTime={!dateOnly} + {enableTime} {timeOnly} - ignoreTimezones={schema.ignoreTimezones} - bind:api={datePickerAPI} - on:open={() => (isOpen = true)} - on:close={() => (isOpen = false)} + {ignoreTimezones} useKeyboardShortcuts={false} />
@@ -80,6 +129,10 @@ align-items: center; flex: 1 1 auto; gap: var(--cell-spacing); + user-select: none; + } + .container.editable:hover { + cursor: pointer; } .value { flex: 1 1 auto; @@ -92,9 +145,10 @@ } .picker { position: absolute; - opacity: 0; - } - .picker :global(.spectrum-Textfield-input) { - width: 100%; + top: 100%; + left: -1px; + background: var(--grid-background-alt); + border: var(--cell-border); + border-radius: 2px; } diff --git a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte index 0299e66e2f..b3eab24fa1 100644 --- a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte @@ -103,8 +103,8 @@ position: absolute; top: 0; left: 0; - width: calc(100% + var(--max-cell-render-width-overflow)); - height: calc(var(--row-height) + var(--max-cell-render-height)); + width: calc(100% + var(--max-cell-render-verflow)); + height: calc(var(--row-height) + var(--max-cell-render-overflow)); z-index: 1; border-radius: 2px; resize: none; diff --git a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte index ede9bd1cff..158451c930 100644 --- a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte @@ -23,7 +23,7 @@ $: values = Array.isArray(value) ? value : [value].filter(x => x != null) $: { // Close when deselected - if (!focused) { + if (!focused && isOpen) { close() } } @@ -219,7 +219,7 @@ flex-direction: column; justify-content: flex-start; align-items: stretch; - max-height: var(--max-cell-render-height); + max-height: var(--max-cell-render-overflow); overflow-y: auto; border: var(--cell-border); box-shadow: 0 0 20px -4px rgba(0, 0, 0, 0.15); diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index bf1fe92ef0..a52dd9d53c 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -35,7 +35,7 @@ $: lookupMap = buildLookupMap(value, isOpen) $: debouncedSearch(searchString) $: { - if (!focused) { + if (!focused && isOpen) { close() } } @@ -451,7 +451,7 @@ left: 0; width: 100%; max-height: calc( - var(--max-cell-render-height) + var(--row-height) - var(--values-height) + var(--max-cell-render-overflow) + var(--row-height) - var(--values-height) ); background: var(--grid-background-alt); border: var(--cell-border); diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte index b6c686fd62..468f49bdec 100644 --- a/packages/frontend-core/src/components/grid/layout/Grid.svelte +++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte @@ -22,8 +22,7 @@ import NewRow from "./NewRow.svelte" import { createGridWebsocket } from "../lib/websocket" import { - MaxCellRenderHeight, - MaxCellRenderWidthOverflow, + MaxCellRenderOverflow, GutterWidth, DefaultRowHeight, } from "../lib/constants" @@ -78,6 +77,7 @@ contentLines, gridFocused, error, + focusedCellId, } = context // Keep config store up to date with props @@ -129,7 +129,7 @@ class:quiet on:mouseenter={() => gridFocused.set(true)} on:mouseleave={() => gridFocused.set(false)} - style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-height:{MaxCellRenderHeight}px; --max-cell-render-width-overflow:{MaxCellRenderWidthOverflow}px; --content-lines:{$contentLines};" + style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-overflow:{MaxCellRenderOverflow}px; --content-lines:{$contentLines};" > {#if showControls}
diff --git a/packages/frontend-core/src/components/grid/lib/constants.js b/packages/frontend-core/src/components/grid/lib/constants.js index a6e6723463..37d829873d 100644 --- a/packages/frontend-core/src/components/grid/lib/constants.js +++ b/packages/frontend-core/src/components/grid/lib/constants.js @@ -1,5 +1,4 @@ export const Padding = 246 -export const MaxCellRenderHeight = 222 export const ScrollBarSize = 8 export const GutterWidth = 72 export const DefaultColumnWidth = 200 @@ -12,4 +11,4 @@ export const NewRowID = "new" export const BlankRowID = "blank" export const RowPageSize = 100 export const FocusedCellMinOffset = 48 -export const MaxCellRenderWidthOverflow = Padding - 3 * ScrollBarSize +export const MaxCellRenderOverflow = Padding - 3 * ScrollBarSize diff --git a/packages/frontend-core/src/components/grid/stores/viewport.js b/packages/frontend-core/src/components/grid/stores/viewport.js index 8df8acd0f4..96a5a954ee 100644 --- a/packages/frontend-core/src/components/grid/stores/viewport.js +++ b/packages/frontend-core/src/components/grid/stores/viewport.js @@ -1,7 +1,6 @@ import { derived } from "svelte/store" import { - MaxCellRenderHeight, - MaxCellRenderWidthOverflow, + MaxCellRenderOverflow, MinColumnWidth, ScrollBarSize, } from "../lib/constants" @@ -95,11 +94,11 @@ export const deriveStores = context => { // Compute the last row index with space to render popovers below it const minBottom = - $height - ScrollBarSize * 3 - MaxCellRenderHeight + offset + $height - ScrollBarSize * 3 - MaxCellRenderOverflow + offset const lastIdx = Math.floor(minBottom / $rowHeight) // Compute the first row index with space to render popovers above it - const minTop = MaxCellRenderHeight + offset + const minTop = MaxCellRenderOverflow + offset const firstIdx = Math.ceil(minTop / $rowHeight) // Use the greater of the two indices so that we prefer content below, @@ -117,7 +116,7 @@ export const deriveStores = context => { let inversionIdx = $visibleColumns.length for (let i = $visibleColumns.length - 1; i >= 0; i--, inversionIdx--) { const rightEdge = $visibleColumns[i].left + $visibleColumns[i].width - if (rightEdge + MaxCellRenderWidthOverflow <= cutoff) { + if (rightEdge + MaxCellRenderOverflow <= cutoff) { break } } From c8446fa5a6d963caeaac71c3cd42749a96f18113 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:12:24 +0000 Subject: [PATCH 073/338] Bump mysql2 from 3.5.2 to 3.9.7 in /packages/server Bumps [mysql2](https://github.com/sidorares/node-mysql2) from 3.5.2 to 3.9.7. - [Release notes](https://github.com/sidorares/node-mysql2/releases) - [Changelog](https://github.com/sidorares/node-mysql2/blob/master/Changelog.md) - [Commits](https://github.com/sidorares/node-mysql2/compare/v3.5.2...v3.9.7) --- updated-dependencies: - dependency-name: mysql2 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- packages/server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/package.json b/packages/server/package.json index b2ac4e7d43..5e25fd3fc5 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -98,7 +98,7 @@ "memorystream": "0.3.1", "mongodb": "^6.3.0", "mssql": "10.0.1", - "mysql2": "3.5.2", + "mysql2": "3.9.7", "node-fetch": "2.6.7", "object-sizeof": "2.6.1", "open": "8.4.0", From cbf33adc0e4433356d5dde6f977da58e633d058f Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Wed, 24 Apr 2024 08:05:29 +0100 Subject: [PATCH 074/338] fix --- packages/builder/src/stores/builder/components.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/builder/src/stores/builder/components.js b/packages/builder/src/stores/builder/components.js index fe5f4e8a05..a2f5810d64 100644 --- a/packages/builder/src/stores/builder/components.js +++ b/packages/builder/src/stores/builder/components.js @@ -440,11 +440,14 @@ export class ComponentStore extends BudiStore { return state }) + componentTreeNodesStore.makeNodeVisible(componentInstance._id) + // Log event analytics.captureEvent(Events.COMPONENT_CREATED, { name: componentInstance._component, }) + return componentInstance } From 46d8a0698190a24cab9525dd119de5dea4e15261 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 24 Apr 2024 08:52:39 +0100 Subject: [PATCH 075/338] Update grid min size to accomodate new date picker --- .../src/components/app/GridBlock.svelte | 4 +-- .../src/components/grid/cells/DateCell.svelte | 25 ++++++++++--------- .../src/components/grid/layout/Grid.svelte | 5 +++- .../src/components/grid/lib/constants.js | 5 ++-- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/client/src/components/app/GridBlock.svelte b/packages/client/src/components/app/GridBlock.svelte index 55e5a2bd66..deefe6726a 100644 --- a/packages/client/src/components/app/GridBlock.svelte +++ b/packages/client/src/components/app/GridBlock.svelte @@ -149,8 +149,8 @@ border: 1px solid var(--spectrum-global-color-gray-300); border-radius: 4px; overflow: hidden; - min-height: 230px; - height: 410px; + /* min-height: 230px;*/ + /* height: 410px;*/ } div.in-builder :global(*) { pointer-events: none; diff --git a/packages/frontend-core/src/components/grid/cells/DateCell.svelte b/packages/frontend-core/src/components/grid/cells/DateCell.svelte index 394b6e1f43..04d5841f49 100644 --- a/packages/frontend-core/src/components/grid/cells/DateCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DateCell.svelte @@ -48,6 +48,10 @@ const close = () => { isOpen = false + + // Only save the changed value when closing. If the value is unchanged then + // this is handled upstream and no action is taken. + onChange(value) } const onKeyDown = e => { @@ -70,22 +74,19 @@ } const changeDate = (quantity, unit) => { + let newValue if (!value) { - value = dayjs() + newValue = dayjs() } else { - value = dayjs(value).add(quantity, unit) + newValue = dayjs(value).add(quantity, unit) } - debouncedOnChange( - Helpers.stringifyDate(value, { - enableTime, - timeOnly, - ignoreTimezones, - }) - ) + value = Helpers.stringifyDate(newValue, { + enableTime, + timeOnly, + ignoreTimezones, + }) } - const debouncedOnChange = debounce(onChange, 250) - onMount(() => { api = { onKeyDown, @@ -111,7 +112,7 @@
onChange(e.detail)} + on:change={e => (value = e.detail)} {enableTime} {timeOnly} {ignoreTimezones} diff --git a/packages/frontend-core/src/components/grid/layout/Grid.svelte b/packages/frontend-core/src/components/grid/layout/Grid.svelte index 468f49bdec..d62715083a 100644 --- a/packages/frontend-core/src/components/grid/layout/Grid.svelte +++ b/packages/frontend-core/src/components/grid/layout/Grid.svelte @@ -25,6 +25,7 @@ MaxCellRenderOverflow, GutterWidth, DefaultRowHeight, + MinHeight, } from "../lib/constants" export let API = null @@ -129,7 +130,8 @@ class:quiet on:mouseenter={() => gridFocused.set(true)} on:mouseleave={() => gridFocused.set(false)} - style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-overflow:{MaxCellRenderOverflow}px; --content-lines:{$contentLines};" + style="--row-height:{$rowHeight}px; --default-row-height:{DefaultRowHeight}px; --gutter-width:{GutterWidth}px; --max-cell-render-overflow:{MaxCellRenderOverflow}px; --content-lines:{$contentLines}; --min-height:{MinHeight + + $rowHeight}px;" > {#if showControls}
@@ -219,6 +221,7 @@ position: relative; overflow: hidden; background: var(--grid-background); + min-height: var(--min-height); } .grid, .grid :global(*) { diff --git a/packages/frontend-core/src/components/grid/lib/constants.js b/packages/frontend-core/src/components/grid/lib/constants.js index 37d829873d..1a01c9f1bd 100644 --- a/packages/frontend-core/src/components/grid/lib/constants.js +++ b/packages/frontend-core/src/components/grid/lib/constants.js @@ -1,4 +1,4 @@ -export const Padding = 246 +export const Padding = 400 export const ScrollBarSize = 8 export const GutterWidth = 72 export const DefaultColumnWidth = 200 @@ -11,4 +11,5 @@ export const NewRowID = "new" export const BlankRowID = "blank" export const RowPageSize = 100 export const FocusedCellMinOffset = 48 -export const MaxCellRenderOverflow = Padding - 3 * ScrollBarSize +export const MaxCellRenderOverflow = 222 +export const MinHeight = Padding + SmallRowHeight From 8ab2ca41d5d5a0d0aefabeec0c6f062699c7ab06 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 24 Apr 2024 09:54:10 +0100 Subject: [PATCH 076/338] Added array parsing for search query config --- .../src/api/controllers/row/utils/utils.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/server/src/api/controllers/row/utils/utils.ts b/packages/server/src/api/controllers/row/utils/utils.ts index 7eb4901ded..962756ff87 100644 --- a/packages/server/src/api/controllers/row/utils/utils.ts +++ b/packages/server/src/api/controllers/row/utils/utils.ts @@ -190,6 +190,23 @@ export function isUserMetadataTable(tableId: string) { return tableId === InternalTables.USER_METADATA } +export async function enrichArrayContext( + fields: any[], + inputs = {}, + helpers = true +): Promise { + const map: Record = {} + for (let index in fields) { + map[index] = fields[index] + } + const output = await enrichSearchContext(map, inputs, helpers) + const outputArray: any[] = [] + for (let [key, value] of Object.entries(output)) { + outputArray[parseInt(key)] = value + } + return outputArray +} + export async function enrichSearchContext( fields: Record, inputs = {}, @@ -200,6 +217,11 @@ export async function enrichSearchContext( return enrichedQuery } const parameters = { ...inputs } + + if (Array.isArray(fields)) { + return enrichArrayContext(fields, inputs, helpers) + } + // enrich the fields with dynamic parameters for (let key of Object.keys(fields)) { if (fields[key] == null) { From 2fc96549aa893bceef425af11237cc0914021f63 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 24 Apr 2024 12:05:06 +0000 Subject: [PATCH 077/338] Bump version to 2.23.12 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 728cddc194..94631c6820 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.23.11", + "version": "2.23.12", "npmClient": "yarn", "packages": [ "packages/*", From 4ce7162bb09ef865f641ca9fc453cd21cd9e4552 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 24 Apr 2024 15:20:10 +0100 Subject: [PATCH 078/338] wip --- .../src/api/routes/tests/search.spec.ts | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 698ea0c10b..cc3bde3a07 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -3,6 +3,7 @@ import { DatabaseName, getDatasource } from "../../../integrations/tests/utils" import * as setup from "./utilities" import { + AutoFieldSubType, Datasource, EmptyFilterOption, FieldType, @@ -18,12 +19,12 @@ import _ from "lodash" jest.unmock("mssql") describe.each([ - ["internal", undefined], - ["internal-sqs", undefined], + // ["internal", undefined], + // ["internal-sqs", undefined], [DatabaseName.POSTGRES, getDatasource(DatabaseName.POSTGRES)], - [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], - [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], - [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], + // [DatabaseName.MYSQL, getDatasource(DatabaseName.MYSQL)], + // [DatabaseName.SQL_SERVER, getDatasource(DatabaseName.SQL_SERVER)], + // [DatabaseName.MARIADB, getDatasource(DatabaseName.MARIADB)], ])("/api/:sourceId/search (%s)", (name, dsProvider) => { const isSqs = name === "internal-sqs" const isInternal = name === "internal" @@ -675,4 +676,27 @@ describe.each([ }).toContainExactly([{ num: SMALL }, { num: MEDIUM }])) }) }) + + isInternal && + describe.only("auto", () => { + beforeAll(async () => { + await createTable({ + auto: { + name: "auto", + type: FieldType.AUTO, + autocolumn: true, + subtype: AutoFieldSubType.AUTO_ID, + }, + }) + await createRows([{}, {}, {}]) + }) + + describe("equal", () => { + it("successfully finds a row", () => + expectQuery({ equal: { auto: 1 } }).toContainExactly([{ auto: 1 }])) + + it("fails to find nonexistent row", () => + expectQuery({ equal: { auto: 0 } }).toFindNothing()) + }) + }) }) From 849253faba49fd79917780e49bcba9de85df81d8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 24 Apr 2024 15:37:47 +0100 Subject: [PATCH 079/338] Bringing back the old mechanism of returning the client library through a pre-signed URL, rather than always serving through the service. --- .../backend-core/src/objectStore/buckets/app.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/backend-core/src/objectStore/buckets/app.ts b/packages/backend-core/src/objectStore/buckets/app.ts index 43bc965c65..74fdd2e92a 100644 --- a/packages/backend-core/src/objectStore/buckets/app.ts +++ b/packages/backend-core/src/objectStore/buckets/app.ts @@ -13,23 +13,16 @@ export function clientLibraryPath(appId: string) { * due to issues with the domain we were unable to continue doing this - keeping * incase we are able to switch back to CDN path again in future. */ -export function clientLibraryCDNUrl(appId: string, version: string) { +export function cloudClientLibraryUrl(appId: string, version: string) { let file = clientLibraryPath(appId) - if (env.CLOUDFRONT_CDN) { - // append app version to bust the cache - if (version) { - file += `?v=${version}` - } - // don't need to use presigned for client with cloudfront - // file is public - return cloudfront.getUrl(file) - } else { - return objectStore.getPresignedUrl(env.APPS_BUCKET_NAME, file) - } + return objectStore.getPresignedUrl(env.APPS_BUCKET_NAME, file) } export function clientLibraryUrl(appId: string, version: string) { let tenantId, qsParams: { appId: string; version: string; tenantId?: string } + if (env.isProd() && !env.SELF_HOSTED) { + return cloudClientLibraryUrl(appId, version) + } try { tenantId = getTenantId() } finally { From 795991438f769fc2969d83feabed11b3af338494 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 24 Apr 2024 15:47:39 +0100 Subject: [PATCH 080/338] Use new grid popover for date cells to allow overflow outside grid --- packages/bbui/src/Popover/Popover.svelte | 3 +- .../src/components/grid/cells/DataCell.svelte | 2 + .../src/components/grid/cells/DateCell.svelte | 38 +++++++++++------- .../src/components/grid/layout/Grid.svelte | 10 ++--- .../src/components/grid/layout/GridRow.svelte | 2 + .../src/components/grid/lib/constants.js | 3 +- .../grid/overlays/GridPopover.svelte | 40 +++++++++++++++++++ .../grid/overlays/PopoverOverlay.svelte | 9 +++++ .../grid/overlays/ScrollOverlay.svelte | 2 + 9 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 packages/frontend-core/src/components/grid/overlays/GridPopover.svelte create mode 100644 packages/frontend-core/src/components/grid/overlays/PopoverOverlay.svelte diff --git a/packages/bbui/src/Popover/Popover.svelte b/packages/bbui/src/Popover/Popover.svelte index 263a0b6dc6..079aee32b5 100644 --- a/packages/bbui/src/Popover/Popover.svelte +++ b/packages/bbui/src/Popover/Popover.svelte @@ -116,12 +116,11 @@ min-width: var(--spectrum-global-dimension-size-2000); border-color: var(--spectrum-global-color-gray-300); overflow: auto; - transition: opacity 260ms ease-out, transform 260ms ease-out; + transition: opacity 260ms ease-out; } .hidden { opacity: 0; pointer-events: none; - transform: translateY(-20px); } .customZindex { z-index: var(--customZindex) !important; diff --git a/packages/frontend-core/src/components/grid/cells/DataCell.svelte b/packages/frontend-core/src/components/grid/cells/DataCell.svelte index d8cff26b9d..33f74b116c 100644 --- a/packages/frontend-core/src/components/grid/cells/DataCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DataCell.svelte @@ -22,6 +22,7 @@ export let invertY = false export let contentLines = 1 export let hidden = false + export let rand const emptyError = writable(null) @@ -96,6 +97,7 @@ {invertY} {invertX} {contentLines} + {rand} /> diff --git a/packages/frontend-core/src/components/grid/cells/DateCell.svelte b/packages/frontend-core/src/components/grid/cells/DateCell.svelte index 04d5841f49..26f5653fd0 100644 --- a/packages/frontend-core/src/components/grid/cells/DateCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DateCell.svelte @@ -7,7 +7,7 @@ } from "@budibase/bbui" import { onMount } from "svelte" import dayjs from "dayjs" - import { debounce } from "../../../utils/utils" + import GridPopover from "../overlays/GridPopover.svelte" export let value export let schema @@ -15,8 +15,12 @@ export let focused = false export let readonly = false export let api + export let invertX = false + export let invertY = false + export let rand let isOpen + let anchor $: timeOnly = schema?.timeOnly $: enableTime = !schema?.dateOnly @@ -99,7 +103,12 @@ -
+
{displayValue}
@@ -109,16 +118,18 @@
{#if isOpen} -
- (value = e.detail)} - {enableTime} - {timeOnly} - {ignoreTimezones} - useKeyboardShortcuts={false} - /> -
+ +
+ (value = e.detail)} + {enableTime} + {timeOnly} + {ignoreTimezones} + useKeyboardShortcuts={false} + /> +
+
{/if} ` + + const updateInitialOffsets = open => { + if (!open) { + return + } + initialOffsetX = $scroll.left + initialOffsetY = $scroll.top % $rowHeight + } + + + +{@html markup} + + + diff --git a/packages/frontend-core/src/components/grid/overlays/PopoverOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/PopoverOverlay.svelte new file mode 100644 index 0000000000..a03ba6d927 --- /dev/null +++ b/packages/frontend-core/src/components/grid/overlays/PopoverOverlay.svelte @@ -0,0 +1,9 @@ +
+ + diff --git a/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte index 43a64f3fbd..c256e0dc19 100644 --- a/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte @@ -127,6 +127,7 @@ on:mousedown={startVDragging} on:touchstart={startVDragging} class:dragging={isDraggingV} + data-ignore-click-outside="true" /> {/if} {#if $showHScrollbar} @@ -137,6 +138,7 @@ on:mousedown={startHDragging} on:touchstart={startHDragging} class:dragging={isDraggingH} + data-ignore-click-outside="true" /> {/if} From f76abb0ca93434b4047f544434430046812e62a7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 24 Apr 2024 15:59:11 +0100 Subject: [PATCH 081/338] Getting rid of linting error. --- packages/backend-core/src/objectStore/buckets/app.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/objectStore/buckets/app.ts b/packages/backend-core/src/objectStore/buckets/app.ts index 74fdd2e92a..3ad575150e 100644 --- a/packages/backend-core/src/objectStore/buckets/app.ts +++ b/packages/backend-core/src/objectStore/buckets/app.ts @@ -13,7 +13,7 @@ export function clientLibraryPath(appId: string) { * due to issues with the domain we were unable to continue doing this - keeping * incase we are able to switch back to CDN path again in future. */ -export function cloudClientLibraryUrl(appId: string, version: string) { +function cloudClientLibraryUrl(appId: string) { let file = clientLibraryPath(appId) return objectStore.getPresignedUrl(env.APPS_BUCKET_NAME, file) } @@ -21,7 +21,7 @@ export function cloudClientLibraryUrl(appId: string, version: string) { export function clientLibraryUrl(appId: string, version: string) { let tenantId, qsParams: { appId: string; version: string; tenantId?: string } if (env.isProd() && !env.SELF_HOSTED) { - return cloudClientLibraryUrl(appId, version) + return cloudClientLibraryUrl(appId) } try { tenantId = getTenantId() From 443be4cdab85c4a6d0083f3b16d34d6cdb86dde9 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 24 Apr 2024 16:28:44 +0100 Subject: [PATCH 082/338] More upgrades to grids to support new popovers and use popovers for options cells --- .../backend/DataTable/TableDataTable.svelte | 2 +- .../src/components/grid/cells/DateCell.svelte | 33 ++++-------- .../components/grid/cells/OptionsCell.svelte | 51 ++++++++----------- .../grid/layout/GridScrollWrapper.svelte | 4 ++ .../grid/overlays/GridPopover.svelte | 38 +++++--------- .../grid/overlays/ScrollOverlay.svelte | 10 ++-- .../src/components/grid/stores/reorder.js | 2 +- 7 files changed, 56 insertions(+), 84 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 6a5cd2f282..fe393c5268 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -101,7 +101,7 @@ diff --git a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte index 158451c930..81882c867c 100644 --- a/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/OptionsCell.svelte @@ -1,7 +1,8 @@ - -{@html markup} - +
dispatch("close")}> + +
+ + diff --git a/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte index c256e0dc19..b10f245fcb 100644 --- a/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/ScrollOverlay.svelte @@ -17,6 +17,7 @@ height, isDragging, menu, + focusedCellAPI, } = getContext("grid") // State for dragging bars @@ -47,10 +48,11 @@ $: barLeft = ScrollBarSize + availWidth * ($scrollLeft / $maxScrollLeft) // Helper to close the context menu if it's open - const closeMenu = () => { + const closePopovers = () => { if ($menu.visible) { menu.actions.close() } + $focusedCellAPI?.blur() } const getLocation = e => { @@ -70,7 +72,7 @@ document.addEventListener("mouseup", stopVDragging) document.addEventListener("touchend", stopVDragging) isDraggingV = true - closeMenu() + closePopovers() } const moveVDragging = domDebounce(e => { const delta = getLocation(e).y - initialMouse @@ -99,7 +101,7 @@ document.addEventListener("mouseup", stopHDragging) document.addEventListener("touchend", stopHDragging) isDraggingH = true - closeMenu() + closePopovers() } const moveHDragging = domDebounce(e => { const delta = getLocation(e).x - initialMouse @@ -127,7 +129,6 @@ on:mousedown={startVDragging} on:touchstart={startVDragging} class:dragging={isDraggingV} - data-ignore-click-outside="true" /> {/if} {#if $showHScrollbar} @@ -138,7 +139,6 @@ on:mousedown={startHDragging} on:touchstart={startHDragging} class:dragging={isDraggingH} - data-ignore-click-outside="true" /> {/if} diff --git a/packages/frontend-core/src/components/grid/stores/reorder.js b/packages/frontend-core/src/components/grid/stores/reorder.js index f820593174..fed9f4c6ef 100644 --- a/packages/frontend-core/src/components/grid/stores/reorder.js +++ b/packages/frontend-core/src/components/grid/stores/reorder.js @@ -87,7 +87,7 @@ export const createActions = context => { // Check if we need to start auto-scrolling const $reorder = get(reorder) const proximityCutoff = 140 - const speedFactor = 8 + const speedFactor = 16 const rightProximity = Math.max(0, $reorder.gridLeft + $reorder.width - x) const leftProximity = Math.max(0, x - $reorder.gridLeft) if (rightProximity < proximityCutoff) { From 2187d25711241bbc1525868b531f2aa683b0509c Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Wed, 24 Apr 2024 17:12:36 +0100 Subject: [PATCH 083/338] Revert "Changing client library to be retrieved via pre-signed URL for Cloud" --- .../backend-core/src/objectStore/buckets/app.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/packages/backend-core/src/objectStore/buckets/app.ts b/packages/backend-core/src/objectStore/buckets/app.ts index 3ad575150e..43bc965c65 100644 --- a/packages/backend-core/src/objectStore/buckets/app.ts +++ b/packages/backend-core/src/objectStore/buckets/app.ts @@ -13,16 +13,23 @@ export function clientLibraryPath(appId: string) { * due to issues with the domain we were unable to continue doing this - keeping * incase we are able to switch back to CDN path again in future. */ -function cloudClientLibraryUrl(appId: string) { +export function clientLibraryCDNUrl(appId: string, version: string) { let file = clientLibraryPath(appId) - return objectStore.getPresignedUrl(env.APPS_BUCKET_NAME, file) + if (env.CLOUDFRONT_CDN) { + // append app version to bust the cache + if (version) { + file += `?v=${version}` + } + // don't need to use presigned for client with cloudfront + // file is public + return cloudfront.getUrl(file) + } else { + return objectStore.getPresignedUrl(env.APPS_BUCKET_NAME, file) + } } export function clientLibraryUrl(appId: string, version: string) { let tenantId, qsParams: { appId: string; version: string; tenantId?: string } - if (env.isProd() && !env.SELF_HOSTED) { - return cloudClientLibraryUrl(appId) - } try { tenantId = getTenantId() } finally { From 377cd97f4b85ab63810da1ee386a1ffb350291e7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 24 Apr 2024 17:35:23 +0100 Subject: [PATCH 084/338] Update attachment cells to use popovers --- .../grid/cells/AttachmentCell.svelte | 49 ++++++++----------- .../src/components/grid/cells/DataCell.svelte | 4 +- .../src/components/grid/cells/DateCell.svelte | 4 +- .../components/grid/cells/HeaderCell.svelte | 4 +- .../components/grid/cells/OptionsCell.svelte | 4 +- .../src/components/grid/layout/Grid.svelte | 6 +-- .../src/components/grid/layout/GridRow.svelte | 4 +- .../grid/overlays/GridPopover.svelte | 6 +-- .../src/components/grid/stores/menu.js | 4 +- 9 files changed, 39 insertions(+), 46 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte index e7dc51e5d5..ac1bb464b3 100644 --- a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte @@ -1,6 +1,7 @@ @@ -14,7 +14,7 @@ bind:open {anchor} align={invertX ? "right" : "left"} - portalTarget="#grid-{rand} .grid-popover-container" + portalTarget="#{gridID} .grid-popover-container" offset={1} >
dispatch("close")}> diff --git a/packages/frontend-core/src/components/grid/stores/menu.js b/packages/frontend-core/src/components/grid/stores/menu.js index 2d11b65bd4..ea32285a95 100644 --- a/packages/frontend-core/src/components/grid/stores/menu.js +++ b/packages/frontend-core/src/components/grid/stores/menu.js @@ -13,13 +13,13 @@ export const createStores = () => { } export const createActions = context => { - const { menu, focusedCellId, rand } = context + const { menu, focusedCellId, gridID } = context const open = (cellId, e) => { e.preventDefault() // Get DOM node for grid data wrapper to compute relative position to - const gridNode = document.getElementById(`grid-${rand}`) + const gridNode = document.getElementById(gridID) const dataNode = gridNode?.getElementsByClassName("grid-data-outer")?.[0] if (!dataNode) { return From 23bd635a8bf0ee534824b04db8af31052a861eb0 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 24 Apr 2024 20:26:58 +0100 Subject: [PATCH 085/338] Update relationship cells to use popovers --- .../grid/cells/AttachmentCell.svelte | 9 ++- .../src/components/grid/cells/DateCell.svelte | 2 +- .../components/grid/cells/OptionsCell.svelte | 8 +-- .../grid/cells/RelationshipCell.svelte | 60 +++++-------------- .../src/components/grid/lib/constants.js | 5 ++ .../grid/overlays/GridPopover.svelte | 45 ++++++++++++-- 6 files changed, 70 insertions(+), 59 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte index ac1bb464b3..a74878467b 100644 --- a/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/AttachmentCell.svelte @@ -93,7 +93,14 @@
{#if isOpen} - +
{#if isOpen} - + {#if isOpen} - -
e.stopPropagation()}> + +
{#each options as option, idx} {@const color = optionColors[option] || getOptionColor(option)} @@ -219,10 +219,6 @@ flex-direction: column; justify-content: flex-start; align-items: stretch; - max-height: var(--max-cell-render-overflow); - overflow-y: auto; - min-width: 200px; - max-width: 400px; } .option { flex: 0 0 var(--default-row-height); diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index a52dd9d53c..ba84316ab4 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -1,8 +1,9 @@ -
dispatch("close")}> +
dispatch("close")} + on:wheel={e => e.stopPropagation()} + >
@@ -26,5 +54,12 @@ :global(.grid-popover-container .spectrum-Popover) { background: var(--grid-background-alt); border: var(--cell-border); + min-width: none; + max-width: none; + overflow: hidden; + } + .grid-popover-contents { + overflow-y: auto; + overflow-x: hidden; } From 957facb99cb073d8e7f9bc41c915a9ba08255a45 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 24 Apr 2024 20:39:16 +0100 Subject: [PATCH 086/338] Update long form cells to use popovers --- .../components/grid/cells/LongFormCell.svelte | 64 +++++++++---------- .../grid/overlays/GridPopover.svelte | 2 + 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte index b3eab24fa1..6a05b4ae49 100644 --- a/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/LongFormCell.svelte @@ -1,6 +1,7 @@ -{#if isOpen} -