Merge branch 'master' into fix/logical-operator-filter-cleanup
This commit is contained in:
commit
bbe2763e54
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "2.30.6",
|
||||
"version": "2.30.8",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*",
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"@spectrum-css/switch": "1.0.2",
|
||||
"@spectrum-css/table": "3.0.1",
|
||||
"@spectrum-css/tabs": "3.2.12",
|
||||
"@spectrum-css/tag": "3.0.0",
|
||||
"@spectrum-css/tags": "3.0.2",
|
||||
"@spectrum-css/textfield": "3.0.1",
|
||||
"@spectrum-css/toast": "3.0.1",
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
on:click={() => onSelect(data)}
|
||||
>
|
||||
<span class="spectrum-Menu-itemLabel">
|
||||
{data.datasource?.name ? `${data.datasource.name} - ` : ""}{data.label}
|
||||
{data.datasourceName ? `${data.datasourceName} - ` : ""}{data.label}
|
||||
</span>
|
||||
<svg
|
||||
class="spectrum-Icon spectrum-UIIcon-Checkmark100 spectrum-Menu-checkmark spectrum-Menu-itemIcon"
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
export const datasourceSelect = {
|
||||
table: (table, datasources) => ({
|
||||
label: table.name,
|
||||
tableId: table._id,
|
||||
type: "table",
|
||||
datasource: datasources.find(
|
||||
datasource => datasource._id === table.sourceId || table.datasourceId
|
||||
),
|
||||
}),
|
||||
table: (table, datasources) => {
|
||||
const sourceId = table.sourceId || table.datasourceId
|
||||
const datasource = datasources.find(ds => ds._id === sourceId)
|
||||
return {
|
||||
label: table.name,
|
||||
tableId: table._id,
|
||||
type: "table",
|
||||
datasourceName: datasource?.name,
|
||||
}
|
||||
},
|
||||
viewV2: view => ({
|
||||
...view,
|
||||
label: view.name,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<script>
|
||||
import { helpers } from "@budibase/shared-core"
|
||||
import { DetailSummary, notifications } from "@budibase/bbui"
|
||||
import { componentStore, builderStore } from "stores/builder"
|
||||
import PropertyControl from "components/design/settings/controls/PropertyControl.svelte"
|
||||
|
@ -8,6 +7,7 @@
|
|||
import { getComponentForSetting } from "components/design/settings/componentSettings"
|
||||
import InfoDisplay from "./InfoDisplay.svelte"
|
||||
import analytics, { Events } from "analytics"
|
||||
import { shouldDisplaySetting } from "@budibase/frontend-core"
|
||||
|
||||
export let componentDefinition
|
||||
export let componentInstance
|
||||
|
@ -48,7 +48,7 @@
|
|||
|
||||
// Filter out settings which shouldn't be rendered
|
||||
sections.forEach(section => {
|
||||
section.visible = shouldDisplay(instance, section)
|
||||
section.visible = shouldDisplaySetting(instance, section)
|
||||
if (!section.visible) {
|
||||
return
|
||||
}
|
||||
|
@ -88,46 +88,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
const shouldDisplay = (instance, setting) => {
|
||||
let dependsOn = setting.dependsOn
|
||||
if (dependsOn && !Array.isArray(dependsOn)) {
|
||||
dependsOn = [dependsOn]
|
||||
}
|
||||
if (!dependsOn?.length) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ensure all conditions are met
|
||||
return dependsOn.every(condition => {
|
||||
let dependantSetting = condition
|
||||
let dependantValues = null
|
||||
let invert = !!condition.invert
|
||||
if (typeof condition === "object") {
|
||||
dependantSetting = condition.setting
|
||||
dependantValues = condition.value
|
||||
}
|
||||
if (!dependantSetting) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure values is an array
|
||||
if (!Array.isArray(dependantValues)) {
|
||||
dependantValues = [dependantValues]
|
||||
}
|
||||
|
||||
// If inverting, we want to ensure that we don't have any matches.
|
||||
// If not inverting, we want to ensure that we do have any matches.
|
||||
const currentVal = helpers.deepGet(instance, dependantSetting)
|
||||
const anyMatches = dependantValues.some(dependantVal => {
|
||||
if (dependantVal == null) {
|
||||
return currentVal != null && currentVal !== false && currentVal !== ""
|
||||
}
|
||||
return dependantVal === currentVal
|
||||
})
|
||||
return anyMatches !== invert
|
||||
})
|
||||
}
|
||||
|
||||
const canRenderControl = (instance, setting, isScreen, includeHidden) => {
|
||||
// Prevent rendering on click setting for screens
|
||||
if (setting?.type === "event" && isScreen) {
|
||||
|
@ -142,7 +102,7 @@
|
|||
if (setting.hidden && !includeHidden) {
|
||||
return false
|
||||
}
|
||||
return shouldDisplay(instance, setting)
|
||||
return shouldDisplaySetting(instance, setting)
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -14,11 +14,90 @@
|
|||
import sanitizeUrl from "helpers/sanitizeUrl"
|
||||
import ButtonActionEditor from "components/design/settings/controls/ButtonActionEditor/ButtonActionEditor.svelte"
|
||||
import { getBindableProperties } from "dataBinding"
|
||||
import BarButtonList from "components/design/settings/controls/BarButtonList.svelte"
|
||||
|
||||
$: bindings = getBindableProperties($selectedScreen, null)
|
||||
$: screenSettings = getScreenSettings($selectedScreen)
|
||||
|
||||
let errors = {}
|
||||
|
||||
const getScreenSettings = screen => {
|
||||
let settings = [
|
||||
{
|
||||
key: "routing.homeScreen",
|
||||
control: Checkbox,
|
||||
props: {
|
||||
text: "Set as home screen",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "routing.route",
|
||||
label: "Route",
|
||||
control: Input,
|
||||
parser: val => {
|
||||
if (!val.startsWith("/")) {
|
||||
val = "/" + val
|
||||
}
|
||||
return sanitizeUrl(val)
|
||||
},
|
||||
validate: route => {
|
||||
const existingRoute = screen.routing.route
|
||||
if (route !== existingRoute && routeTaken(route)) {
|
||||
return "That URL is already in use for this role"
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "routing.roleId",
|
||||
label: "Access",
|
||||
control: RoleSelect,
|
||||
validate: role => {
|
||||
const existingRole = screen.routing.roleId
|
||||
if (role !== existingRole && roleTaken(role)) {
|
||||
return "That role is already in use for this URL"
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "onLoad",
|
||||
label: "On screen load",
|
||||
control: ButtonActionEditor,
|
||||
},
|
||||
{
|
||||
key: "width",
|
||||
label: "Width",
|
||||
control: Select,
|
||||
props: {
|
||||
options: ["Extra small", "Small", "Medium", "Large", "Max"],
|
||||
placeholder: "Default",
|
||||
disabled: !!screen.layoutId,
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "props.layout",
|
||||
label: "Layout",
|
||||
defaultValue: "flex",
|
||||
control: BarButtonList,
|
||||
props: {
|
||||
options: [
|
||||
{
|
||||
barIcon: "ModernGridView",
|
||||
value: "flex",
|
||||
},
|
||||
{
|
||||
barIcon: "ViewGrid",
|
||||
value: "grid",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return settings
|
||||
}
|
||||
|
||||
const routeTaken = url => {
|
||||
const roleId = get(selectedScreen).routing.roleId || "BASIC"
|
||||
return get(screenStore).screens.some(
|
||||
|
@ -71,61 +150,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
$: screenSettings = [
|
||||
{
|
||||
key: "routing.homeScreen",
|
||||
control: Checkbox,
|
||||
props: {
|
||||
text: "Set as home screen",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "routing.route",
|
||||
label: "Route",
|
||||
control: Input,
|
||||
parser: val => {
|
||||
if (!val.startsWith("/")) {
|
||||
val = "/" + val
|
||||
}
|
||||
return sanitizeUrl(val)
|
||||
},
|
||||
validate: route => {
|
||||
const existingRoute = get(selectedScreen).routing.route
|
||||
if (route !== existingRoute && routeTaken(route)) {
|
||||
return "That URL is already in use for this role"
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "routing.roleId",
|
||||
label: "Access",
|
||||
control: RoleSelect,
|
||||
validate: role => {
|
||||
const existingRole = get(selectedScreen).routing.roleId
|
||||
if (role !== existingRole && roleTaken(role)) {
|
||||
return "That role is already in use for this URL"
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "onLoad",
|
||||
label: "On screen load",
|
||||
control: ButtonActionEditor,
|
||||
},
|
||||
{
|
||||
key: "width",
|
||||
label: "Width",
|
||||
control: Select,
|
||||
props: {
|
||||
options: ["Extra small", "Small", "Medium", "Large", "Max"],
|
||||
placeholder: "Default",
|
||||
disabled: !!$selectedScreen.layoutId,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const removeCustomLayout = async () => {
|
||||
return screenStore.removeCustomLayout(get(selectedScreen))
|
||||
}
|
||||
|
@ -149,6 +173,7 @@
|
|||
value={Helpers.deepGet($selectedScreen, setting.key)}
|
||||
onChange={val => setScreenSetting(setting, val)}
|
||||
props={{ ...setting.props, error: errors[setting.key] }}
|
||||
defaultValue={setting.defaultValue}
|
||||
{bindings}
|
||||
/>
|
||||
{/each}
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<Layout gap="S" paddingX="L" paddingY="XL">
|
||||
<Layout gap="XS" paddingX="L" paddingY="XL">
|
||||
{#if activeTab === "theme"}
|
||||
<ThemePanel />
|
||||
{:else}
|
||||
|
|
|
@ -144,7 +144,12 @@
|
|||
const rootComponent = get(selectedScreen).props
|
||||
const component = findComponent(rootComponent, data.id)
|
||||
componentStore.copy(component)
|
||||
await componentStore.paste(component)
|
||||
await componentStore.paste(
|
||||
component,
|
||||
data.mode,
|
||||
null,
|
||||
data.selectComponent
|
||||
)
|
||||
} else if (type === "preview-loaded") {
|
||||
// Wait for this event to show the client library if intelligent
|
||||
// loading is supported
|
||||
|
@ -246,13 +251,13 @@
|
|||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="component-container">
|
||||
<div
|
||||
class="component-container"
|
||||
class:tablet={$previewStore.previewDevice === "tablet"}
|
||||
class:mobile={$previewStore.previewDevice === "mobile"}
|
||||
>
|
||||
{#if loading}
|
||||
<div
|
||||
class={`loading ${$themeStore.baseTheme} ${$themeStore.theme}`}
|
||||
class:tablet={$previewStore.previewDevice === "tablet"}
|
||||
class:mobile={$previewStore.previewDevice === "mobile"}
|
||||
>
|
||||
<div class={`loading ${$themeStore.baseTheme} ${$themeStore.theme}`}>
|
||||
<ClientAppSkeleton
|
||||
sideNav={$navigationStore?.navigation === "Left"}
|
||||
hideFooter
|
||||
|
@ -275,6 +280,7 @@
|
|||
src="/app/preview"
|
||||
class:hidden={loading || error}
|
||||
/>
|
||||
<div class="underlay" />
|
||||
<div
|
||||
class="add-component"
|
||||
class:active={isAddingComponent}
|
||||
|
@ -293,34 +299,13 @@
|
|||
/>
|
||||
|
||||
<style>
|
||||
.loading {
|
||||
position: absolute;
|
||||
container-type: inline-size;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid transparent;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.loading.tablet {
|
||||
width: calc(1024px + 6px);
|
||||
max-height: calc(768px + 6px);
|
||||
}
|
||||
|
||||
.loading.mobile {
|
||||
width: calc(390px + 6px);
|
||||
max-height: calc(844px + 6px);
|
||||
}
|
||||
|
||||
.component-container {
|
||||
grid-row-start: middle;
|
||||
grid-column-start: middle;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
--client-padding: 6px;
|
||||
}
|
||||
.component-container iframe {
|
||||
border: 0;
|
||||
|
@ -329,6 +314,33 @@
|
|||
width: 100%;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.loading,
|
||||
.underlay {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
width: calc(100% - var(--client-padding) * 2);
|
||||
height: calc(100% - var(--client-padding) * 2);
|
||||
}
|
||||
.tablet .loading,
|
||||
.tablet .underlay {
|
||||
max-width: 1024px;
|
||||
max-height: 768px;
|
||||
}
|
||||
.mobile .loading,
|
||||
.mobile .underlay {
|
||||
max-width: 390px;
|
||||
max-height: 844px;
|
||||
}
|
||||
|
||||
.underlay {
|
||||
background: var(--spectrum-global-color-gray-200);
|
||||
z-index: -1;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.center {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
|
@ -574,15 +574,26 @@ export class ComponentStore extends BudiStore {
|
|||
return
|
||||
}
|
||||
|
||||
// Determine the next component to select after deletion
|
||||
// Determine the next component to select, and select it before deletion
|
||||
// to avoid an intermediate state of no component selection
|
||||
const state = get(this.store)
|
||||
let nextSelectedComponentId
|
||||
let nextId
|
||||
if (state.selectedComponentId === component._id) {
|
||||
nextSelectedComponentId = this.getNext()
|
||||
if (!nextSelectedComponentId) {
|
||||
nextSelectedComponentId = this.getPrevious()
|
||||
nextId = this.getNext()
|
||||
if (!nextId) {
|
||||
nextId = this.getPrevious()
|
||||
}
|
||||
}
|
||||
if (nextId) {
|
||||
// If this is the nav, select the screen instead
|
||||
if (nextId.endsWith("-navigation")) {
|
||||
nextId = nextId.replace("-navigation", "-screen")
|
||||
}
|
||||
this.update(state => {
|
||||
state.selectedComponentId = nextId
|
||||
return state
|
||||
})
|
||||
}
|
||||
|
||||
// Patch screen
|
||||
await screenStore.patch(screen => {
|
||||
|
@ -601,14 +612,6 @@ export class ComponentStore extends BudiStore {
|
|||
child => child._id !== component._id
|
||||
)
|
||||
})
|
||||
|
||||
// Update selected component if required
|
||||
if (nextSelectedComponentId) {
|
||||
this.update(state => {
|
||||
state.selectedComponentId = nextSelectedComponentId
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
copy(component, cut = false, selectParent = true) {
|
||||
|
@ -616,6 +619,7 @@ export class ComponentStore extends BudiStore {
|
|||
this.update(state => {
|
||||
state.componentToPaste = cloneDeep(component)
|
||||
state.componentToPaste.isCut = cut
|
||||
state.componentToPaste.screenId = get(screenStore).selectedScreenId
|
||||
return state
|
||||
})
|
||||
|
||||
|
@ -650,7 +654,7 @@ export class ComponentStore extends BudiStore {
|
|||
* @param {object} targetScreen
|
||||
* @returns
|
||||
*/
|
||||
async paste(targetComponent, mode, targetScreen) {
|
||||
async paste(targetComponent, mode, targetScreen, selectComponent = true) {
|
||||
const state = get(this.store)
|
||||
if (!state.componentToPaste) {
|
||||
return
|
||||
|
@ -674,8 +678,10 @@ export class ComponentStore extends BudiStore {
|
|||
return false
|
||||
}
|
||||
const cut = componentToPaste.isCut
|
||||
const sourceScreenId = componentToPaste.screenId
|
||||
const originalId = componentToPaste._id
|
||||
delete componentToPaste.isCut
|
||||
delete componentToPaste.screenId
|
||||
|
||||
// Make new component unique if copying
|
||||
if (!cut) {
|
||||
|
@ -683,6 +689,19 @@ export class ComponentStore extends BudiStore {
|
|||
}
|
||||
newComponentId = componentToPaste._id
|
||||
|
||||
// Strip grid position metadata if pasting into a new screen, but keep
|
||||
// alignment metadata
|
||||
if (sourceScreenId && sourceScreenId !== screen._id) {
|
||||
for (let style of Object.keys(componentToPaste._styles?.normal || {})) {
|
||||
if (
|
||||
style.startsWith("--grid") &&
|
||||
(style.endsWith("-start") || style.endsWith("-end"))
|
||||
) {
|
||||
delete componentToPaste._styles.normal[style]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old component if cutting
|
||||
if (cut) {
|
||||
const parent = findComponentParent(screen.props, originalId)
|
||||
|
@ -725,12 +744,13 @@ export class ComponentStore extends BudiStore {
|
|||
await screenStore.patch(patch, targetScreenId)
|
||||
|
||||
// Select the new component
|
||||
this.update(state => {
|
||||
state.selectedScreenId = targetScreenId
|
||||
state.selectedComponentId = newComponentId
|
||||
|
||||
return state
|
||||
})
|
||||
if (selectComponent) {
|
||||
this.update(state => {
|
||||
state.selectedScreenId = targetScreenId
|
||||
state.selectedComponentId = newComponentId
|
||||
return state
|
||||
})
|
||||
}
|
||||
|
||||
componentTreeNodesStore.makeNodeVisible(newComponentId)
|
||||
}
|
||||
|
|
|
@ -54,4 +54,28 @@ export class Component extends BaseStructure {
|
|||
getId() {
|
||||
return this._json._id
|
||||
}
|
||||
|
||||
gridDesktopColSpan(start, end) {
|
||||
this._json._styles.normal["--grid-desktop-col-start"] = start
|
||||
this._json._styles.normal["--grid-desktop-col-end"] = end
|
||||
return this
|
||||
}
|
||||
|
||||
gridDesktopRowSpan(start, end) {
|
||||
this._json._styles.normal["--grid-desktop-row-start"] = start
|
||||
this._json._styles.normal["--grid-desktop-row-end"] = end
|
||||
return this
|
||||
}
|
||||
|
||||
gridMobileColSpan(start, end) {
|
||||
this._json._styles.normal["--grid-mobile-col-start"] = start
|
||||
this._json._styles.normal["--grid-mobile-col-end"] = end
|
||||
return this
|
||||
}
|
||||
|
||||
gridMobileRowSpan(start, end) {
|
||||
this._json._styles.normal["--grid-mobile-row-start"] = start
|
||||
this._json._styles.normal["--grid-mobile-row-end"] = end
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ export class Screen extends BaseStructure {
|
|||
},
|
||||
_children: [],
|
||||
_instanceName: "",
|
||||
layout: "flex",
|
||||
direction: "column",
|
||||
hAlign: "stretch",
|
||||
vAlign: "top",
|
||||
|
|
|
@ -8,6 +8,7 @@ const blank = ({ route, screens }) => {
|
|||
|
||||
const template = new Screen()
|
||||
.instanceName("Blank screen")
|
||||
.customProps({ layout: "grid" })
|
||||
.role(Roles.BASIC)
|
||||
.route(validRoute)
|
||||
.json()
|
||||
|
|
|
@ -9,16 +9,21 @@ const inline = ({ tableOrView, permissions, screens }) => {
|
|||
.customProps({
|
||||
text: tableOrView.name,
|
||||
})
|
||||
.gridDesktopColSpan(1, 13)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const tableBlock = new Component("@budibase/standard-components/gridblock")
|
||||
.instanceName(`${tableOrView.name} - Table`)
|
||||
.customProps({
|
||||
table: tableOrView.datasourceSelectFormat,
|
||||
})
|
||||
.gridDesktopColSpan(1, 13)
|
||||
.gridDesktopRowSpan(3, 21)
|
||||
|
||||
const screenTemplate = new Screen()
|
||||
.route(getValidRoute(screens, tableOrView.name, permissions.write))
|
||||
.instanceName(`${tableOrView.name} - List`)
|
||||
.customProps({ layout: "grid" })
|
||||
.role(permissions.write)
|
||||
.autoTableId(tableOrView.id)
|
||||
.addChild(heading)
|
||||
|
|
|
@ -33,26 +33,22 @@ const modal = ({ tableOrView, permissions, screens }) => {
|
|||
type: "cta",
|
||||
})
|
||||
|
||||
buttonGroup.instanceName(`${tableOrView.name} - Create`).customProps({
|
||||
hAlign: "right",
|
||||
buttons: [createButton.json()],
|
||||
})
|
||||
|
||||
const tableHeader = new Component("@budibase/standard-components/container")
|
||||
.instanceName("Heading container")
|
||||
buttonGroup
|
||||
.instanceName(`${tableOrView.name} - Create`)
|
||||
.customProps({
|
||||
direction: "row",
|
||||
hAlign: "stretch",
|
||||
hAlign: "right",
|
||||
buttons: [createButton.json()],
|
||||
})
|
||||
.gridDesktopColSpan(7, 13)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const heading = new Component("@budibase/standard-components/heading")
|
||||
.instanceName("Table heading")
|
||||
.customProps({
|
||||
text: tableOrView.name,
|
||||
})
|
||||
|
||||
tableHeader.addChild(heading)
|
||||
tableHeader.addChild(buttonGroup)
|
||||
.gridDesktopColSpan(1, 7)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const createFormBlock = new Component(
|
||||
"@budibase/standard-components/formblock"
|
||||
|
@ -134,13 +130,17 @@ const modal = ({ tableOrView, permissions, screens }) => {
|
|||
],
|
||||
})
|
||||
.instanceName(`${tableOrView.name} - Table`)
|
||||
.gridDesktopColSpan(1, 13)
|
||||
.gridDesktopRowSpan(3, 21)
|
||||
|
||||
const template = new Screen()
|
||||
.route(getValidRoute(screens, tableOrView.name, permissions.write))
|
||||
.instanceName(`${tableOrView.name} - List and details`)
|
||||
.customProps({ layout: "grid" })
|
||||
.role(permissions.write)
|
||||
.autoTableId(tableOrView.id)
|
||||
.addChild(tableHeader)
|
||||
.addChild(buttonGroup)
|
||||
.addChild(heading)
|
||||
.addChild(tableBlock)
|
||||
.addChild(createRowModal)
|
||||
.addChild(detailsModal)
|
||||
|
|
|
@ -11,36 +11,41 @@ const getTableScreenTemplate = ({
|
|||
createScreenRoute,
|
||||
tableOrView,
|
||||
permissions,
|
||||
gridLayout,
|
||||
}) => {
|
||||
const newButton = new Component("@budibase/standard-components/button")
|
||||
.instanceName("New button")
|
||||
.customProps({
|
||||
text: "Create row",
|
||||
onClick: [
|
||||
{
|
||||
"##eventHandlerType": "Navigate To",
|
||||
parameters: {
|
||||
type: "url",
|
||||
url: createScreenRoute,
|
||||
},
|
||||
const buttonGroup = new Component("@budibase/standard-components/buttongroup")
|
||||
const createButton = new Component("@budibase/standard-components/button")
|
||||
|
||||
createButton.customProps({
|
||||
onClick: [
|
||||
{
|
||||
"##eventHandlerType": "Navigate To",
|
||||
parameters: {
|
||||
type: "url",
|
||||
url: createScreenRoute,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
text: "Create row",
|
||||
type: "cta",
|
||||
})
|
||||
|
||||
buttonGroup
|
||||
.instanceName(`${tableOrView.name} - Create`)
|
||||
.customProps({
|
||||
hAlign: "right",
|
||||
buttons: [createButton.json()],
|
||||
})
|
||||
.gridDesktopColSpan(7, 13)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const heading = new Component("@budibase/standard-components/heading")
|
||||
.instanceName("Table heading")
|
||||
.customProps({
|
||||
text: tableOrView.name,
|
||||
})
|
||||
|
||||
const tableHeader = new Component("@budibase/standard-components/container")
|
||||
.instanceName("Heading container")
|
||||
.customProps({
|
||||
direction: "row",
|
||||
hAlign: "stretch",
|
||||
})
|
||||
.addChild(heading)
|
||||
.addChild(newButton)
|
||||
.gridDesktopColSpan(1, 7)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const updateScreenRouteSegments = updateScreenRoute.split(":id")
|
||||
if (updateScreenRouteSegments.length !== 2) {
|
||||
|
@ -67,14 +72,18 @@ const getTableScreenTemplate = ({
|
|||
},
|
||||
],
|
||||
})
|
||||
.gridDesktopColSpan(1, 13)
|
||||
.gridDesktopRowSpan(3, 21)
|
||||
|
||||
const template = new Screen()
|
||||
.route(route)
|
||||
.instanceName(`${tableOrView.name} - List`)
|
||||
.customProps({ layout: gridLayout ? "grid" : "flex" })
|
||||
.role(permissions.write)
|
||||
.autoTableId(tableOrView.id)
|
||||
.addChild(tableHeader)
|
||||
.addChild(tableBlock)
|
||||
.addChild(heading)
|
||||
.addChild(buttonGroup)
|
||||
.json()
|
||||
|
||||
return {
|
||||
|
@ -300,6 +309,7 @@ const newScreen = ({ tableOrView, permissions, screens }) => {
|
|||
createScreenRoute,
|
||||
permissions,
|
||||
tableOrView,
|
||||
gridLayout: true,
|
||||
})
|
||||
|
||||
const updateScreenTemplate = getUpdateScreenTemplate({
|
||||
|
@ -307,6 +317,7 @@ const newScreen = ({ tableOrView, permissions, screens }) => {
|
|||
tableScreenRoute,
|
||||
tableOrView,
|
||||
permissions,
|
||||
gridLayout: false,
|
||||
})
|
||||
|
||||
const createScreenTemplate = getCreateScreenTemplate({
|
||||
|
@ -314,6 +325,7 @@ const newScreen = ({ tableOrView, permissions, screens }) => {
|
|||
tableScreenRoute,
|
||||
tableOrView,
|
||||
permissions,
|
||||
gridLayout: false,
|
||||
})
|
||||
|
||||
return [tableScreenTemplate, updateScreenTemplate, createScreenTemplate]
|
||||
|
|
|
@ -31,26 +31,22 @@ const sidePanel = ({ tableOrView, permissions, screens }) => {
|
|||
type: "cta",
|
||||
})
|
||||
|
||||
buttonGroup.instanceName(`${tableOrView.name} - Create`).customProps({
|
||||
hAlign: "right",
|
||||
buttons: [createButton.json()],
|
||||
})
|
||||
|
||||
const tableHeader = new Component("@budibase/standard-components/container")
|
||||
.instanceName("Heading container")
|
||||
buttonGroup
|
||||
.instanceName(`${tableOrView.name} - Create`)
|
||||
.customProps({
|
||||
direction: "row",
|
||||
hAlign: "stretch",
|
||||
hAlign: "right",
|
||||
buttons: [createButton.json()],
|
||||
})
|
||||
.gridDesktopColSpan(7, 13)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const heading = new Component("@budibase/standard-components/heading")
|
||||
.instanceName("Table heading")
|
||||
.customProps({
|
||||
text: tableOrView.name,
|
||||
})
|
||||
|
||||
tableHeader.addChild(heading)
|
||||
tableHeader.addChild(buttonGroup)
|
||||
.gridDesktopColSpan(1, 7)
|
||||
.gridDesktopRowSpan(1, 3)
|
||||
|
||||
const createFormBlock = new Component(
|
||||
"@budibase/standard-components/formblock"
|
||||
|
@ -130,13 +126,17 @@ const sidePanel = ({ tableOrView, permissions, screens }) => {
|
|||
],
|
||||
})
|
||||
.instanceName(`${tableOrView.name} - Table`)
|
||||
.gridDesktopColSpan(1, 13)
|
||||
.gridDesktopRowSpan(3, 21)
|
||||
|
||||
const template = new Screen()
|
||||
.route(getValidRoute(screens, tableOrView.name, permissions.write))
|
||||
.instanceName(`${tableOrView.name} - List and details`)
|
||||
.customProps({ layout: "grid" })
|
||||
.role(permissions.write)
|
||||
.autoTableId(tableOrView.id)
|
||||
.addChild(tableHeader)
|
||||
.addChild(heading)
|
||||
.addChild(buttonGroup)
|
||||
.addChild(tableBlock)
|
||||
.addChild(createRowSidePanel)
|
||||
.addChild(detailsSidePanel)
|
||||
|
|
|
@ -18,14 +18,37 @@
|
|||
"numberLike": {
|
||||
"supported": ["number", "boolean"],
|
||||
"partialSupport": [
|
||||
{ "type": "longform", "message": "stringAsNumber" },
|
||||
{ "type": "string", "message": "stringAsNumber" },
|
||||
{ "type": "bigint", "message": "stringAsNumber" },
|
||||
{ "type": "options", "message": "stringAsNumber" },
|
||||
{ "type": "formula", "message": "stringAsNumber" },
|
||||
{ "type": "datetime", "message": "dateAsNumber" }
|
||||
{
|
||||
"type": "longform",
|
||||
"message": "stringAsNumber"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"message": "stringAsNumber"
|
||||
},
|
||||
{
|
||||
"type": "bigint",
|
||||
"message": "stringAsNumber"
|
||||
},
|
||||
{
|
||||
"type": "options",
|
||||
"message": "stringAsNumber"
|
||||
},
|
||||
{
|
||||
"type": "formula",
|
||||
"message": "stringAsNumber"
|
||||
},
|
||||
{
|
||||
"type": "datetime",
|
||||
"message": "dateAsNumber"
|
||||
}
|
||||
],
|
||||
"unsupported": [{ "type": "json", "message": "jsonPrimitivesOnly" }]
|
||||
"unsupported": [
|
||||
{
|
||||
"type": "json",
|
||||
"message": "jsonPrimitivesOnly"
|
||||
}
|
||||
]
|
||||
},
|
||||
"stringLike": {
|
||||
"supported": [
|
||||
|
@ -37,19 +60,47 @@
|
|||
"boolean",
|
||||
"datetime"
|
||||
],
|
||||
"unsupported": [{ "type": "json", "message": "jsonPrimitivesOnly" }]
|
||||
"unsupported": [
|
||||
{
|
||||
"type": "json",
|
||||
"message": "jsonPrimitivesOnly"
|
||||
}
|
||||
]
|
||||
},
|
||||
"datetimeLike": {
|
||||
"supported": ["datetime"],
|
||||
"partialSupport": [
|
||||
{ "type": "longform", "message": "stringAsDate" },
|
||||
{ "type": "string", "message": "stringAsDate" },
|
||||
{ "type": "options", "message": "stringAsDate" },
|
||||
{ "type": "formula", "message": "stringAsDate" },
|
||||
{ "type": "bigint", "message": "stringAsDate" },
|
||||
{ "type": "number", "message": "numberAsDate" }
|
||||
{
|
||||
"type": "longform",
|
||||
"message": "stringAsDate"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"message": "stringAsDate"
|
||||
},
|
||||
{
|
||||
"type": "options",
|
||||
"message": "stringAsDate"
|
||||
},
|
||||
{
|
||||
"type": "formula",
|
||||
"message": "stringAsDate"
|
||||
},
|
||||
{
|
||||
"type": "bigint",
|
||||
"message": "stringAsDate"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"message": "numberAsDate"
|
||||
}
|
||||
],
|
||||
"unsupported": [{ "type": "json", "message": "jsonPrimitivesOnly" }]
|
||||
"unsupported": [
|
||||
{
|
||||
"type": "json",
|
||||
"message": "jsonPrimitivesOnly"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"layout": {
|
||||
|
@ -114,11 +165,37 @@
|
|||
"icon": "Selection",
|
||||
"hasChildren": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"width": 500,
|
||||
"height": 200
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"styles": ["padding", "size", "background", "border", "shadow"],
|
||||
"settings": [
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Layout",
|
||||
"key": "layout",
|
||||
"showInBar": true,
|
||||
"barStyle": "buttons",
|
||||
"options": [
|
||||
{
|
||||
"label": "Flex",
|
||||
"value": "flex",
|
||||
"barIcon": "ModernGridView",
|
||||
"barTitle": "Flex layout"
|
||||
},
|
||||
{
|
||||
"label": "Grid",
|
||||
"value": "grid",
|
||||
"barIcon": "ViewGrid",
|
||||
"barTitle": "Grid layout"
|
||||
}
|
||||
],
|
||||
"defaultValue": "grid"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"label": "Direction",
|
||||
|
@ -139,7 +216,12 @@
|
|||
"barTitle": "Row layout"
|
||||
}
|
||||
],
|
||||
"defaultValue": "column"
|
||||
"defaultValue": "column",
|
||||
"dependsOn": {
|
||||
"setting": "layout",
|
||||
"value": "grid",
|
||||
"invert": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
|
@ -173,7 +255,12 @@
|
|||
"barTitle": "Align stretched horizontally"
|
||||
}
|
||||
],
|
||||
"defaultValue": "stretch"
|
||||
"defaultValue": "stretch",
|
||||
"dependsOn": {
|
||||
"setting": "layout",
|
||||
"value": "grid",
|
||||
"invert": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
|
@ -207,7 +294,12 @@
|
|||
"barTitle": "Align stretched vertically"
|
||||
}
|
||||
],
|
||||
"defaultValue": "top"
|
||||
"defaultValue": "top",
|
||||
"dependsOn": {
|
||||
"setting": "layout",
|
||||
"value": "grid",
|
||||
"invert": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
|
@ -229,7 +321,12 @@
|
|||
"barTitle": "Grow container"
|
||||
}
|
||||
],
|
||||
"defaultValue": "shrink"
|
||||
"defaultValue": "shrink",
|
||||
"dependsOn": {
|
||||
"setting": "layout",
|
||||
"value": "grid",
|
||||
"invert": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
|
@ -255,7 +352,12 @@
|
|||
"value": "L"
|
||||
}
|
||||
],
|
||||
"defaultValue": "M"
|
||||
"defaultValue": "M",
|
||||
"dependsOn": {
|
||||
"setting": "layout",
|
||||
"value": "grid",
|
||||
"invert": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
|
@ -263,7 +365,12 @@
|
|||
"key": "wrap",
|
||||
"showInBar": true,
|
||||
"barIcon": "ModernGridView",
|
||||
"barTitle": "Wrap"
|
||||
"barTitle": "Wrap",
|
||||
"dependsOn": {
|
||||
"setting": "layout",
|
||||
"value": "grid",
|
||||
"invert": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "event",
|
||||
|
@ -280,8 +387,12 @@
|
|||
"illegalChildren": ["section"],
|
||||
"showEmptyState": false,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 100
|
||||
"width": 600,
|
||||
"height": 200
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -302,6 +413,14 @@
|
|||
"name": "Button group",
|
||||
"icon": "Button",
|
||||
"hasChildren": false,
|
||||
"size": {
|
||||
"width": 200,
|
||||
"height": 60
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"section": true,
|
||||
|
@ -484,9 +603,13 @@
|
|||
"icon": "Button",
|
||||
"editable": true,
|
||||
"size": {
|
||||
"width": 105,
|
||||
"width": 120,
|
||||
"height": 32
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "center",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -648,8 +771,12 @@
|
|||
"illegalChildren": ["section"],
|
||||
"hasChildren": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 100
|
||||
"width": 500,
|
||||
"height": 200
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -1143,6 +1270,10 @@
|
|||
"width": 100,
|
||||
"height": 25
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "center",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -1220,6 +1351,7 @@
|
|||
"icon": "Images",
|
||||
"hasChildren": true,
|
||||
"styles": ["size"],
|
||||
"showEmptyState": false,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 300
|
||||
|
@ -1285,6 +1417,10 @@
|
|||
"width": 25,
|
||||
"height": 25
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "center",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "icon",
|
||||
|
@ -1598,6 +1734,10 @@
|
|||
"width": 260,
|
||||
"height": 143
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "center",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -1631,6 +1771,10 @@
|
|||
"width": 400,
|
||||
"height": 100
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -1647,7 +1791,11 @@
|
|||
"icon": "GraphBarVertical",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -1816,7 +1964,11 @@
|
|||
"icon": "GraphTrend",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -1980,7 +2132,11 @@
|
|||
"icon": "GraphAreaStacked",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -2156,7 +2312,11 @@
|
|||
"icon": "GraphPie",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -2296,7 +2456,11 @@
|
|||
"icon": "GraphDonut",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -2436,7 +2600,11 @@
|
|||
"icon": "GraphBarVerticalStacked",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -2553,7 +2721,11 @@
|
|||
"icon": "Histogram",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -2704,11 +2876,15 @@
|
|||
"UpdateFieldValue",
|
||||
"ScrollTo"
|
||||
],
|
||||
"styles": ["size"],
|
||||
"styles": ["padding", "size", "background", "border", "shadow"],
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 400
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "select",
|
||||
|
@ -2864,7 +3040,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 32
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -2895,7 +3071,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 32
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3031,7 +3207,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3133,7 +3309,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3219,7 +3395,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3305,7 +3481,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3519,7 +3695,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3725,8 +3901,8 @@
|
|||
"editable": true,
|
||||
"requiredAncestors": ["form"],
|
||||
"size": {
|
||||
"width": 20,
|
||||
"height": 20
|
||||
"width": 400,
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3852,7 +4028,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 150
|
||||
"height": 100
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -3976,7 +4152,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -4094,7 +4270,7 @@
|
|||
"styles": ["size"],
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -4167,7 +4343,10 @@
|
|||
"label": "High",
|
||||
"value": 3136
|
||||
},
|
||||
{ "label": "Custom", "value": "custom" }
|
||||
{
|
||||
"label": "Custom",
|
||||
"value": "custom"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -4251,7 +4430,7 @@
|
|||
"styles": ["size"],
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -4303,6 +4482,10 @@
|
|||
"width": 400,
|
||||
"height": 320
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "dataProvider",
|
||||
|
@ -4602,7 +4785,7 @@
|
|||
"editable": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 50
|
||||
"height": 60
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -4872,8 +5055,12 @@
|
|||
"hasChildren": true,
|
||||
"actions": ["RefreshDatasource"],
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 100
|
||||
"width": 500,
|
||||
"height": 200
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -5126,6 +5313,10 @@
|
|||
"width": 300,
|
||||
"height": 120
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "center",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -5190,6 +5381,10 @@
|
|||
"width": 100,
|
||||
"height": 35
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "center",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "dataProvider",
|
||||
|
@ -5236,6 +5431,14 @@
|
|||
"name": "Chart Block",
|
||||
"icon": "GraphPie",
|
||||
"hasChildren": false,
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 420
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "center"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "select",
|
||||
|
@ -6159,6 +6362,10 @@
|
|||
"width": 600,
|
||||
"height": 400
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -6368,8 +6575,12 @@
|
|||
"illegalChildren": ["section", "rowexplorer"],
|
||||
"hasChildren": true,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"height": 100
|
||||
"width": 500,
|
||||
"height": 200
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -6624,6 +6835,10 @@
|
|||
"width": 400,
|
||||
"height": 100
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "start"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "text",
|
||||
|
@ -6640,10 +6855,14 @@
|
|||
"hasChildren": false,
|
||||
"ejectable": false,
|
||||
"size": {
|
||||
"width": 400,
|
||||
"width": 600,
|
||||
"height": 400
|
||||
},
|
||||
"styles": ["size"],
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "start"
|
||||
},
|
||||
"styles": ["padding", "size", "background", "border", "shadow"],
|
||||
"settings": [
|
||||
{
|
||||
"type": "table",
|
||||
|
@ -6827,13 +7046,17 @@
|
|||
"formblock": {
|
||||
"name": "Form Block",
|
||||
"icon": "Form",
|
||||
"styles": ["size"],
|
||||
"styles": ["padding", "size", "background", "border", "shadow"],
|
||||
"block": true,
|
||||
"info": "Form blocks are only compatible with internal or SQL tables",
|
||||
"size": {
|
||||
"width": 400,
|
||||
"width": 600,
|
||||
"height": 400
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "start"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "table",
|
||||
|
@ -6983,6 +7206,7 @@
|
|||
"name": "Side Panel",
|
||||
"icon": "RailRight",
|
||||
"hasChildren": true,
|
||||
"ignoresLayout": true,
|
||||
"illegalChildren": ["section", "sidepanel", "modal"],
|
||||
"showEmptyState": false,
|
||||
"draggable": false,
|
||||
|
@ -7006,6 +7230,7 @@
|
|||
"icon": "MBox",
|
||||
"hasChildren": true,
|
||||
"illegalChildren": ["section", "modal", "sidepanel"],
|
||||
"ignoresLayout": true,
|
||||
"showEmptyState": false,
|
||||
"draggable": false,
|
||||
"info": "Modals are hidden by default. They will only be revealed when triggered by the 'Open Modal' action.",
|
||||
|
@ -7052,8 +7277,12 @@
|
|||
"name": "Row Explorer Block",
|
||||
"icon": "PersonalizationField",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 400
|
||||
"width": 800,
|
||||
"height": 426
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
|
@ -7167,23 +7396,6 @@
|
|||
"scope": "local"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
"name": "Grid",
|
||||
"icon": "ViewGrid",
|
||||
"hasChildren": true,
|
||||
"settings": [
|
||||
{
|
||||
"type": "number",
|
||||
"key": "cols",
|
||||
"label": "Columns"
|
||||
},
|
||||
{
|
||||
"type": "number",
|
||||
"key": "rows",
|
||||
"label": "Rows"
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridblock": {
|
||||
"name": "Table",
|
||||
"icon": "Table",
|
||||
|
@ -7192,6 +7404,10 @@
|
|||
"width": 600,
|
||||
"height": 400
|
||||
},
|
||||
"grid": {
|
||||
"hAlign": "stretch",
|
||||
"vAlign": "stretch"
|
||||
},
|
||||
"settings": [
|
||||
{
|
||||
"type": "dataSource",
|
||||
|
|
|
@ -235,9 +235,6 @@
|
|||
/>
|
||||
{/key}
|
||||
|
||||
<!-- Modal container to ensure they sit on top -->
|
||||
<div class="modal-container" />
|
||||
|
||||
<!-- Layers on top of app -->
|
||||
<NotificationDisplay />
|
||||
<ConfirmationDisplay />
|
||||
|
@ -284,7 +281,7 @@
|
|||
visibility: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
overflow: clip;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
@ -301,7 +298,7 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
overflow: clip;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
@ -311,7 +308,7 @@
|
|||
}
|
||||
|
||||
#app-root {
|
||||
overflow: hidden;
|
||||
overflow: clip;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
@ -327,6 +324,7 @@
|
|||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.error {
|
||||
|
@ -356,22 +354,16 @@
|
|||
}
|
||||
|
||||
/* Preview styles */
|
||||
/* The additional 6px of size is to account for 4px padding and 2px border */
|
||||
#clip-root.preview {
|
||||
padding: 2px;
|
||||
padding: 6px;
|
||||
}
|
||||
#clip-root.tablet-preview {
|
||||
width: calc(1024px + 6px);
|
||||
height: calc(768px + 6px);
|
||||
width: calc(1024px + 12px);
|
||||
height: calc(768px + 12px);
|
||||
}
|
||||
#clip-root.mobile-preview {
|
||||
width: calc(390px + 6px);
|
||||
height: calc(844px + 6px);
|
||||
}
|
||||
|
||||
.preview #app-root {
|
||||
border: 1px solid var(--spectrum-global-color-gray-300);
|
||||
border-radius: 4px;
|
||||
width: calc(390px + 12px);
|
||||
height: calc(844px + 12px);
|
||||
}
|
||||
|
||||
/* Print styles */
|
||||
|
|
|
@ -39,8 +39,10 @@
|
|||
getActionContextKey,
|
||||
getActionDependentContextKeys,
|
||||
} from "../utils/buttonActions.js"
|
||||
import { gridLayout } from "utils/grid.js"
|
||||
|
||||
export let instance = {}
|
||||
export let parent = null
|
||||
export let isLayout = false
|
||||
export let isRoot = false
|
||||
export let isBlock = false
|
||||
|
@ -102,8 +104,8 @@
|
|||
let settingsDefinitionMap
|
||||
let missingRequiredSettings = false
|
||||
|
||||
// Temporary styles which can be added in the app preview for things like DND.
|
||||
// We clear these whenever a new instance is received.
|
||||
// Temporary styles which can be added in the app preview for things like
|
||||
// DND. We clear these whenever a new instance is received.
|
||||
let ephemeralStyles
|
||||
|
||||
// Single string of all HBS blocks, used to check if we use a certain binding
|
||||
|
@ -193,19 +195,37 @@
|
|||
$: pad = pad || (interactive && hasChildren && inDndPath)
|
||||
$: $dndIsDragging, (pad = false)
|
||||
|
||||
// Themes
|
||||
$: currentTheme = $context?.device?.theme
|
||||
$: darkMode = !currentTheme?.includes("light")
|
||||
|
||||
// Apply ephemeral styles (such as when resizing grid components)
|
||||
$: normalStyles = {
|
||||
...instance._styles?.normal,
|
||||
...ephemeralStyles,
|
||||
}
|
||||
|
||||
// Metadata to pass into grid action to apply CSS
|
||||
const insideGrid =
|
||||
parent?._component.endsWith("/container") && parent?.layout === "grid"
|
||||
$: gridMetadata = {
|
||||
insideGrid,
|
||||
ignoresLayout: definition?.ignoresLayout === true,
|
||||
id,
|
||||
interactive,
|
||||
styles: normalStyles,
|
||||
draggable,
|
||||
definition,
|
||||
errored: errorState,
|
||||
}
|
||||
|
||||
// Update component context
|
||||
$: store.set({
|
||||
id,
|
||||
children: children.length,
|
||||
styles: {
|
||||
...instance._styles,
|
||||
normal: {
|
||||
...instance._styles?.normal,
|
||||
...ephemeralStyles,
|
||||
},
|
||||
normal: normalStyles,
|
||||
custom: customCSS,
|
||||
id,
|
||||
empty: emptyState,
|
||||
|
@ -242,6 +262,9 @@
|
|||
lastInstanceKey = instanceKey
|
||||
}
|
||||
|
||||
// Reset ephemeral state
|
||||
ephemeralStyles = null
|
||||
|
||||
// Pull definition and constructor
|
||||
const component = instance._component
|
||||
constructor = componentStore.actions.getComponentConstructor(component)
|
||||
|
@ -561,19 +584,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
const scrollIntoView = () => {
|
||||
// Don't scroll into view if we selected this component because we were
|
||||
// starting dragging on it
|
||||
if (get(dndIsDragging)) {
|
||||
return
|
||||
}
|
||||
const node = document.getElementsByClassName(id)?.[0]?.children[0]
|
||||
const scrollIntoView = async () => {
|
||||
const className = insideGrid ? id : `${id}-dom`
|
||||
const node = document.getElementsByClassName(className)[0]
|
||||
if (!node) {
|
||||
return
|
||||
}
|
||||
node.style.scrollMargin = "100px"
|
||||
// Don't scroll into view if we selected this component because we were
|
||||
// starting dragging on it
|
||||
if (
|
||||
get(dndIsDragging) ||
|
||||
(insideGrid && node.classList.contains("dragging"))
|
||||
) {
|
||||
return
|
||||
}
|
||||
node.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
behavior: "instant",
|
||||
block: "nearest",
|
||||
inline: "start",
|
||||
})
|
||||
|
@ -650,6 +676,7 @@
|
|||
data-name={name}
|
||||
data-icon={icon}
|
||||
data-parent={$component.id}
|
||||
use:gridLayout={gridMetadata}
|
||||
>
|
||||
{#if errorState}
|
||||
<ComponentErrorState
|
||||
|
@ -660,7 +687,7 @@
|
|||
<svelte:component this={constructor} bind:this={ref} {...initialSettings}>
|
||||
{#if children.length}
|
||||
{#each children as child (child._id)}
|
||||
<svelte:self instance={child} />
|
||||
<svelte:self instance={child} parent={instance} />
|
||||
{/each}
|
||||
{:else if emptyState}
|
||||
{#if isRoot}
|
||||
|
@ -687,7 +714,7 @@
|
|||
border-radius: 4px !important;
|
||||
transition: padding 260ms ease-out, border 260ms ease-out;
|
||||
}
|
||||
.interactive :global(*) {
|
||||
cursor: default;
|
||||
.interactive {
|
||||
cursor: default !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -71,4 +71,7 @@
|
|||
div {
|
||||
position: relative;
|
||||
}
|
||||
div :global(> .component > *) {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
const onLoadActions = memo()
|
||||
|
||||
// Get the screen definition for the current route
|
||||
$: screenDefinition = $screenStore.activeScreen?.props
|
||||
$: onLoadActions.set($screenStore.activeScreen?.onLoad)
|
||||
$: screen = $screenStore.activeScreen
|
||||
$: screenDefinition = { ...screen?.props, addEmptyRows: true }
|
||||
$: onLoadActions.set(screen?.onLoad)
|
||||
$: runOnLoadActions($onLoadActions, params)
|
||||
|
||||
// Enrich and execute any on load actions.
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import Placeholder from "./Placeholder.svelte"
|
||||
|
||||
const { styleable, builderStore } = getContext("sdk")
|
||||
const { styleable } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
||||
export let url
|
||||
|
@ -19,20 +18,11 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
{#if url}
|
||||
<div class="outer" use:styleable={$component.styles}>
|
||||
<div class="inner" {style}>
|
||||
<slot />
|
||||
</div>
|
||||
<div class="outer" use:styleable={$component.styles}>
|
||||
<div class="inner" {style}>
|
||||
<slot />
|
||||
</div>
|
||||
{:else if $builderStore.inBuilder}
|
||||
<div
|
||||
class="placeholder"
|
||||
use:styleable={{ ...$component.styles, empty: true }}
|
||||
>
|
||||
<Placeholder />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.outer {
|
||||
|
@ -49,9 +39,4 @@
|
|||
background-size: cover;
|
||||
background-position: center center;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
gap,
|
||||
wrap: true,
|
||||
}}
|
||||
styles={{
|
||||
normal: {
|
||||
height: "100%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{#each buttons as { text, type, quiet, disabled, onClick, size, icon, gap }}
|
||||
<BlockComponent
|
||||
|
|
|
@ -1,102 +0,0 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
|
||||
const component = getContext("component")
|
||||
const { styleable, builderStore } = getContext("sdk")
|
||||
|
||||
export let cols = 12
|
||||
export let rows = 12
|
||||
|
||||
// Deliberately non-reactive as we want this fixed whenever the grid renders
|
||||
const defaultColSpan = Math.ceil((cols + 1) / 2)
|
||||
const defaultRowSpan = Math.ceil((rows + 1) / 2)
|
||||
|
||||
$: coords = generateCoords(rows, cols)
|
||||
|
||||
const generateCoords = (rows, cols) => {
|
||||
let grid = []
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
grid.push({ row, col })
|
||||
}
|
||||
}
|
||||
return grid
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="grid"
|
||||
use:styleable={{
|
||||
...$component.styles,
|
||||
normal: {
|
||||
...$component.styles?.normal,
|
||||
"--cols": cols,
|
||||
"--rows": rows,
|
||||
"--default-col-span": defaultColSpan,
|
||||
"--default-row-span": defaultRowSpan,
|
||||
gap: "0 !important",
|
||||
},
|
||||
}}
|
||||
data-rows={rows}
|
||||
data-cols={cols}
|
||||
>
|
||||
{#if $builderStore.inBuilder}
|
||||
<div class="underlay">
|
||||
{#each coords as _}
|
||||
<div class="placeholder" />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/*
|
||||
Ensure all children of containers which are top level children of
|
||||
grids do not overflow
|
||||
*/
|
||||
:global(.grid > .component > .valid-container > .component > *) {
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Ensure all top level children have some grid styles set */
|
||||
:global(.grid > .component > *) {
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
height: auto;
|
||||
grid-column-start: 1;
|
||||
grid-column-end: var(--default-col-span);
|
||||
grid-row-start: 1;
|
||||
grid-row-end: var(--default-row-span);
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.grid {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
}
|
||||
.grid,
|
||||
.underlay {
|
||||
display: grid;
|
||||
grid-template-rows: repeat(var(--rows), 1fr);
|
||||
grid-template-columns: repeat(var(--cols), 1fr);
|
||||
}
|
||||
.underlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
grid-gap: 2px;
|
||||
background-color: var(--spectrum-global-color-gray-200);
|
||||
border: 2px solid var(--spectrum-global-color-gray-200);
|
||||
}
|
||||
.underlay {
|
||||
z-index: -1;
|
||||
}
|
||||
.placeholder {
|
||||
background-color: var(--spectrum-global-color-gray-100);
|
||||
}
|
||||
</style>
|
|
@ -198,7 +198,7 @@
|
|||
overflow: hidden;
|
||||
height: 410px;
|
||||
}
|
||||
div.in-builder :global(*) {
|
||||
pointer-events: none;
|
||||
div.in-builder :global(> *) {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
export let logoLinkUrl
|
||||
export let openLogoLinkInNewTab
|
||||
export let textAlign
|
||||
|
||||
export let embedded = false
|
||||
|
||||
const NavigationClasses = {
|
||||
|
@ -339,6 +338,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-container" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@ -353,6 +353,9 @@
|
|||
z-index: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
/* Deliberately unitless as we need to do unitless calculations in grids */
|
||||
--grid-spacing: 4;
|
||||
}
|
||||
.component {
|
||||
display: contents;
|
||||
|
@ -415,7 +418,6 @@
|
|||
color: var(--navTextColor);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.nav :global(h1) {
|
||||
color: var(--navTextColor);
|
||||
}
|
||||
|
@ -481,9 +483,10 @@
|
|||
position: relative;
|
||||
padding: 32px;
|
||||
}
|
||||
.main.size--max {
|
||||
padding: 0;
|
||||
.main:not(.size--max):has(.screenslot-dom > .component > .grid) {
|
||||
padding: calc(32px - var(--grid-spacing) * 2px);
|
||||
}
|
||||
|
||||
.layout--none .main {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -502,6 +505,9 @@
|
|||
.size--max {
|
||||
width: 100%;
|
||||
}
|
||||
.main.size--max {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Nav components */
|
||||
.burger {
|
||||
|
@ -613,6 +619,10 @@
|
|||
.mobile:not(.layout--none) .main {
|
||||
padding: 16px;
|
||||
}
|
||||
.mobile:not(.layout--none)
|
||||
.main:not(.size--max):has(.screenslot-dom > .component > .grid) {
|
||||
padding: 6px;
|
||||
}
|
||||
.mobile .main.size--max {
|
||||
padding: 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { getContext } from "svelte"
|
||||
import Placeholder from "./Placeholder.svelte"
|
||||
import Container from "./Container.svelte"
|
||||
import Container from "./container/Container.svelte"
|
||||
|
||||
const { Provider, ContextScopes } = getContext("sdk")
|
||||
const component = getContext("component")
|
||||
|
|
|
@ -57,4 +57,7 @@
|
|||
.spectrum-Tag--sizeL {
|
||||
padding: 0 var(--spectrum-global-dimension-size-150);
|
||||
}
|
||||
.spectrum-Tag-label {
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<script>
|
||||
import GridContainer from "./GridContainer.svelte"
|
||||
import FlexContainer from "./FlexContainer.svelte"
|
||||
|
||||
export let layout = "flex"
|
||||
|
||||
$: component = layout === "grid" ? GridContainer : FlexContainer
|
||||
</script>
|
||||
|
||||
<svelte:component this={component} {...$$props}>
|
||||
<slot />
|
||||
</svelte:component>
|
|
@ -12,7 +12,7 @@
|
|||
export let wrap
|
||||
export let onClick
|
||||
|
||||
$: directionClass = direction ? `valid-container direction-${direction}` : ""
|
||||
$: directionClass = direction ? `flex-container direction-${direction}` : ""
|
||||
$: hAlignClass = hAlign ? `hAlign-${hAlign}` : ""
|
||||
$: vAlignClass = vAlign ? `vAlign-${vAlign}` : ""
|
||||
$: sizeClass = size ? `size-${size}` : ""
|
||||
|
@ -39,11 +39,11 @@
|
|||
</div>
|
||||
|
||||
<style>
|
||||
.valid-container {
|
||||
.flex-container {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
}
|
||||
.valid-container :global(.component > *) {
|
||||
.flex-container :global(.component > *) {
|
||||
max-width: 100%;
|
||||
}
|
||||
.direction-row {
|
|
@ -0,0 +1,264 @@
|
|||
<script>
|
||||
import { getContext, onMount } from "svelte"
|
||||
import { writable } from "svelte/store"
|
||||
import { GridRowHeight, GridColumns } from "constants"
|
||||
import { memo } from "@budibase/frontend-core"
|
||||
|
||||
export let addEmptyRows = false
|
||||
|
||||
const component = getContext("component")
|
||||
const { styleable, builderStore } = getContext("sdk")
|
||||
const context = getContext("context")
|
||||
|
||||
let width
|
||||
let height
|
||||
let ref
|
||||
let children = writable({})
|
||||
let mounted = false
|
||||
let styles = memo({})
|
||||
|
||||
$: inBuilder = $builderStore.inBuilder
|
||||
$: requiredRows = calculateRequiredRows(
|
||||
$children,
|
||||
mobile,
|
||||
addEmptyRows && inBuilder
|
||||
)
|
||||
$: requiredHeight = requiredRows * GridRowHeight
|
||||
$: availableRows = Math.floor(height / GridRowHeight)
|
||||
$: rows = Math.max(requiredRows, availableRows)
|
||||
$: mobile = $context.device.mobile
|
||||
$: empty = $component.empty
|
||||
$: colSize = width / GridColumns
|
||||
$: styles.set({
|
||||
...$component.styles,
|
||||
normal: {
|
||||
...$component.styles?.normal,
|
||||
"--height": `${requiredHeight}px`,
|
||||
"--min-height": $component.styles?.normal?.height || 0,
|
||||
"--cols": GridColumns,
|
||||
"--rows": rows,
|
||||
"--col-size": colSize,
|
||||
"--row-size": GridRowHeight,
|
||||
},
|
||||
empty: false,
|
||||
})
|
||||
|
||||
// Calculates the minimum number of rows required to render all child
|
||||
// components, on a certain device type
|
||||
const calculateRequiredRows = (children, mobile, addEmptyRows) => {
|
||||
const key = mobile ? "mobileRowEnd" : "desktopRowEnd"
|
||||
let max = 2
|
||||
for (let id of Object.keys(children)) {
|
||||
if (children[id][key] > max) {
|
||||
max = children[id][key]
|
||||
}
|
||||
}
|
||||
let requiredRows = max - 1
|
||||
if (addEmptyRows) {
|
||||
return Math.ceil((requiredRows + 10) / 10) * 10
|
||||
} else {
|
||||
return requiredRows
|
||||
}
|
||||
}
|
||||
|
||||
// Stores metadata about a child node as constraints for determining grid size
|
||||
const storeChild = node => {
|
||||
children.update(state => ({
|
||||
...state,
|
||||
[node.dataset.id]: {
|
||||
desktopRowEnd: parseInt(node.dataset.gridDesktopRowEnd),
|
||||
mobileRowEnd: parseInt(node.dataset.gridMobileRowEnd),
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
// Removes constraint metadata for a certain child node
|
||||
const removeChild = node => {
|
||||
children.update(state => {
|
||||
delete state[node.dataset.id]
|
||||
return { ...state }
|
||||
})
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
let observer
|
||||
// Set up an observer to watch for changes in metadata attributes of child
|
||||
// components, as well as child addition and deletion
|
||||
observer = new MutationObserver(mutations => {
|
||||
for (let mutation of mutations) {
|
||||
const { target, type, addedNodes, removedNodes } = mutation
|
||||
if (target === ref) {
|
||||
if (addedNodes[0]?.classList?.contains("component")) {
|
||||
// We've added a new child component inside the grid, so we need
|
||||
// to consider it when determining required rows
|
||||
storeChild(addedNodes[0])
|
||||
} else if (removedNodes[0]?.classList?.contains("component")) {
|
||||
// We've removed a child component inside the grid, so we need
|
||||
// to stop considering it when determining required rows
|
||||
removeChild(removedNodes[0])
|
||||
}
|
||||
} else if (
|
||||
type === "attributes" &&
|
||||
target.parentNode === ref &&
|
||||
target.classList.contains("component")
|
||||
) {
|
||||
// We've updated the size or position of a child
|
||||
storeChild(target)
|
||||
}
|
||||
}
|
||||
})
|
||||
observer.observe(ref, {
|
||||
childList: true,
|
||||
attributes: true,
|
||||
subtree: true,
|
||||
attributeFilter: [
|
||||
"data-grid-desktop-row-end",
|
||||
"data-grid-mobile-row-end",
|
||||
],
|
||||
})
|
||||
|
||||
// Now that the observer is set up, we mark the grid as mounted to mount
|
||||
// our child components
|
||||
mounted = true
|
||||
|
||||
// Cleanup our observer
|
||||
return () => {
|
||||
observer?.disconnect()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={ref}
|
||||
class="grid"
|
||||
class:mobile
|
||||
bind:clientWidth={width}
|
||||
bind:clientHeight={height}
|
||||
use:styleable={$styles}
|
||||
data-cols={GridColumns}
|
||||
data-col-size={colSize}
|
||||
>
|
||||
{#if inBuilder}
|
||||
<div class="underlay">
|
||||
{#each { length: GridColumns * rows } as _, idx}
|
||||
<div class="placeholder" class:first-col={idx % GridColumns === 0} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Only render the slot if not empty, as we don't want the placeholder -->
|
||||
{#if !empty && mounted}
|
||||
<slot />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.grid,
|
||||
.underlay {
|
||||
height: var(--height) !important;
|
||||
min-height: var(--min-height) !important;
|
||||
max-height: none !important;
|
||||
display: grid;
|
||||
gap: 0;
|
||||
grid-template-rows: repeat(var(--rows), calc(var(--row-size) * 1px));
|
||||
grid-template-columns: repeat(var(--cols), calc(var(--col-size) * 1px));
|
||||
position: relative;
|
||||
}
|
||||
.underlay {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-top: 1px solid var(--spectrum-global-color-gray-900);
|
||||
opacity: 0.1;
|
||||
pointer-events: none;
|
||||
}
|
||||
.underlay {
|
||||
z-index: 0;
|
||||
}
|
||||
.placeholder {
|
||||
border-bottom: 1px solid var(--spectrum-global-color-gray-900);
|
||||
border-right: 1px solid var(--spectrum-global-color-gray-900);
|
||||
}
|
||||
.placeholder.first-col {
|
||||
border-left: 1px solid var(--spectrum-global-color-gray-900);
|
||||
}
|
||||
|
||||
/* Highlight grid lines when resizing children */
|
||||
:global(.grid.highlight > .underlay) {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
/* Highlight sibling borders when resizing childern */
|
||||
:global(.grid.highlight > .component:not(.dragging)) {
|
||||
outline: 2px solid var(--spectrum-global-color-static-blue-200);
|
||||
pointer-events: none !important;
|
||||
}
|
||||
:global(.grid.highlight > .component.dragging) {
|
||||
z-index: 999 !important;
|
||||
}
|
||||
|
||||
/* Ensure all top level children have grid styles applied */
|
||||
.grid :global(> .component:not(.ignores-layout)) {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
pointer-events: all;
|
||||
position: relative;
|
||||
padding: calc(var(--grid-spacing) * 1px);
|
||||
margin: calc(var(--grid-spacing) * 1px);
|
||||
|
||||
/* On desktop, use desktop metadata and fall back to mobile */
|
||||
--col-start: var(--grid-desktop-col-start, var(--grid-mobile-col-start));
|
||||
--col-end: var(--grid-desktop-col-end, var(--grid-mobile-col-end));
|
||||
--row-start: var(--grid-desktop-row-start, var(--grid-mobile-row-start));
|
||||
--row-end: var(--grid-desktop-row-end, var(--grid-mobile-row-end));
|
||||
--h-align: var(--grid-desktop-h-align, var(--grid-mobile-h-align));
|
||||
--v-align: var(--grid-desktop-v-align, var(--grid-mobile-v-align));
|
||||
|
||||
/* Ensure grid metadata falls within limits */
|
||||
grid-column-start: min(max(1, var(--col-start)), var(--cols)) !important;
|
||||
grid-column-end: min(
|
||||
max(2, var(--col-end)),
|
||||
calc(var(--cols) + 1)
|
||||
) !important;
|
||||
grid-row-start: max(1, var(--row-start)) !important;
|
||||
grid-row-end: max(2, var(--row-end)) !important;
|
||||
|
||||
/* Flex container styles */
|
||||
flex-direction: column;
|
||||
align-items: var(--h-align);
|
||||
justify-content: var(--v-align);
|
||||
}
|
||||
|
||||
/* On mobile, use mobile metadata and fall back to desktop */
|
||||
.grid.mobile :global(> .component) {
|
||||
--col-start: var(--grid-mobile-col-start, var(--grid-desktop-col-start));
|
||||
--col-end: var(--grid-mobile-col-end, var(--grid-desktop-col-end));
|
||||
--row-start: var(--grid-mobile-row-start, var(--grid-desktop-row-start));
|
||||
--row-end: var(--grid-mobile-row-end, var(--grid-desktop-row-end));
|
||||
--h-align: var(--grid-mobile-h-align, var(--grid-desktop-h-align));
|
||||
--v-align: var(--grid-mobile-v-align, var(--grid-desktop-v-align));
|
||||
}
|
||||
|
||||
/* Handle grid children which need to fill the outer component wrapper */
|
||||
.grid :global(> .component > *) {
|
||||
flex: 0 0 auto !important;
|
||||
}
|
||||
.grid:not(.mobile)
|
||||
:global(> .component[data-grid-desktop-v-align="stretch"] > *) {
|
||||
flex: 1 1 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
.grid.mobile :global(> .component[data-grid-mobile-v-align="stretch"] > *) {
|
||||
flex: 1 1 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
/* Grid specific CSS overrides for certain components */
|
||||
.grid :global(> .component > img) {
|
||||
object-fit: contain;
|
||||
max-height: 100%;
|
||||
}
|
||||
</style>
|
|
@ -13,7 +13,7 @@ import "@spectrum-css/page/dist/index-vars.css"
|
|||
export { default as Placeholder } from "./Placeholder.svelte"
|
||||
|
||||
// User facing components
|
||||
export { default as container } from "./Container.svelte"
|
||||
export { default as container } from "./container/Container.svelte"
|
||||
export { default as section } from "./Section.svelte"
|
||||
export { default as dataprovider } from "./DataProvider.svelte"
|
||||
export { default as divider } from "./Divider.svelte"
|
||||
|
@ -35,7 +35,6 @@ export { default as spectrumcard } from "./SpectrumCard.svelte"
|
|||
export { default as tag } from "./Tag.svelte"
|
||||
export { default as markdownviewer } from "./MarkdownViewer.svelte"
|
||||
export { default as embeddedmap } from "./embedded-map/EmbeddedMap.svelte"
|
||||
export { default as grid } from "./Grid.svelte"
|
||||
export { default as sidepanel } from "./SidePanel.svelte"
|
||||
export { default as modal } from "./Modal.svelte"
|
||||
export { default as gridblock } from "./GridBlock.svelte"
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import { Utils } from "@budibase/frontend-core"
|
||||
import { findComponentById } from "utils/components.js"
|
||||
import { DNDPlaceholderID } from "constants"
|
||||
import { isGridEvent } from "utils/grid"
|
||||
|
||||
const ThrottleRate = 130
|
||||
|
||||
|
@ -25,15 +26,6 @@
|
|||
// Local flag for whether we are awaiting an async drop event
|
||||
let dropping = false
|
||||
|
||||
// Util to check if a DND event originates from a grid (or inside a grid).
|
||||
// This is important as we do not handle grid DND in this handler.
|
||||
const isGridEvent = e => {
|
||||
return e.target
|
||||
?.closest?.(".component")
|
||||
?.parentNode?.closest?.(".component")
|
||||
?.childNodes[0]?.classList.contains("grid")
|
||||
}
|
||||
|
||||
// Util to get the inner DOM node by a component ID
|
||||
const getDOMNode = id => {
|
||||
return document.getElementsByClassName(`${id}-dom`)[0]
|
||||
|
@ -267,7 +259,7 @@
|
|||
// Check if we're adding a new component rather than moving one
|
||||
if (source.newComponentType) {
|
||||
dropping = true
|
||||
await builderStore.actions.dropNewComponent(
|
||||
builderStore.actions.dropNewComponent(
|
||||
source.newComponentType,
|
||||
drop.parent,
|
||||
drop.index
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { onMount } from "svelte"
|
||||
import { DNDPlaceholderID } from "constants"
|
||||
import { domDebounce } from "utils/domDebounce.js"
|
||||
import { Utils } from "@budibase/frontend-core"
|
||||
|
||||
let left, top, height, width
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
|||
width = bounds.width
|
||||
}
|
||||
}
|
||||
const debouncedUpdate = domDebounce(updatePosition)
|
||||
const debouncedUpdate = Utils.domDebounce(updatePosition)
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(debouncedUpdate, 100)
|
||||
|
|
|
@ -1,112 +1,96 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { onMount, onDestroy, getContext } from "svelte"
|
||||
import { builderStore, componentStore } from "stores"
|
||||
import { Utils } from "@budibase/frontend-core"
|
||||
import { Utils, memo } from "@budibase/frontend-core"
|
||||
import { GridRowHeight } from "constants"
|
||||
import {
|
||||
isGridEvent,
|
||||
GridParams,
|
||||
getGridVar,
|
||||
Devices,
|
||||
GridDragModes,
|
||||
} from "utils/grid"
|
||||
|
||||
const context = getContext("context")
|
||||
|
||||
// Smallest possible 1x1 transparent GIF
|
||||
const ghost = new Image(1, 1)
|
||||
ghost.src =
|
||||
"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="
|
||||
|
||||
let dragInfo
|
||||
let gridStyles
|
||||
let id
|
||||
let styles = memo()
|
||||
|
||||
// Grid CSS variables
|
||||
$: device = $context.device.mobile ? Devices.Mobile : Devices.Desktop
|
||||
$: vars = {
|
||||
colStart: getGridVar(device, GridParams.ColStart),
|
||||
colEnd: getGridVar(device, GridParams.ColEnd),
|
||||
rowStart: getGridVar(device, GridParams.RowStart),
|
||||
rowEnd: getGridVar(device, GridParams.RowEnd),
|
||||
}
|
||||
|
||||
// Some memoisation of primitive types for performance
|
||||
$: jsonStyles = JSON.stringify(gridStyles)
|
||||
$: id = dragInfo?.id || id
|
||||
$: id = dragInfo?.id
|
||||
|
||||
// Set ephemeral grid styles on the dragged component
|
||||
// Set ephemeral styles
|
||||
$: instance = componentStore.actions.getComponentInstance(id)
|
||||
$: $instance?.setEphemeralStyles({
|
||||
...gridStyles,
|
||||
...(gridStyles ? { "z-index": 999 } : null),
|
||||
})
|
||||
$: $instance?.setEphemeralStyles($styles)
|
||||
|
||||
// Util to check if a DND event originates from a grid (or inside a grid).
|
||||
// This is important as we do not handle grid DND in this handler.
|
||||
const isGridEvent = e => {
|
||||
return (
|
||||
e.target
|
||||
.closest?.(".component")
|
||||
?.parentNode.closest(".component")
|
||||
?.childNodes[0].classList.contains("grid") ||
|
||||
e.target.classList.contains("anchor")
|
||||
)
|
||||
}
|
||||
// Sugar for a combination of both min and max
|
||||
const minMax = (value, min, max) => Math.min(max, Math.max(min, value))
|
||||
|
||||
// Util to get the inner DOM node by a component ID
|
||||
const getDOMNode = id => {
|
||||
const component = document.getElementsByClassName(id)[0]
|
||||
return [...component.children][0]
|
||||
}
|
||||
|
||||
const processEvent = Utils.throttle((mouseX, mouseY) => {
|
||||
const processEvent = Utils.domDebounce((mouseX, mouseY) => {
|
||||
if (!dragInfo?.grid) {
|
||||
return
|
||||
}
|
||||
|
||||
const { mode, side, gridId, grid } = dragInfo
|
||||
const {
|
||||
startX,
|
||||
startY,
|
||||
rowStart,
|
||||
rowEnd,
|
||||
colStart,
|
||||
colEnd,
|
||||
rowDeltaMin,
|
||||
rowDeltaMax,
|
||||
colDeltaMin,
|
||||
colDeltaMax,
|
||||
} = grid
|
||||
|
||||
const domGrid = getDOMNode(gridId)
|
||||
const { mode, side, grid, domGrid } = dragInfo
|
||||
const { startX, startY, rowStart, rowEnd, colStart, colEnd } = grid
|
||||
if (!domGrid) {
|
||||
return
|
||||
}
|
||||
const cols = parseInt(domGrid.dataset.cols)
|
||||
const rows = parseInt(domGrid.dataset.rows)
|
||||
const { width, height } = domGrid.getBoundingClientRect()
|
||||
|
||||
const colWidth = width / cols
|
||||
const colSize = parseInt(domGrid.dataset.colSize)
|
||||
const diffX = mouseX - startX
|
||||
let deltaX = Math.round(diffX / colWidth)
|
||||
const rowHeight = height / rows
|
||||
let deltaX = Math.round(diffX / colSize)
|
||||
const diffY = mouseY - startY
|
||||
let deltaY = Math.round(diffY / rowHeight)
|
||||
|
||||
if (mode === "move") {
|
||||
deltaY = Math.min(Math.max(deltaY, rowDeltaMin), rowDeltaMax)
|
||||
deltaX = Math.min(Math.max(deltaX, colDeltaMin), colDeltaMax)
|
||||
let deltaY = Math.round(diffY / GridRowHeight)
|
||||
if (mode === GridDragModes.Move) {
|
||||
deltaX = minMax(deltaX, 1 - colStart, cols + 1 - colEnd)
|
||||
deltaY = Math.max(deltaY, 1 - rowStart)
|
||||
const newStyles = {
|
||||
"grid-row-start": rowStart + deltaY,
|
||||
"grid-row-end": rowEnd + deltaY,
|
||||
"grid-column-start": colStart + deltaX,
|
||||
"grid-column-end": colEnd + deltaX,
|
||||
[vars.colStart]: colStart + deltaX,
|
||||
[vars.colEnd]: colEnd + deltaX,
|
||||
[vars.rowStart]: rowStart + deltaY,
|
||||
[vars.rowEnd]: rowEnd + deltaY,
|
||||
}
|
||||
if (JSON.stringify(newStyles) !== jsonStyles) {
|
||||
gridStyles = newStyles
|
||||
}
|
||||
} else if (mode === "resize") {
|
||||
styles.set(newStyles)
|
||||
} else if (mode === GridDragModes.Resize) {
|
||||
let newStyles = {}
|
||||
if (side === "right") {
|
||||
newStyles["grid-column-end"] = Math.max(colEnd + deltaX, colStart + 1)
|
||||
newStyles[vars.colEnd] = Math.max(colEnd + deltaX, colStart + 1)
|
||||
} else if (side === "left") {
|
||||
newStyles["grid-column-start"] = Math.min(colStart + deltaX, colEnd - 1)
|
||||
newStyles[vars.colStart] = Math.min(colStart + deltaX, colEnd - 1)
|
||||
} else if (side === "top") {
|
||||
newStyles["grid-row-start"] = Math.min(rowStart + deltaY, rowEnd - 1)
|
||||
newStyles[vars.rowStart] = Math.min(rowStart + deltaY, rowEnd - 1)
|
||||
} else if (side === "bottom") {
|
||||
newStyles["grid-row-end"] = Math.max(rowEnd + deltaY, rowStart + 1)
|
||||
newStyles[vars.rowEnd] = Math.max(rowEnd + deltaY, rowStart + 1)
|
||||
} else if (side === "bottom-right") {
|
||||
newStyles["grid-column-end"] = Math.max(colEnd + deltaX, colStart + 1)
|
||||
newStyles["grid-row-end"] = Math.max(rowEnd + deltaY, rowStart + 1)
|
||||
newStyles[vars.colEnd] = Math.max(colEnd + deltaX, colStart + 1)
|
||||
newStyles[vars.rowEnd] = Math.max(rowEnd + deltaY, rowStart + 1)
|
||||
} else if (side === "bottom-left") {
|
||||
newStyles["grid-column-start"] = Math.min(colStart + deltaX, colEnd - 1)
|
||||
newStyles["grid-row-end"] = Math.max(rowEnd + deltaY, rowStart + 1)
|
||||
newStyles[vars.colStart] = Math.min(colStart + deltaX, colEnd - 1)
|
||||
newStyles[vars.rowEnd] = Math.max(rowEnd + deltaY, rowStart + 1)
|
||||
} else if (side === "top-right") {
|
||||
newStyles["grid-column-end"] = Math.max(colEnd + deltaX, colStart + 1)
|
||||
newStyles["grid-row-start"] = Math.min(rowStart + deltaY, rowEnd - 1)
|
||||
newStyles[vars.colEnd] = Math.max(colEnd + deltaX, colStart + 1)
|
||||
newStyles[vars.rowStart] = Math.min(rowStart + deltaY, rowEnd - 1)
|
||||
} else if (side === "top-left") {
|
||||
newStyles["grid-column-start"] = Math.min(colStart + deltaX, colEnd - 1)
|
||||
newStyles["grid-row-start"] = Math.min(rowStart + deltaY, rowEnd - 1)
|
||||
}
|
||||
if (JSON.stringify(newStyles) !== jsonStyles) {
|
||||
gridStyles = newStyles
|
||||
newStyles[vars.colStart] = Math.min(colStart + deltaX, colEnd - 1)
|
||||
newStyles[vars.rowStart] = Math.min(rowStart + deltaY, rowEnd - 1)
|
||||
}
|
||||
styles.set(newStyles)
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
|
||||
const handleEvent = e => {
|
||||
e.preventDefault()
|
||||
|
@ -121,77 +105,64 @@
|
|||
}
|
||||
|
||||
// Hide drag ghost image
|
||||
e.dataTransfer.setDragImage(new Image(), 0, 0)
|
||||
e.dataTransfer.setDragImage(ghost, 0, 0)
|
||||
|
||||
// Extract state
|
||||
let mode, id, side
|
||||
if (e.target.classList.contains("anchor")) {
|
||||
// Handle resize
|
||||
mode = "resize"
|
||||
if (e.target.dataset.indicator === "true") {
|
||||
mode = e.target.dataset.dragMode
|
||||
id = e.target.dataset.id
|
||||
side = e.target.dataset.side
|
||||
} else {
|
||||
// Handle move
|
||||
mode = "move"
|
||||
mode = GridDragModes.Move
|
||||
const component = e.target.closest(".component")
|
||||
id = component.dataset.id
|
||||
}
|
||||
|
||||
// Find grid parent
|
||||
const domComponent = getDOMNode(id)
|
||||
const gridId = domComponent?.closest(".grid")?.parentNode.dataset.id
|
||||
if (!gridId) {
|
||||
// If holding ctrl/cmd then leave behind a duplicate of this component
|
||||
if (mode === GridDragModes.Move && (e.ctrlKey || e.metaKey)) {
|
||||
builderStore.actions.duplicateComponent(id, "above", false)
|
||||
}
|
||||
|
||||
// Find grid parent and read from DOM
|
||||
const domComponent = document.getElementsByClassName(id)[0]
|
||||
const domGrid = domComponent?.closest(".grid")
|
||||
if (!domGrid) {
|
||||
return
|
||||
}
|
||||
const styles = getComputedStyle(domComponent)
|
||||
|
||||
// Show as active
|
||||
domComponent.classList.add("dragging")
|
||||
domGrid.classList.add("highlight")
|
||||
builderStore.actions.selectComponent(id)
|
||||
|
||||
// Update state
|
||||
dragInfo = {
|
||||
domTarget: e.target,
|
||||
domComponent,
|
||||
domGrid,
|
||||
id,
|
||||
gridId,
|
||||
gridId: domGrid.parentNode.dataset.id,
|
||||
mode,
|
||||
side,
|
||||
grid: {
|
||||
startX: e.clientX,
|
||||
startY: e.clientY,
|
||||
rowStart: parseInt(styles["grid-row-start"]),
|
||||
rowEnd: parseInt(styles["grid-row-end"]),
|
||||
colStart: parseInt(styles["grid-column-start"]),
|
||||
colEnd: parseInt(styles["grid-column-end"]),
|
||||
},
|
||||
}
|
||||
|
||||
// Add event handler to clear all drag state when dragging ends
|
||||
dragInfo.domTarget.addEventListener("dragend", stopDragging)
|
||||
}
|
||||
|
||||
// Callback when entering a potential drop target
|
||||
const onDragEnter = e => {
|
||||
// Skip if we aren't validly dragging currently
|
||||
if (!dragInfo || dragInfo.grid) {
|
||||
return
|
||||
}
|
||||
|
||||
const domGrid = getDOMNode(dragInfo.gridId)
|
||||
const gridCols = parseInt(domGrid.dataset.cols)
|
||||
const gridRows = parseInt(domGrid.dataset.rows)
|
||||
const domNode = getDOMNode(dragInfo.id)
|
||||
const styles = window.getComputedStyle(domNode)
|
||||
if (domGrid) {
|
||||
const minMax = (value, min, max) => Math.min(max, Math.max(min, value))
|
||||
const getStyle = x => parseInt(styles?.[x] || "0")
|
||||
const getColStyle = x => minMax(getStyle(x), 1, gridCols + 1)
|
||||
const getRowStyle = x => minMax(getStyle(x), 1, gridRows + 1)
|
||||
dragInfo.grid = {
|
||||
startX: e.clientX,
|
||||
startY: e.clientY,
|
||||
rowStart: getRowStyle("grid-row-start"),
|
||||
rowEnd: getRowStyle("grid-row-end"),
|
||||
colStart: getColStyle("grid-column-start"),
|
||||
colEnd: getColStyle("grid-column-end"),
|
||||
rowDeltaMin: 1 - getRowStyle("grid-row-start"),
|
||||
rowDeltaMax: gridRows + 1 - getRowStyle("grid-row-end"),
|
||||
colDeltaMin: 1 - getColStyle("grid-column-start"),
|
||||
colDeltaMax: gridCols + 1 - getColStyle("grid-column-end"),
|
||||
}
|
||||
handleEvent(e)
|
||||
}
|
||||
}
|
||||
|
||||
const onDragOver = e => {
|
||||
if (!dragInfo?.grid) {
|
||||
if (!dragInfo) {
|
||||
return
|
||||
}
|
||||
handleEvent(e)
|
||||
|
@ -199,30 +170,33 @@
|
|||
|
||||
// Callback when drag stops (whether dropped or not)
|
||||
const stopDragging = async () => {
|
||||
// Save changes
|
||||
if (gridStyles) {
|
||||
await builderStore.actions.updateStyles(gridStyles, dragInfo.id)
|
||||
if (!dragInfo) {
|
||||
return
|
||||
}
|
||||
const { id, domTarget, domGrid, domComponent } = dragInfo
|
||||
|
||||
// Reset listener
|
||||
if (dragInfo?.domTarget) {
|
||||
dragInfo.domTarget.removeEventListener("dragend", stopDragging)
|
||||
// Reset DOM
|
||||
domComponent.classList.remove("dragging")
|
||||
domGrid.classList.remove("highlight")
|
||||
domTarget.removeEventListener("dragend", stopDragging)
|
||||
|
||||
// Save changes
|
||||
if ($styles) {
|
||||
builderStore.actions.updateStyles($styles, id)
|
||||
}
|
||||
|
||||
// Reset state
|
||||
dragInfo = null
|
||||
gridStyles = null
|
||||
styles.set(null)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
document.addEventListener("dragstart", onDragStart, false)
|
||||
document.addEventListener("dragenter", onDragEnter, false)
|
||||
document.addEventListener("dragover", onDragOver, false)
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
document.removeEventListener("dragstart", onDragStart, false)
|
||||
document.removeEventListener("dragenter", onDragEnter, false)
|
||||
document.removeEventListener("dragover", onDragOver, false)
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<script>
|
||||
import { Icon } from "@budibase/bbui"
|
||||
import { builderStore } from "stores"
|
||||
|
||||
export let style
|
||||
export let value
|
||||
export let icon
|
||||
export let title
|
||||
export let componentId
|
||||
export let active
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
{title}
|
||||
class:active
|
||||
on:click={() => {
|
||||
builderStore.actions.updateStyles({ [style]: value }, componentId)
|
||||
}}
|
||||
>
|
||||
<Icon name={icon} size="S" />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
padding: 6px;
|
||||
border-radius: 2px;
|
||||
color: var(--spectrum-global-color-gray-700);
|
||||
display: flex;
|
||||
transition: color 0.13s ease-in-out, background-color 0.13s ease-in-out;
|
||||
}
|
||||
div:hover {
|
||||
background-color: var(--spectrum-global-color-gray-200);
|
||||
cursor: pointer;
|
||||
}
|
||||
.active,
|
||||
.active:hover {
|
||||
background-color: rgba(13, 102, 208, 0.1);
|
||||
color: var(--spectrum-global-color-blue-600);
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,6 @@
|
|||
<script>
|
||||
import { Icon } from "@budibase/bbui"
|
||||
import { GridDragModes } from "utils/grid"
|
||||
|
||||
export let top
|
||||
export let left
|
||||
|
@ -34,9 +35,20 @@
|
|||
class:line
|
||||
style="top: {top}px; left: {left}px; width: {width}px; height: {height}px; --color: {color}; --zIndex: {zIndex};"
|
||||
class:withText={!!text}
|
||||
class:vCompact={height < 40}
|
||||
class:hCompact={width < 40}
|
||||
>
|
||||
{#if text || icon}
|
||||
<div class="label" class:flipped class:line class:right={alignRight}>
|
||||
<div
|
||||
class="label"
|
||||
class:flipped
|
||||
class:line
|
||||
class:right={alignRight}
|
||||
draggable="true"
|
||||
data-indicator="true"
|
||||
data-drag-mode={GridDragModes.Move}
|
||||
data-id={componentId}
|
||||
>
|
||||
{#if icon}
|
||||
<Icon name={icon} size="S" color="white" />
|
||||
{/if}
|
||||
|
@ -50,8 +62,10 @@
|
|||
{#if showResizeAnchors}
|
||||
{#each AnchorSides as side}
|
||||
<div
|
||||
draggable="true"
|
||||
class="anchor {side}"
|
||||
draggable="true"
|
||||
data-indicator="true"
|
||||
data-drag-mode={GridDragModes.Resize}
|
||||
data-side={side}
|
||||
data-id={componentId}
|
||||
>
|
||||
|
@ -99,6 +113,7 @@
|
|||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
pointer-events: all;
|
||||
}
|
||||
.label.line {
|
||||
transform: translateY(-50%);
|
||||
|
@ -123,7 +138,7 @@
|
|||
|
||||
/* Anchor */
|
||||
.anchor {
|
||||
--size: 24px;
|
||||
--size: 20px;
|
||||
position: absolute;
|
||||
width: var(--size);
|
||||
height: var(--size);
|
||||
|
@ -131,53 +146,84 @@
|
|||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
.anchor-inner {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
width: calc(var(--size) / 2);
|
||||
height: calc(var(--size) / 2);
|
||||
background: white;
|
||||
border: 2px solid var(--color);
|
||||
pointer-events: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Thinner anchors for each edge */
|
||||
.anchor.right,
|
||||
.anchor.left {
|
||||
height: calc(var(--size) * 2);
|
||||
}
|
||||
.anchor.top,
|
||||
.anchor.bottom {
|
||||
width: calc(var(--size) * 2);
|
||||
}
|
||||
.anchor.right .anchor-inner,
|
||||
.anchor.left .anchor-inner {
|
||||
height: calc(var(--size) * 1.2);
|
||||
width: calc(var(--size) * 0.3);
|
||||
}
|
||||
.anchor.top .anchor-inner,
|
||||
.anchor.bottom .anchor-inner {
|
||||
width: calc(var(--size) * 1.2);
|
||||
height: calc(var(--size) * 0.3);
|
||||
}
|
||||
|
||||
/* Hide side indicators when they don't fit */
|
||||
.indicator.hCompact .anchor.top,
|
||||
.indicator.hCompact .anchor.bottom,
|
||||
.indicator.vCompact .anchor.left,
|
||||
.indicator.vCompact .anchor.right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Anchor positions */
|
||||
.anchor.right {
|
||||
right: calc(var(--size) / -2 - 1px);
|
||||
top: calc(50% - var(--size) / 2);
|
||||
left: calc(100% + 1px);
|
||||
top: 50%;
|
||||
cursor: e-resize;
|
||||
}
|
||||
.anchor.left {
|
||||
left: calc(var(--size) / -2 - 1px);
|
||||
top: calc(50% - var(--size) / 2);
|
||||
left: -1px;
|
||||
top: 50%;
|
||||
cursor: w-resize;
|
||||
}
|
||||
.anchor.bottom {
|
||||
left: calc(50% - var(--size) / 2 + 1px);
|
||||
bottom: calc(var(--size) / -2 - 1px);
|
||||
left: 50%;
|
||||
top: calc(100% + 1px);
|
||||
cursor: s-resize;
|
||||
}
|
||||
.anchor.top {
|
||||
left: calc(50% - var(--size) / 2 + 1px);
|
||||
top: calc(var(--size) / -2 - 1px);
|
||||
left: 50%;
|
||||
top: -1px;
|
||||
cursor: n-resize;
|
||||
}
|
||||
|
||||
.anchor.bottom-right {
|
||||
right: calc(var(--size) / -2 - 1px);
|
||||
bottom: calc(var(--size) / -2 - 1px);
|
||||
top: 100%;
|
||||
left: 100%;
|
||||
cursor: se-resize;
|
||||
}
|
||||
.anchor.bottom-left {
|
||||
left: calc(var(--size) / -2 - 1px);
|
||||
bottom: calc(var(--size) / -2 - 1px);
|
||||
left: 0;
|
||||
top: 100%;
|
||||
cursor: sw-resize;
|
||||
}
|
||||
.anchor.top-right {
|
||||
right: calc(var(--size) / -2 - 1px);
|
||||
top: calc(var(--size) / -2 - 1px);
|
||||
left: 100%;
|
||||
top: 0;
|
||||
cursor: ne-resize;
|
||||
}
|
||||
.anchor.top-left {
|
||||
left: calc(var(--size) / -2 - 1px);
|
||||
top: calc(var(--size) / -2 - 1px);
|
||||
left: 0;
|
||||
top: 0;
|
||||
cursor: nw-resize;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import Indicator from "./Indicator.svelte"
|
||||
import { domDebounce } from "utils/domDebounce"
|
||||
import { builderStore } from "stores"
|
||||
import { memo, Utils } from "@budibase/frontend-core"
|
||||
|
||||
export let componentId = null
|
||||
export let color = null
|
||||
|
@ -10,7 +10,10 @@
|
|||
export let prefix = null
|
||||
export let allowResizeAnchors = false
|
||||
|
||||
// Offset = 6 (clip-root padding) - 1 (half the border thickness)
|
||||
const config = memo($$props)
|
||||
const errorColor = "var(--spectrum-global-color-static-red-600)"
|
||||
const mutationObserver = new MutationObserver(() => debouncedUpdate())
|
||||
const defaultState = () => ({
|
||||
// Cached props
|
||||
componentId,
|
||||
|
@ -29,38 +32,49 @@
|
|||
|
||||
let interval
|
||||
let state = defaultState()
|
||||
let nextState = null
|
||||
let observingMutations = false
|
||||
let updating = false
|
||||
let observers = []
|
||||
let intersectionObservers = []
|
||||
let callbackCount = 0
|
||||
let nextState
|
||||
|
||||
$: componentId, reset()
|
||||
$: visibleIndicators = state.indicators.filter(x => x.visible)
|
||||
$: offset = $builderStore.inBuilder ? 0 : 2
|
||||
$: $$props, debouncedUpdate()
|
||||
$: offset = $builderStore.inBuilder ? 5 : -1
|
||||
$: config.set({
|
||||
componentId,
|
||||
color,
|
||||
zIndex,
|
||||
prefix,
|
||||
allowResizeAnchors,
|
||||
})
|
||||
|
||||
const checkInsideGrid = id => {
|
||||
const component = document.getElementsByClassName(id)[0]
|
||||
const domNode = component?.children[0]
|
||||
// Update position when any props change
|
||||
$: $config, debouncedUpdate()
|
||||
|
||||
// Ignore grid itself
|
||||
if (domNode?.classList.contains("grid")) {
|
||||
return false
|
||||
}
|
||||
const reset = () => {
|
||||
mutationObserver.disconnect()
|
||||
observingMutations = false
|
||||
updating = false
|
||||
}
|
||||
|
||||
return component?.parentNode
|
||||
?.closest?.(".component")
|
||||
?.childNodes[0]?.classList.contains("grid")
|
||||
const observeMutations = element => {
|
||||
mutationObserver.observe(element, {
|
||||
attributes: true,
|
||||
attributeFilter: ["style"],
|
||||
})
|
||||
observingMutations = true
|
||||
}
|
||||
|
||||
const createIntersectionCallback = idx => entries => {
|
||||
if (callbackCount >= observers.length) {
|
||||
if (callbackCount >= intersectionObservers.length) {
|
||||
return
|
||||
}
|
||||
nextState.indicators[idx].visible =
|
||||
nextState.indicators[idx].insideModal ||
|
||||
nextState.indicators[idx].insideSidePanel ||
|
||||
entries[0].isIntersecting
|
||||
if (++callbackCount === observers.length) {
|
||||
if (++callbackCount === intersectionObservers.length) {
|
||||
state = nextState
|
||||
updating = false
|
||||
}
|
||||
|
@ -76,76 +90,95 @@
|
|||
state = defaultState()
|
||||
return
|
||||
}
|
||||
|
||||
// Reset state
|
||||
let elements = document.getElementsByClassName(componentId)
|
||||
if (!elements.length) {
|
||||
state = defaultState()
|
||||
return
|
||||
}
|
||||
updating = true
|
||||
callbackCount = 0
|
||||
observers.forEach(o => o.disconnect())
|
||||
observers = []
|
||||
intersectionObservers.forEach(o => o.disconnect())
|
||||
intersectionObservers = []
|
||||
nextState = defaultState()
|
||||
|
||||
// Start observing mutations if this is the first time we've seen our
|
||||
// component in the DOM
|
||||
if (!observingMutations) {
|
||||
observeMutations(elements[0])
|
||||
}
|
||||
|
||||
// Check if we're inside a grid
|
||||
if (allowResizeAnchors) {
|
||||
nextState.insideGrid = checkInsideGrid(componentId)
|
||||
nextState.insideGrid = elements[0]?.dataset.insideGrid === "true"
|
||||
}
|
||||
|
||||
// Determine next set of indicators
|
||||
const parents = document.getElementsByClassName(componentId)
|
||||
if (parents.length) {
|
||||
nextState.text = parents[0].dataset.name
|
||||
if (nextState.prefix) {
|
||||
nextState.text = `${nextState.prefix} ${nextState.text}`
|
||||
}
|
||||
if (parents[0].dataset.icon) {
|
||||
nextState.icon = parents[0].dataset.icon
|
||||
}
|
||||
// Get text to display
|
||||
nextState.text = elements[0].dataset.name
|
||||
if (nextState.prefix) {
|
||||
nextState.text = `${nextState.prefix} ${nextState.text}`
|
||||
}
|
||||
nextState.error = parents?.[0]?.classList.contains("error")
|
||||
if (elements[0].dataset.icon) {
|
||||
nextState.icon = elements[0].dataset.icon
|
||||
}
|
||||
nextState.error = elements[0].classList.contains("error")
|
||||
|
||||
// Batch reads to minimize reflow
|
||||
const scrollX = window.scrollX
|
||||
const scrollY = window.scrollY
|
||||
|
||||
// Extract valid children
|
||||
// Sanity limit of 100 active indicators
|
||||
const children = Array.from(
|
||||
document.getElementsByClassName(`${componentId}-dom`)
|
||||
)
|
||||
// Sanity limit of active indicators
|
||||
if (!nextState.insideGrid) {
|
||||
elements = document.getElementsByClassName(`${componentId}-dom`)
|
||||
}
|
||||
elements = Array.from(elements)
|
||||
.filter(x => x != null)
|
||||
.slice(0, 100)
|
||||
const multi = elements.length > 1
|
||||
|
||||
// If there aren't any nodes then reset
|
||||
if (!children.length) {
|
||||
if (!elements.length) {
|
||||
state = defaultState()
|
||||
updating = false
|
||||
return
|
||||
}
|
||||
|
||||
const device = document.getElementById("app-root")
|
||||
const deviceBounds = device.getBoundingClientRect()
|
||||
children.forEach((child, idx) => {
|
||||
const callback = createIntersectionCallback(idx)
|
||||
const threshold = children.length > 1 ? 1 : 0
|
||||
const observer = new IntersectionObserver(callback, {
|
||||
threshold,
|
||||
root: device,
|
||||
})
|
||||
observer.observe(child)
|
||||
observers.push(observer)
|
||||
nextState.indicators = elements.map((element, idx) => {
|
||||
const elBounds = element.getBoundingClientRect()
|
||||
let indicator = {
|
||||
top: Math.round(elBounds.top + scrollY - deviceBounds.top + offset),
|
||||
left: Math.round(elBounds.left + scrollX - deviceBounds.left + offset),
|
||||
width: Math.round(elBounds.width + 2),
|
||||
height: Math.round(elBounds.height + 2),
|
||||
visible: true,
|
||||
}
|
||||
|
||||
const elBounds = child.getBoundingClientRect()
|
||||
nextState.indicators.push({
|
||||
top: elBounds.top + scrollY - deviceBounds.top - offset,
|
||||
left: elBounds.left + scrollX - deviceBounds.left - offset,
|
||||
width: elBounds.width + 4,
|
||||
height: elBounds.height + 4,
|
||||
visible: false,
|
||||
insideSidePanel: !!child.closest(".side-panel"),
|
||||
insideModal: !!child.closest(".modal-content"),
|
||||
})
|
||||
// If observing more than one node then we need to use an intersection
|
||||
// observer to determine whether each indicator should be visible
|
||||
if (multi) {
|
||||
const callback = createIntersectionCallback(idx)
|
||||
const intersectionObserver = new IntersectionObserver(callback, {
|
||||
threshold: 1,
|
||||
root: device,
|
||||
})
|
||||
intersectionObserver.observe(element)
|
||||
intersectionObservers.push(intersectionObserver)
|
||||
indicator.visible = false
|
||||
indicator.insideSidePanel = !!element.closest(".side-panel")
|
||||
indicator.insideModal = !!element.closest(".modal-content")
|
||||
}
|
||||
|
||||
return indicator
|
||||
})
|
||||
|
||||
// Immediately apply the update if we're just observing a single node
|
||||
if (!multi) {
|
||||
state = nextState
|
||||
updating = false
|
||||
}
|
||||
}
|
||||
const debouncedUpdate = domDebounce(updatePosition)
|
||||
const debouncedUpdate = Utils.domDebounce(updatePosition)
|
||||
|
||||
onMount(() => {
|
||||
debouncedUpdate()
|
||||
|
@ -154,9 +187,9 @@
|
|||
})
|
||||
|
||||
onDestroy(() => {
|
||||
mutationObserver.disconnect()
|
||||
clearInterval(interval)
|
||||
document.removeEventListener("scroll", debouncedUpdate, true)
|
||||
observers.forEach(o => o.disconnect())
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,117 +1,180 @@
|
|||
<script>
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { onMount, onDestroy, getContext } from "svelte"
|
||||
import SettingsButton from "./SettingsButton.svelte"
|
||||
import GridStylesButton from "./GridStylesButton.svelte"
|
||||
import SettingsColorPicker from "./SettingsColorPicker.svelte"
|
||||
import SettingsPicker from "./SettingsPicker.svelte"
|
||||
import { builderStore, componentStore, dndIsDragging } from "stores"
|
||||
import { domDebounce } from "utils/domDebounce"
|
||||
import { Utils, shouldDisplaySetting } from "@budibase/frontend-core"
|
||||
import { getGridVar, GridParams, Devices } from "utils/grid"
|
||||
|
||||
const context = getContext("context")
|
||||
const verticalOffset = 36
|
||||
const horizontalOffset = 2
|
||||
const observer = new MutationObserver(() => debouncedUpdate())
|
||||
|
||||
let top = 0
|
||||
let left = 0
|
||||
let interval
|
||||
let self
|
||||
let measured = false
|
||||
let observing = false
|
||||
let insideGrid = false
|
||||
let gridHAlign
|
||||
let gridVAlign
|
||||
|
||||
$: id = $builderStore.selectedComponentId
|
||||
$: id, reset()
|
||||
$: component = $componentStore.selectedComponent
|
||||
$: definition = $componentStore.selectedComponentDefinition
|
||||
$: instance = componentStore.actions.getComponentInstance(id)
|
||||
$: state = $instance?.state
|
||||
$: definition = $componentStore.selectedComponentDefinition
|
||||
$: showBar =
|
||||
definition?.showSettingsBar !== false &&
|
||||
!$dndIsDragging &&
|
||||
definition &&
|
||||
!$state?.errorState
|
||||
$: {
|
||||
if (!showBar) {
|
||||
measured = false
|
||||
}
|
||||
}
|
||||
$: settings = getBarSettings(definition)
|
||||
$: settings = getBarSettings(component, definition)
|
||||
$: isRoot = id === $builderStore.screen?.props?._id
|
||||
$: showGridStyles =
|
||||
insideGrid &&
|
||||
(definition?.grid?.hAlign !== "stretch" ||
|
||||
definition?.grid?.vAlign !== "stretch")
|
||||
$: mobile = $context.device.mobile
|
||||
$: device = mobile ? Devices.Mobile : Devices.Desktop
|
||||
$: gridHAlignVar = getGridVar(device, GridParams.HAlign)
|
||||
$: gridVAlignVar = getGridVar(device, GridParams.VAlign)
|
||||
|
||||
const getBarSettings = definition => {
|
||||
const reset = () => {
|
||||
observer.disconnect()
|
||||
measured = false
|
||||
observing = false
|
||||
insideGrid = false
|
||||
}
|
||||
|
||||
const startObserving = domBoundary => {
|
||||
observer.observe(domBoundary, {
|
||||
attributes: true,
|
||||
attributeFilter: ["style"],
|
||||
})
|
||||
observing = true
|
||||
}
|
||||
|
||||
const getBarSettings = (component, definition) => {
|
||||
let allSettings = []
|
||||
definition?.settings?.forEach(setting => {
|
||||
if (setting.section) {
|
||||
if (setting.section && shouldDisplaySetting(component, setting)) {
|
||||
allSettings = allSettings.concat(setting.settings || [])
|
||||
} else {
|
||||
allSettings.push(setting)
|
||||
}
|
||||
})
|
||||
return allSettings.filter(setting => setting.showInBar && !setting.hidden)
|
||||
return allSettings.filter(
|
||||
setting =>
|
||||
setting.showInBar &&
|
||||
!setting.hidden &&
|
||||
shouldDisplaySetting(component, setting)
|
||||
)
|
||||
}
|
||||
|
||||
const updatePosition = () => {
|
||||
if (!showBar) {
|
||||
return
|
||||
}
|
||||
const id = $builderStore.selectedComponentId
|
||||
const parent = document.getElementsByClassName(id)?.[0]
|
||||
const element = parent?.children?.[0]
|
||||
|
||||
// The settings bar is higher in the dom tree than the selection indicators
|
||||
// as we want to be able to render the settings bar wider than the screen,
|
||||
// or outside the screen.
|
||||
// Therefore we use the clip root rather than the app root to determine
|
||||
// its position.
|
||||
const device = document.getElementById("clip-root")
|
||||
if (element && self) {
|
||||
// Batch reads to minimize reflow
|
||||
const deviceBounds = device.getBoundingClientRect()
|
||||
const elBounds = element.getBoundingClientRect()
|
||||
const width = self.offsetWidth
|
||||
const height = self.offsetHeight
|
||||
const { scrollX, scrollY, innerWidth } = window
|
||||
// Find DOM boundary and ensure it is valid
|
||||
let domBoundary = document.getElementsByClassName(id)[0]
|
||||
if (!domBoundary) {
|
||||
return reset()
|
||||
}
|
||||
|
||||
// Vertically, always render above unless no room, then render inside
|
||||
let newTop = elBounds.top + scrollY - verticalOffset - height
|
||||
if (newTop < deviceBounds.top - 50) {
|
||||
newTop = deviceBounds.top - 50
|
||||
}
|
||||
if (newTop < 0) {
|
||||
newTop = 0
|
||||
}
|
||||
const deviceBottom = deviceBounds.top + deviceBounds.height
|
||||
if (newTop > deviceBottom - 44) {
|
||||
newTop = deviceBottom - 44
|
||||
}
|
||||
// If we're inside a grid, allow time for buttons to render
|
||||
const nextInsideGrid = domBoundary.dataset.insideGrid === "true"
|
||||
if (nextInsideGrid && !insideGrid) {
|
||||
insideGrid = true
|
||||
return
|
||||
} else {
|
||||
insideGrid = nextInsideGrid
|
||||
}
|
||||
|
||||
//If element is at the very top of the screen, put the bar below the element
|
||||
if (elBounds.top < elBounds.height && elBounds.height < 80) {
|
||||
newTop = elBounds.bottom + verticalOffset
|
||||
}
|
||||
// Get the correct DOM boundary depending if we're inside a grid or not
|
||||
if (!insideGrid) {
|
||||
domBoundary =
|
||||
domBoundary.getElementsByClassName(`${id}-dom`)[0] ||
|
||||
domBoundary.children?.[0]
|
||||
}
|
||||
if (!domBoundary || !self) {
|
||||
return reset()
|
||||
}
|
||||
|
||||
// Horizontally, try to center first.
|
||||
// Failing that, render to left edge of component.
|
||||
// Failing that, render to right edge of component,
|
||||
// Failing that, render to window left edge and accept defeat.
|
||||
let elCenter = elBounds.left + scrollX + elBounds.width / 2
|
||||
let newLeft = elCenter - width / 2
|
||||
// Start observing if required
|
||||
if (!observing) {
|
||||
startObserving(domBoundary)
|
||||
}
|
||||
|
||||
// Batch reads to minimize reflow
|
||||
const deviceEl = document.getElementById("clip-root")
|
||||
const deviceBounds = deviceEl.getBoundingClientRect()
|
||||
const elBounds = domBoundary.getBoundingClientRect()
|
||||
const width = self.offsetWidth
|
||||
const height = self.offsetHeight
|
||||
const { scrollX, scrollY, innerWidth } = window
|
||||
|
||||
// Read grid metadata from data attributes
|
||||
if (insideGrid) {
|
||||
if (mobile) {
|
||||
gridHAlign = domBoundary.dataset.gridMobileHAlign
|
||||
gridVAlign = domBoundary.dataset.gridMobileVAlign
|
||||
} else {
|
||||
gridHAlign = domBoundary.dataset.gridDesktopHAlign
|
||||
gridVAlign = domBoundary.dataset.gridDesktopVAlign
|
||||
}
|
||||
}
|
||||
|
||||
// Vertically, always render above unless no room, then render inside
|
||||
let newTop = elBounds.top + scrollY - verticalOffset - height
|
||||
if (newTop < deviceBounds.top - 50) {
|
||||
newTop = deviceBounds.top - 50
|
||||
}
|
||||
if (newTop < 0) {
|
||||
newTop = 0
|
||||
}
|
||||
const deviceBottom = deviceBounds.top + deviceBounds.height
|
||||
if (newTop > deviceBottom - 44) {
|
||||
newTop = deviceBottom - 44
|
||||
}
|
||||
|
||||
//If element is at the very top of the screen, put the bar below the element
|
||||
if (elBounds.top < elBounds.height && elBounds.height < 80) {
|
||||
newTop = elBounds.bottom + verticalOffset
|
||||
}
|
||||
|
||||
// Horizontally, try to center first.
|
||||
// Failing that, render to left edge of component.
|
||||
// Failing that, render to right edge of component,
|
||||
// Failing that, render to window left edge and accept defeat.
|
||||
let elCenter = elBounds.left + scrollX + elBounds.width / 2
|
||||
let newLeft = elCenter - width / 2
|
||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||
newLeft = elBounds.left + scrollX - horizontalOffset
|
||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||
newLeft = elBounds.left + scrollX - horizontalOffset
|
||||
newLeft = elBounds.right + scrollX - width + horizontalOffset
|
||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||
newLeft = elBounds.right + scrollX - width + horizontalOffset
|
||||
if (newLeft < 0 || newLeft + width > innerWidth) {
|
||||
newLeft = horizontalOffset
|
||||
}
|
||||
newLeft = horizontalOffset
|
||||
}
|
||||
}
|
||||
|
||||
// Only update state when things changes to minimize renders
|
||||
if (Math.round(newTop) !== Math.round(top)) {
|
||||
top = newTop
|
||||
}
|
||||
if (Math.round(newLeft) !== Math.round(left)) {
|
||||
left = newLeft
|
||||
}
|
||||
|
||||
measured = true
|
||||
}
|
||||
|
||||
// Only update state when things changes to minimize renders
|
||||
if (Math.round(newTop) !== Math.round(top)) {
|
||||
top = newTop
|
||||
}
|
||||
if (Math.round(newLeft) !== Math.round(left)) {
|
||||
left = newLeft
|
||||
}
|
||||
measured = true
|
||||
}
|
||||
const debouncedUpdate = domDebounce(updatePosition)
|
||||
const debouncedUpdate = Utils.domDebounce(updatePosition)
|
||||
|
||||
onMount(() => {
|
||||
debouncedUpdate()
|
||||
|
@ -122,16 +185,85 @@
|
|||
onDestroy(() => {
|
||||
clearInterval(interval)
|
||||
document.removeEventListener("scroll", debouncedUpdate, true)
|
||||
reset()
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if showBar}
|
||||
<div
|
||||
class="bar"
|
||||
style="top: {top}px; left: {left}px;"
|
||||
style="top:{top}px; left:{left}px;"
|
||||
bind:this={self}
|
||||
class:visible={measured}
|
||||
>
|
||||
{#if showGridStyles}
|
||||
<GridStylesButton
|
||||
style={gridHAlignVar}
|
||||
value="start"
|
||||
icon="AlignLeft"
|
||||
title="Align left"
|
||||
active={gridHAlign === "start"}
|
||||
componentId={id}
|
||||
/>
|
||||
<GridStylesButton
|
||||
style={gridHAlignVar}
|
||||
value="center"
|
||||
icon="AlignCenter"
|
||||
title="Align center"
|
||||
active={gridHAlign === "center"}
|
||||
componentId={id}
|
||||
/>
|
||||
<GridStylesButton
|
||||
style={gridHAlignVar}
|
||||
value="end"
|
||||
icon="AlignRight"
|
||||
title="Align right"
|
||||
active={gridHAlign === "end"}
|
||||
componentId={id}
|
||||
/>
|
||||
<GridStylesButton
|
||||
style={gridHAlignVar}
|
||||
value="stretch"
|
||||
icon="MoveLeftRight"
|
||||
title="Stretch horizontally"
|
||||
active={gridHAlign === "stretch"}
|
||||
componentId={id}
|
||||
/>
|
||||
<div class="divider" />
|
||||
<GridStylesButton
|
||||
style={gridVAlignVar}
|
||||
value="start"
|
||||
icon="AlignTop"
|
||||
title="Align top"
|
||||
active={gridVAlign === "start"}
|
||||
componentId={id}
|
||||
/>
|
||||
<GridStylesButton
|
||||
style={gridVAlignVar}
|
||||
value="center"
|
||||
icon="AlignMiddle"
|
||||
title="Align middle"
|
||||
active={gridVAlign === "center"}
|
||||
componentId={id}
|
||||
/>
|
||||
<GridStylesButton
|
||||
style={gridVAlignVar}
|
||||
value="end"
|
||||
icon="AlignBottom"
|
||||
title="Align bottom"
|
||||
active={gridVAlign === "end"}
|
||||
componentId={id}
|
||||
/>
|
||||
<GridStylesButton
|
||||
style={gridVAlignVar}
|
||||
value="stretch"
|
||||
icon="MoveUpDown"
|
||||
title="Stretch vertically"
|
||||
active={gridVAlign === "stretch"}
|
||||
componentId={id}
|
||||
/>
|
||||
<div class="divider" />
|
||||
{/if}
|
||||
{#each settings as setting, idx}
|
||||
{#if setting.type === "select"}
|
||||
{#if setting.barStyle === "buttons"}
|
||||
|
@ -141,6 +273,7 @@
|
|||
value={option.value}
|
||||
icon={option.barIcon}
|
||||
title={option.barTitle || option.label}
|
||||
{component}
|
||||
/>
|
||||
{/each}
|
||||
{:else}
|
||||
|
@ -148,6 +281,7 @@
|
|||
prop={setting.key}
|
||||
options={setting.options}
|
||||
label={setting.label}
|
||||
{component}
|
||||
/>
|
||||
{/if}
|
||||
{:else if setting.type === "boolean"}
|
||||
|
@ -156,9 +290,10 @@
|
|||
icon={setting.barIcon}
|
||||
title={setting.barTitle || setting.label}
|
||||
bool
|
||||
{component}
|
||||
/>
|
||||
{:else if setting.type === "color"}
|
||||
<SettingsColorPicker prop={setting.key} />
|
||||
<SettingsColorPicker prop={setting.key} {component} />
|
||||
{/if}
|
||||
{#if setting.barSeparator !== false && (settings.length != idx + 1 || !isRoot)}
|
||||
<div class="divider" />
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
<script>
|
||||
import { Icon } from "@budibase/bbui"
|
||||
import { builderStore, componentStore } from "stores"
|
||||
import { builderStore } from "stores"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
export let prop
|
||||
export let value
|
||||
export let icon
|
||||
export let title
|
||||
export let rotate = false
|
||||
export let bool = false
|
||||
export let active = false
|
||||
export let component
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
$: currentValue = $componentStore.selectedComponent?.[prop]
|
||||
|
||||
$: currentValue = component?.[prop]
|
||||
$: active = prop && (bool ? !!currentValue : currentValue === value)
|
||||
</script>
|
||||
|
||||
|
@ -19,7 +21,6 @@
|
|||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
{title}
|
||||
class:rotate
|
||||
class:active
|
||||
on:click={() => {
|
||||
if (prop) {
|
||||
|
@ -49,7 +50,4 @@
|
|||
background-color: rgba(13, 102, 208, 0.1);
|
||||
color: var(--spectrum-global-color-blue-600);
|
||||
}
|
||||
.rotate {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<script>
|
||||
import { ColorPicker } from "@budibase/bbui"
|
||||
import { builderStore, componentStore } from "stores"
|
||||
import { builderStore } from "stores"
|
||||
|
||||
export let prop
|
||||
export let component
|
||||
|
||||
$: currentValue = $componentStore.selectedComponent?.[prop]
|
||||
$: currentValue = component?.[prop]
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
<script>
|
||||
import { Select } from "@budibase/bbui"
|
||||
import { builderStore, componentStore } from "stores"
|
||||
import { builderStore } from "stores"
|
||||
|
||||
export let prop
|
||||
export let options
|
||||
export let label
|
||||
export let component
|
||||
|
||||
$: currentValue = $componentStore.selectedComponent?.[prop]
|
||||
$: currentValue = component?.[prop]
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
|
|
@ -15,3 +15,6 @@ export const ActionTypes = {
|
|||
|
||||
export const DNDPlaceholderID = "dnd-placeholder"
|
||||
export const ScreenslotType = "screenslot"
|
||||
export const GridRowHeight = 24
|
||||
export const GridColumns = 12
|
||||
export const GridSpacing = 4
|
||||
|
|
|
@ -41,13 +41,20 @@ const createBuilderStore = () => {
|
|||
eventStore.actions.dispatchEvent("update-prop", { prop, value })
|
||||
},
|
||||
updateStyles: async (styles, id) => {
|
||||
await eventStore.actions.dispatchEvent("update-styles", { styles, id })
|
||||
await eventStore.actions.dispatchEvent("update-styles", {
|
||||
styles,
|
||||
id,
|
||||
})
|
||||
},
|
||||
keyDown: (key, ctrlKey) => {
|
||||
eventStore.actions.dispatchEvent("key-down", { key, ctrlKey })
|
||||
},
|
||||
duplicateComponent: id => {
|
||||
eventStore.actions.dispatchEvent("duplicate-component", { id })
|
||||
duplicateComponent: (id, mode = "below", selectComponent = true) => {
|
||||
eventStore.actions.dispatchEvent("duplicate-component", {
|
||||
id,
|
||||
mode,
|
||||
selectComponent,
|
||||
})
|
||||
},
|
||||
deleteComponent: id => {
|
||||
eventStore.actions.dispatchEvent("delete-component", { id })
|
||||
|
|
|
@ -142,9 +142,6 @@ const createComponentStore = () => {
|
|||
}
|
||||
|
||||
const getComponentInstance = id => {
|
||||
if (!id) {
|
||||
return null
|
||||
}
|
||||
return derived(store, $store => $store.mountedComponents[id])
|
||||
}
|
||||
|
||||
|
|
|
@ -129,29 +129,30 @@ const createScreenStore = () => {
|
|||
// If we don't have a legacy custom layout, build a layout structure
|
||||
// from the screen navigation settings
|
||||
if (!activeLayout) {
|
||||
let navigationSettings = {
|
||||
let layoutSettings = {
|
||||
navigation: "None",
|
||||
pageWidth: activeScreen?.width || "Large",
|
||||
embedded: $appStore.embedded,
|
||||
}
|
||||
if (activeScreen?.showNavigation) {
|
||||
navigationSettings = {
|
||||
...navigationSettings,
|
||||
layoutSettings = {
|
||||
...layoutSettings,
|
||||
...($builderStore.navigation || $appStore.application?.navigation),
|
||||
}
|
||||
|
||||
// Default navigation to top
|
||||
if (!navigationSettings.navigation) {
|
||||
navigationSettings.navigation = "Top"
|
||||
if (!layoutSettings.navigation) {
|
||||
layoutSettings.navigation = "Top"
|
||||
}
|
||||
|
||||
// Default title to app name
|
||||
if (!navigationSettings.title && !navigationSettings.hideTitle) {
|
||||
navigationSettings.title = $appStore.application?.name
|
||||
if (!layoutSettings.title && !layoutSettings.hideTitle) {
|
||||
layoutSettings.title = $appStore.application?.name
|
||||
}
|
||||
|
||||
// Default to the org logo
|
||||
if (!navigationSettings.logoUrl) {
|
||||
navigationSettings.logoUrl = $orgStore?.logoUrl
|
||||
if (!layoutSettings.logoUrl) {
|
||||
layoutSettings.logoUrl = $orgStore?.logoUrl
|
||||
}
|
||||
}
|
||||
activeLayout = {
|
||||
|
@ -173,8 +174,7 @@ const createScreenStore = () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
...navigationSettings,
|
||||
embedded: $appStore.embedded,
|
||||
...layoutSettings,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
export const domDebounce = (callback, extractParams = x => x) => {
|
||||
let active = false
|
||||
let lastParams
|
||||
return (...params) => {
|
||||
lastParams = extractParams(...params)
|
||||
if (!active) {
|
||||
active = true
|
||||
requestAnimationFrame(() => {
|
||||
callback(lastParams)
|
||||
active = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
import { GridSpacing, GridRowHeight } from "constants"
|
||||
import { builderStore } from "stores"
|
||||
import { buildStyleString } from "utils/styleable.js"
|
||||
|
||||
/**
|
||||
* We use CSS variables on components to control positioning and layout of
|
||||
* components inside grids.
|
||||
* --grid-[mobile/desktop]-[row/col]-[start-end]: for positioning
|
||||
* --grid-[mobile/desktop]-[h/v]-align: for layout of inner components within
|
||||
* the components grid bounds
|
||||
*
|
||||
* Component definitions define their default layout preference via the
|
||||
* `grid.hAlign` and `grid.vAlign` keys in the manifest.
|
||||
*
|
||||
* We also apply grid-[mobile/desktop]-grow CSS classes to component wrapper
|
||||
* DOM nodes to use later in selectors, to control the sizing of children.
|
||||
*/
|
||||
|
||||
// Enum representing the different CSS variables we use for grid metadata
|
||||
export const GridParams = {
|
||||
HAlign: "h-align",
|
||||
VAlign: "v-align",
|
||||
ColStart: "col-start",
|
||||
ColEnd: "col-end",
|
||||
RowStart: "row-start",
|
||||
RowEnd: "row-end",
|
||||
}
|
||||
|
||||
// Classes used in selectors inside grid containers to control child styles
|
||||
export const GridClasses = {
|
||||
DesktopFill: "grid-desktop-grow",
|
||||
MobileFill: "grid-mobile-grow",
|
||||
}
|
||||
|
||||
// Enum for device preview type, included in grid CSS variables
|
||||
export const Devices = {
|
||||
Desktop: "desktop",
|
||||
Mobile: "mobile",
|
||||
}
|
||||
|
||||
export const GridDragModes = {
|
||||
Resize: "resize",
|
||||
Move: "move",
|
||||
}
|
||||
|
||||
// Builds a CSS variable name for a certain piece of grid metadata
|
||||
export const getGridVar = (device, param) => `--grid-${device}-${param}`
|
||||
|
||||
// Determines whether a JS event originated from immediately within a grid
|
||||
export const isGridEvent = e => {
|
||||
return (
|
||||
e.target.dataset?.indicator === "true" ||
|
||||
e.target
|
||||
.closest?.(".component")
|
||||
?.parentNode.closest(".component")
|
||||
?.childNodes[0]?.classList?.contains("grid")
|
||||
)
|
||||
}
|
||||
|
||||
// Svelte action to apply required class names and styles to our component
|
||||
// wrappers
|
||||
export const gridLayout = (node, metadata) => {
|
||||
let selectComponent
|
||||
|
||||
// Applies the required listeners, CSS and classes to a component DOM node
|
||||
const applyMetadata = metadata => {
|
||||
const {
|
||||
id,
|
||||
styles,
|
||||
interactive,
|
||||
errored,
|
||||
definition,
|
||||
draggable,
|
||||
insideGrid,
|
||||
ignoresLayout,
|
||||
} = metadata
|
||||
if (!insideGrid) {
|
||||
return
|
||||
}
|
||||
|
||||
// If this component ignores layout, flag it as such so that we can avoid
|
||||
// selecting it later
|
||||
if (ignoresLayout) {
|
||||
node.classList.add("ignores-layout")
|
||||
return
|
||||
}
|
||||
|
||||
// Callback to select the component when clicking on the wrapper
|
||||
selectComponent = e => {
|
||||
e.stopPropagation()
|
||||
builderStore.actions.selectComponent(id)
|
||||
}
|
||||
|
||||
// Determine default width and height of component
|
||||
let width = errored ? 500 : definition?.size?.width || 200
|
||||
let height = errored ? 60 : definition?.size?.height || 200
|
||||
width += 2 * GridSpacing
|
||||
height += 2 * GridSpacing
|
||||
let vars = {
|
||||
"--default-width": width,
|
||||
"--default-height": height,
|
||||
}
|
||||
|
||||
// Generate defaults for all grid params
|
||||
const defaults = {
|
||||
[GridParams.HAlign]: definition?.grid?.hAlign || "stretch",
|
||||
[GridParams.VAlign]: definition?.grid?.vAlign || "center",
|
||||
[GridParams.ColStart]: 1,
|
||||
[GridParams.ColEnd]:
|
||||
"round(up, calc((var(--grid-spacing) * 2 + var(--default-width)) / var(--col-size) + 1))",
|
||||
[GridParams.RowStart]: 1,
|
||||
[GridParams.RowEnd]: Math.max(2, Math.ceil(height / GridRowHeight) + 1),
|
||||
}
|
||||
|
||||
// Specify values for all grid params for all devices, and strip these CSS
|
||||
// variables from the styles being applied to the inner component, as we
|
||||
// want to apply these to the wrapper instead
|
||||
for (let param of Object.values(GridParams)) {
|
||||
let dVar = getGridVar(Devices.Desktop, param)
|
||||
let mVar = getGridVar(Devices.Mobile, param)
|
||||
vars[dVar] = styles[dVar] ?? styles[mVar] ?? defaults[param]
|
||||
vars[mVar] = styles[mVar] ?? styles[dVar] ?? defaults[param]
|
||||
}
|
||||
|
||||
// Apply some overrides depending on component state
|
||||
if (errored) {
|
||||
vars[getGridVar(Devices.Desktop, GridParams.HAlign)] = "stretch"
|
||||
vars[getGridVar(Devices.Mobile, GridParams.HAlign)] = "stretch"
|
||||
vars[getGridVar(Devices.Desktop, GridParams.VAlign)] = "stretch"
|
||||
vars[getGridVar(Devices.Mobile, GridParams.VAlign)] = "stretch"
|
||||
}
|
||||
|
||||
// Apply some metadata to data attributes to speed up lookups
|
||||
const addDataTag = (tagName, device, param) => {
|
||||
const val = `${vars[getGridVar(device, param)]}`
|
||||
if (node.dataset[tagName] !== val) {
|
||||
node.dataset[tagName] = val
|
||||
}
|
||||
}
|
||||
addDataTag("gridDesktopRowEnd", Devices.Desktop, GridParams.RowEnd)
|
||||
addDataTag("gridMobileRowEnd", Devices.Mobile, GridParams.RowEnd)
|
||||
addDataTag("gridDesktopHAlign", Devices.Desktop, GridParams.HAlign)
|
||||
addDataTag("gridMobileHAlign", Devices.Mobile, GridParams.HAlign)
|
||||
addDataTag("gridDesktopVAlign", Devices.Desktop, GridParams.VAlign)
|
||||
addDataTag("gridMobileVAlign", Devices.Mobile, GridParams.VAlign)
|
||||
if (node.dataset.insideGrid !== true) {
|
||||
node.dataset.insideGrid = true
|
||||
}
|
||||
|
||||
// Apply all CSS variables to the wrapper
|
||||
node.style = buildStyleString(vars)
|
||||
|
||||
// Add a listener to select this node on click
|
||||
if (interactive) {
|
||||
node.addEventListener("click", selectComponent, false)
|
||||
}
|
||||
|
||||
// Add draggable attribute
|
||||
node.setAttribute("draggable", !!draggable)
|
||||
}
|
||||
|
||||
// Removes the previously set up listeners
|
||||
const removeListeners = () => {
|
||||
// By checking if this is defined we can avoid trying to remove event
|
||||
// listeners on every component
|
||||
if (selectComponent) {
|
||||
node.removeEventListener("click", selectComponent, false)
|
||||
selectComponent = null
|
||||
}
|
||||
}
|
||||
|
||||
applyMetadata(metadata)
|
||||
|
||||
return {
|
||||
update(newMetadata) {
|
||||
removeListeners()
|
||||
applyMetadata(newMetadata)
|
||||
},
|
||||
destroy() {
|
||||
removeListeners()
|
||||
},
|
||||
}
|
||||
}
|
|
@ -3,13 +3,13 @@ import { builderStore } from "stores"
|
|||
/**
|
||||
* Helper to build a CSS string from a style object.
|
||||
*/
|
||||
const buildStyleString = (styleObject, customStyles) => {
|
||||
export const buildStyleString = (styleObject, customStyles) => {
|
||||
let str = ""
|
||||
Object.entries(styleObject || {}).forEach(([style, value]) => {
|
||||
if (style && value != null) {
|
||||
str += `${style}: ${value}; `
|
||||
for (let key of Object.keys(styleObject || {})) {
|
||||
if (styleObject[key] != null) {
|
||||
str += `${key}:${styleObject[key]};`
|
||||
}
|
||||
})
|
||||
}
|
||||
return str + (customStyles || "")
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@
|
|||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-color: var(--spectrum-global-color-gray-200);
|
||||
}
|
||||
|
|
|
@ -9,3 +9,4 @@ export { memo, derivedMemo } from "./memo"
|
|||
export { createWebsocket } from "./websocket"
|
||||
export * from "./download"
|
||||
export * from "./theme"
|
||||
export * from "./settings"
|
||||
|
|
|
@ -4,32 +4,23 @@ import { writable, get, derived } from "svelte/store"
|
|||
// subscribed children will only fire when a new value is actually set
|
||||
export const memo = initialValue => {
|
||||
const store = writable(initialValue)
|
||||
let currentJSON = JSON.stringify(initialValue)
|
||||
|
||||
const tryUpdateValue = (newValue, currentValue) => {
|
||||
// Sanity check for primitive equality
|
||||
if (currentValue === newValue) {
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise deep compare via JSON stringify
|
||||
const currentString = JSON.stringify(currentValue)
|
||||
const newString = JSON.stringify(newValue)
|
||||
if (currentString !== newString) {
|
||||
const tryUpdateValue = newValue => {
|
||||
const newJSON = JSON.stringify(newValue)
|
||||
if (newJSON !== currentJSON) {
|
||||
store.set(newValue)
|
||||
currentJSON = newJSON
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
set: newValue => {
|
||||
const currentValue = get(store)
|
||||
tryUpdateValue(newValue, currentValue)
|
||||
},
|
||||
set: tryUpdateValue,
|
||||
update: updateFn => {
|
||||
const currentValue = get(store)
|
||||
let mutableCurrentValue = JSON.parse(JSON.stringify(currentValue))
|
||||
let mutableCurrentValue = JSON.parse(currentJSON)
|
||||
const newValue = updateFn(mutableCurrentValue)
|
||||
tryUpdateValue(newValue, currentValue)
|
||||
tryUpdateValue(newValue)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { helpers } from "@budibase/shared-core"
|
||||
|
||||
// Util to check if a setting can be rendered for a certain instance, based on
|
||||
// the "dependsOn" metadata in the manifest
|
||||
export const shouldDisplaySetting = (instance, setting) => {
|
||||
let dependsOn = setting.dependsOn
|
||||
if (dependsOn && !Array.isArray(dependsOn)) {
|
||||
dependsOn = [dependsOn]
|
||||
}
|
||||
if (!dependsOn?.length) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ensure all conditions are met
|
||||
return dependsOn.every(condition => {
|
||||
let dependantSetting = condition
|
||||
let dependantValues = null
|
||||
let invert = !!condition.invert
|
||||
if (typeof condition === "object") {
|
||||
dependantSetting = condition.setting
|
||||
dependantValues = condition.value
|
||||
}
|
||||
if (!dependantSetting) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Ensure values is an array
|
||||
if (!Array.isArray(dependantValues)) {
|
||||
dependantValues = [dependantValues]
|
||||
}
|
||||
|
||||
// If inverting, we want to ensure that we don't have any matches.
|
||||
// If not inverting, we want to ensure that we do have any matches.
|
||||
const currentVal = helpers.deepGet(instance, dependantSetting)
|
||||
const anyMatches = dependantValues.some(dependantVal => {
|
||||
if (dependantVal == null) {
|
||||
return currentVal != null && currentVal !== false && currentVal !== ""
|
||||
}
|
||||
return dependantVal === currentVal
|
||||
})
|
||||
return anyMatches !== invert
|
||||
})
|
||||
}
|
|
@ -151,7 +151,10 @@ export function buildExternalRelationships(
|
|||
return relationships
|
||||
}
|
||||
|
||||
export function buildInternalRelationships(table: Table): RelationshipsJson[] {
|
||||
export function buildInternalRelationships(
|
||||
table: Table,
|
||||
allTables: Table[]
|
||||
): RelationshipsJson[] {
|
||||
const relationships: RelationshipsJson[] = []
|
||||
const links = Object.values(table.schema).filter(
|
||||
column => column.type === FieldType.LINK
|
||||
|
@ -164,6 +167,10 @@ export function buildInternalRelationships(table: Table): RelationshipsJson[] {
|
|||
const linkTableId = link.tableId!
|
||||
const junctionTableId = generateJunctionTableID(tableId, linkTableId)
|
||||
const isFirstTable = tableId > linkTableId
|
||||
// skip relationships with missing table definitions
|
||||
if (!allTables.find(table => table._id === linkTableId)) {
|
||||
continue
|
||||
}
|
||||
relationships.push({
|
||||
through: junctionTableId,
|
||||
column: link.name,
|
||||
|
@ -192,10 +199,10 @@ export function buildSqlFieldList(
|
|||
function extractRealFields(table: Table, existing: string[] = []) {
|
||||
return Object.entries(table.schema)
|
||||
.filter(
|
||||
column =>
|
||||
column[1].type !== FieldType.LINK &&
|
||||
column[1].type !== FieldType.FORMULA &&
|
||||
!existing.find((field: string) => field === column[0])
|
||||
([columnName, column]) =>
|
||||
column.type !== FieldType.LINK &&
|
||||
column.type !== FieldType.FORMULA &&
|
||||
!existing.find((field: string) => field === columnName)
|
||||
)
|
||||
.map(column => `${table.name}.${column[0]}`)
|
||||
}
|
||||
|
|
|
@ -1664,7 +1664,7 @@ describe.each([
|
|||
isInternal &&
|
||||
describe("attachments and signatures", () => {
|
||||
const coreAttachmentEnrichment = async (
|
||||
schema: any,
|
||||
schema: TableSchema,
|
||||
field: string,
|
||||
attachmentCfg: string | string[]
|
||||
) => {
|
||||
|
@ -1691,7 +1691,7 @@ describe.each([
|
|||
|
||||
await withEnv({ SELF_HOSTED: "true" }, async () => {
|
||||
return context.doInAppContext(config.getAppId(), async () => {
|
||||
const enriched: Row[] = await outputProcessing(table, [row])
|
||||
const enriched: Row[] = await outputProcessing(testTable, [row])
|
||||
const [targetRow] = enriched
|
||||
const attachmentEntries = Array.isArray(targetRow[field])
|
||||
? targetRow[field]
|
||||
|
|
|
@ -2762,6 +2762,57 @@ describe.each([
|
|||
})
|
||||
})
|
||||
|
||||
isSql &&
|
||||
describe("primaryDisplay", () => {
|
||||
beforeAll(async () => {
|
||||
let toRelateTable = await createTable({
|
||||
name: {
|
||||
name: "name",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
})
|
||||
table = await config.api.table.save(
|
||||
tableForDatasource(datasource, {
|
||||
schema: {
|
||||
name: {
|
||||
name: "name",
|
||||
type: FieldType.STRING,
|
||||
},
|
||||
link: {
|
||||
name: "link",
|
||||
type: FieldType.LINK,
|
||||
relationshipType: RelationshipType.MANY_TO_ONE,
|
||||
tableId: toRelateTable._id!,
|
||||
fieldName: "link",
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
toRelateTable = await config.api.table.get(toRelateTable._id!)
|
||||
await config.api.table.save({
|
||||
...toRelateTable,
|
||||
primaryDisplay: "link",
|
||||
})
|
||||
const relatedRows = await Promise.all([
|
||||
config.api.row.save(toRelateTable._id!, { name: "test" }),
|
||||
])
|
||||
await Promise.all([
|
||||
config.api.row.save(table._id!, {
|
||||
name: "test",
|
||||
link: relatedRows.map(row => row._id),
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it("should be able to query, primary display on related table shouldn't be used", async () => {
|
||||
// this test makes sure that if a relationship has been specified as the primary display on a table
|
||||
// it is ignored and another column is used instead
|
||||
await expectQuery({}).toContain([
|
||||
{ name: "test", link: [{ primaryDisplay: "test" }] },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
!isLucene &&
|
||||
describe("$and", () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
withEnv as withCoreEnv,
|
||||
setEnv as setCoreEnv,
|
||||
} from "@budibase/backend-core"
|
||||
import sdk from "../../../sdk"
|
||||
|
||||
describe.each([
|
||||
["lucene", undefined],
|
||||
|
@ -120,6 +121,7 @@ describe.each([
|
|||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
mocks.licenses.useCloudFree()
|
||||
})
|
||||
|
||||
|
@ -1602,6 +1604,28 @@ describe.each([
|
|||
})
|
||||
expect(response.rows).toHaveLength(0)
|
||||
})
|
||||
|
||||
it("queries the row api passing the view fields only", async () => {
|
||||
const searchSpy = jest.spyOn(sdk.rows, "search")
|
||||
|
||||
const view = await config.api.viewV2.create({
|
||||
tableId: table._id!,
|
||||
name: generator.guid(),
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
one: { visible: false },
|
||||
},
|
||||
})
|
||||
|
||||
await config.api.viewV2.search(view.id, { query: {} })
|
||||
expect(searchSpy).toHaveBeenCalledTimes(1)
|
||||
|
||||
expect(searchSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
fields: ["id"],
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("permissions", () => {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import LinkController from "./LinkController"
|
||||
import {
|
||||
getLinkDocuments,
|
||||
getUniqueByProp,
|
||||
getRelatedTableForField,
|
||||
getLinkedTableIDs,
|
||||
getLinkedTable,
|
||||
getLinkedTableIDs,
|
||||
getRelatedTableForField,
|
||||
getUniqueByProp,
|
||||
} from "./linkUtils"
|
||||
import flatten from "lodash/flatten"
|
||||
import { USER_METDATA_PREFIX } from "../utils"
|
||||
|
@ -13,16 +13,25 @@ import { getGlobalUsersFromMetadata } from "../../utilities/global"
|
|||
import { processFormulas } from "../../utilities/rowProcessor"
|
||||
import { context } from "@budibase/backend-core"
|
||||
import {
|
||||
Table,
|
||||
Row,
|
||||
LinkDocumentValue,
|
||||
FieldType,
|
||||
ContextUser,
|
||||
FieldType,
|
||||
LinkDocumentValue,
|
||||
Row,
|
||||
Table,
|
||||
} from "@budibase/types"
|
||||
import sdk from "../../sdk"
|
||||
|
||||
export { IncludeDocs, getLinkDocuments, createLinkView } from "./linkUtils"
|
||||
|
||||
const INVALID_DISPLAY_COLUMN_TYPE = [
|
||||
FieldType.LINK,
|
||||
FieldType.ATTACHMENTS,
|
||||
FieldType.ATTACHMENT_SINGLE,
|
||||
FieldType.SIGNATURE_SINGLE,
|
||||
FieldType.BB_REFERENCE,
|
||||
FieldType.BB_REFERENCE_SINGLE,
|
||||
]
|
||||
|
||||
/**
|
||||
* This functionality makes sure that when rows with links are created, updated or deleted they are processed
|
||||
* correctly - making sure that no stale links are left around and that all links have been made successfully.
|
||||
|
@ -206,6 +215,31 @@ export async function attachFullLinkedDocs(
|
|||
return rows
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a valid value for the primary display, avoiding columns which break things
|
||||
* like relationships (can be circular).
|
||||
* @param row The row to lift a value from for the primary display.
|
||||
* @param table The related table to attempt to work out the primary display column from.
|
||||
*/
|
||||
function getPrimaryDisplayValue(row: Row, table?: Table) {
|
||||
const primaryDisplay = table?.primaryDisplay
|
||||
let invalid = true
|
||||
if (primaryDisplay) {
|
||||
const primaryDisplaySchema = table?.schema[primaryDisplay]
|
||||
invalid = INVALID_DISPLAY_COLUMN_TYPE.includes(primaryDisplaySchema.type)
|
||||
}
|
||||
if (invalid || !primaryDisplay) {
|
||||
const validKey = Object.keys(table?.schema || {}).find(
|
||||
key =>
|
||||
table?.schema[key].type &&
|
||||
!INVALID_DISPLAY_COLUMN_TYPE.includes(table?.schema[key].type)
|
||||
)
|
||||
return validKey ? row[validKey] : undefined
|
||||
} else {
|
||||
return row[primaryDisplay]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will take the given enriched rows and squash the links to only contain the primary display field.
|
||||
* @param table The table from which the rows originated.
|
||||
|
@ -232,9 +266,7 @@ export async function squashLinksToPrimaryDisplay(
|
|||
const linkTblId = link.tableId || getRelatedTableForField(table, column)
|
||||
const linkedTable = await getLinkedTable(linkTblId!, linkedTables)
|
||||
const obj: any = { _id: link._id }
|
||||
if (linkedTable?.primaryDisplay && link[linkedTable.primaryDisplay]) {
|
||||
obj.primaryDisplay = link[linkedTable.primaryDisplay]
|
||||
}
|
||||
obj.primaryDisplay = getPrimaryDisplayValue(link, linkedTable)
|
||||
newLinks.push(obj)
|
||||
}
|
||||
row[column] = newLinks
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import { db } from "@budibase/backend-core"
|
||||
import {
|
||||
FieldType,
|
||||
isLogicalSearchOperator,
|
||||
SearchFilters,
|
||||
Table,
|
||||
} from "@budibase/types"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import sdk from "../../../sdk"
|
||||
|
||||
export const removeInvalidFilters = (
|
||||
filters: SearchFilters,
|
||||
validFields: string[]
|
||||
) => {
|
||||
const result = cloneDeep(filters)
|
||||
|
||||
validFields = validFields.map(f => f.toLowerCase())
|
||||
for (const filterKey of Object.keys(result) as (keyof SearchFilters)[]) {
|
||||
const filter = result[filterKey]
|
||||
if (!filter || typeof filter !== "object") {
|
||||
continue
|
||||
}
|
||||
if (isLogicalSearchOperator(filterKey)) {
|
||||
const resultingConditions: SearchFilters[] = []
|
||||
for (const condition of filter.conditions) {
|
||||
const resultingCondition = removeInvalidFilters(condition, validFields)
|
||||
if (Object.keys(resultingCondition).length) {
|
||||
resultingConditions.push(resultingCondition)
|
||||
}
|
||||
}
|
||||
if (resultingConditions.length) {
|
||||
filter.conditions = resultingConditions
|
||||
} else {
|
||||
delete result[filterKey]
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
for (const columnKey of Object.keys(filter)) {
|
||||
const possibleKeys = [columnKey, db.removeKeyNumbering(columnKey)].map(
|
||||
c => c.toLowerCase()
|
||||
)
|
||||
if (!validFields.some(f => possibleKeys.includes(f.toLowerCase()))) {
|
||||
delete filter[columnKey as keyof typeof filter]
|
||||
}
|
||||
}
|
||||
if (!Object.keys(filter).length) {
|
||||
delete result[filterKey]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const getQueryableFields = async (
|
||||
fields: string[],
|
||||
table: Table
|
||||
): Promise<string[]> => {
|
||||
const extractTableFields = async (
|
||||
table: Table,
|
||||
allowedFields: string[],
|
||||
fromTables: string[],
|
||||
opts?: { noRelationships?: boolean }
|
||||
): Promise<string[]> => {
|
||||
const result = []
|
||||
for (const field of Object.keys(table.schema).filter(
|
||||
f => allowedFields.includes(f) && table.schema[f].visible !== false
|
||||
)) {
|
||||
const subSchema = table.schema[field]
|
||||
const isRelationship = subSchema.type === FieldType.LINK
|
||||
// avoid relationship loops
|
||||
if (
|
||||
isRelationship &&
|
||||
(opts?.noRelationships || fromTables.includes(subSchema.tableId))
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (isRelationship) {
|
||||
try {
|
||||
const relatedTable = await sdk.tables.getTable(subSchema.tableId)
|
||||
const relatedFields = await extractTableFields(
|
||||
relatedTable,
|
||||
Object.keys(relatedTable.schema),
|
||||
[...fromTables, subSchema.tableId],
|
||||
// don't let it recurse back and forth between relationships
|
||||
{ noRelationships: true }
|
||||
)
|
||||
|
||||
result.push(
|
||||
...relatedFields.flatMap(f => [
|
||||
`${subSchema.name}.${f}`,
|
||||
// should be able to filter by relationship using table name
|
||||
`${relatedTable.name}.${f}`,
|
||||
])
|
||||
)
|
||||
} catch (err: any) {
|
||||
// if related table is removed, ignore
|
||||
if (err.status !== 404) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.push(field)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const result = [
|
||||
"_id", // Querying by _id is always allowed, even if it's never part of the schema
|
||||
]
|
||||
|
||||
result.push(...(await extractTableFields(table, fields, [table._id!])))
|
||||
|
||||
return result
|
||||
}
|
|
@ -14,6 +14,7 @@ import sdk from "../../index"
|
|||
import { searchInputMapping } from "./search/utils"
|
||||
import { db as dbCore } from "@budibase/backend-core"
|
||||
import tracer from "dd-trace"
|
||||
import { getQueryableFields, removeInvalidFilters } from "./queryUtils"
|
||||
|
||||
export { isValidFilter } from "../../../integrations/utils"
|
||||
|
||||
|
@ -73,6 +74,18 @@ export async function search(
|
|||
const table = await sdk.tables.getTable(options.tableId)
|
||||
options = searchInputMapping(table, options)
|
||||
|
||||
if (options.query) {
|
||||
const tableFields = Object.keys(table.schema).filter(
|
||||
f => table.schema[f].visible !== false
|
||||
)
|
||||
|
||||
const queriableFields = await getQueryableFields(
|
||||
options.fields?.filter(f => tableFields.includes(f)) ?? tableFields,
|
||||
table
|
||||
)
|
||||
options.query = removeInvalidFilters(options.query, queriableFields)
|
||||
}
|
||||
|
||||
let result: SearchResponse<Row>
|
||||
if (isExternalTable) {
|
||||
span?.addTags({ searchType: "external" })
|
||||
|
|
|
@ -297,7 +297,7 @@ export async function search(
|
|||
throw new Error("Unable to find table")
|
||||
}
|
||||
|
||||
const relationships = buildInternalRelationships(table)
|
||||
const relationships = buildInternalRelationships(table, allTables)
|
||||
|
||||
const searchFilters: SearchFilters = {
|
||||
...cleanupFilters(query, table, allTables),
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
import { Datasource, FieldType, Row, Table } from "@budibase/types"
|
||||
import {
|
||||
AutoColumnFieldMetadata,
|
||||
AutoFieldSubType,
|
||||
Datasource,
|
||||
FieldType,
|
||||
NumberFieldMetadata,
|
||||
Table,
|
||||
} from "@budibase/types"
|
||||
|
||||
import TestConfiguration from "../../../../../tests/utilities/TestConfiguration"
|
||||
import { search } from "../../../../../sdk/app/rows/search"
|
||||
|
@ -32,7 +39,6 @@ describe.each([
|
|||
let envCleanup: (() => void) | undefined
|
||||
let datasource: Datasource | undefined
|
||||
let table: Table
|
||||
let rows: Row[]
|
||||
|
||||
beforeAll(async () => {
|
||||
await withCoreEnv({ SQS_SEARCH_ENABLE: isSqs ? "true" : "false" }, () =>
|
||||
|
@ -51,16 +57,28 @@ describe.each([
|
|||
datasource: await dsProvider,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
const idFieldSchema: NumberFieldMetadata | AutoColumnFieldMetadata =
|
||||
isInternal
|
||||
? {
|
||||
name: "id",
|
||||
type: FieldType.AUTO,
|
||||
subtype: AutoFieldSubType.AUTO_ID,
|
||||
autocolumn: true,
|
||||
}
|
||||
: {
|
||||
name: "id",
|
||||
type: FieldType.NUMBER,
|
||||
autocolumn: true,
|
||||
}
|
||||
|
||||
table = await config.api.table.save(
|
||||
tableForDatasource(datasource, {
|
||||
primary: ["id"],
|
||||
schema: {
|
||||
id: {
|
||||
name: "id",
|
||||
type: FieldType.NUMBER,
|
||||
autocolumn: true,
|
||||
},
|
||||
id: idFieldSchema,
|
||||
name: {
|
||||
name: "name",
|
||||
type: FieldType.STRING,
|
||||
|
@ -81,16 +99,13 @@ describe.each([
|
|||
})
|
||||
)
|
||||
|
||||
rows = []
|
||||
for (let i = 0; i < 10; i++) {
|
||||
rows.push(
|
||||
await config.api.row.save(table._id!, {
|
||||
name: generator.first(),
|
||||
surname: generator.last(),
|
||||
age: generator.age(),
|
||||
address: generator.address(),
|
||||
})
|
||||
)
|
||||
await config.api.row.save(table._id!, {
|
||||
name: generator.first(),
|
||||
surname: generator.last(),
|
||||
age: generator.age(),
|
||||
address: generator.address(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -138,4 +153,100 @@ describe.each([
|
|||
)
|
||||
})
|
||||
})
|
||||
|
||||
it("does not allow accessing hidden fields", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
await config.api.table.save({
|
||||
...table,
|
||||
schema: {
|
||||
...table.schema,
|
||||
name: {
|
||||
...table.schema.name,
|
||||
visible: true,
|
||||
},
|
||||
age: {
|
||||
...table.schema.age,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
})
|
||||
expect(result.rows).toHaveLength(10)
|
||||
for (const row of result.rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).toContain("surname")
|
||||
expect(keys).toContain("address")
|
||||
expect(keys).not.toContain("age")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("does not allow accessing hidden fields even if requested", async () => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
await config.api.table.save({
|
||||
...table,
|
||||
schema: {
|
||||
...table.schema,
|
||||
name: {
|
||||
...table.schema.name,
|
||||
visible: true,
|
||||
},
|
||||
age: {
|
||||
...table.schema.age,
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
const result = await search({
|
||||
tableId: table._id!,
|
||||
query: {},
|
||||
fields: ["name", "age"],
|
||||
})
|
||||
expect(result.rows).toHaveLength(10)
|
||||
for (const row of result.rows) {
|
||||
const keys = Object.keys(row)
|
||||
expect(keys).toContain("name")
|
||||
expect(keys).not.toContain("age")
|
||||
expect(keys).not.toContain("surname")
|
||||
expect(keys).not.toContain("address")
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
!isLucene &&
|
||||
it.each([
|
||||
[["id", "name", "age"], 3],
|
||||
[["name", "age"], 10],
|
||||
])(
|
||||
"cannot query by non search fields (fields: %s)",
|
||||
async (queryFields, expectedRows) => {
|
||||
await config.doInContext(config.appId, async () => {
|
||||
const { rows } = await search({
|
||||
tableId: table._id!,
|
||||
query: {
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
$and: {
|
||||
conditions: [
|
||||
{ range: { id: { low: 2, high: 4 } } },
|
||||
{ range: { id: { low: 3, high: 5 } } },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ equal: { id: 7 } },
|
||||
],
|
||||
},
|
||||
},
|
||||
fields: queryFields,
|
||||
})
|
||||
|
||||
expect(rows).toHaveLength(expectedRows)
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
|
@ -0,0 +1,505 @@
|
|||
import {
|
||||
FieldType,
|
||||
RelationshipType,
|
||||
SearchFilters,
|
||||
Table,
|
||||
} from "@budibase/types"
|
||||
import { getQueryableFields, removeInvalidFilters } from "../queryUtils"
|
||||
import { structures } from "../../../../api/routes/tests/utilities"
|
||||
import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
|
||||
|
||||
describe("query utils", () => {
|
||||
describe("removeInvalidFilters", () => {
|
||||
const fullFilters: SearchFilters = {
|
||||
equal: { one: "foo" },
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { one: "foo2", two: "bar" },
|
||||
notEmpty: { one: null },
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { three: "baz" },
|
||||
notEmpty: { forth: null },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
$and: {
|
||||
conditions: [{ equal: { one: "foo2" }, notEmpty: { one: null } }],
|
||||
},
|
||||
}
|
||||
|
||||
it("can filter empty queries", () => {
|
||||
const filters: SearchFilters = {}
|
||||
const result = removeInvalidFilters(filters, [])
|
||||
expect(result).toEqual({})
|
||||
})
|
||||
|
||||
it("does not trim any valid field", () => {
|
||||
const result = removeInvalidFilters(fullFilters, [
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"forth",
|
||||
])
|
||||
expect(result).toEqual(fullFilters)
|
||||
})
|
||||
|
||||
it("trims invalid field", () => {
|
||||
const result = removeInvalidFilters(fullFilters, [
|
||||
"one",
|
||||
"three",
|
||||
"forth",
|
||||
])
|
||||
expect(result).toEqual({
|
||||
equal: { one: "foo" },
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { one: "foo2" },
|
||||
notEmpty: { one: null },
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { three: "baz" },
|
||||
notEmpty: { forth: null },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
$and: {
|
||||
conditions: [{ equal: { one: "foo2" }, notEmpty: { one: null } }],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("trims invalid field keeping a valid fields", () => {
|
||||
const result = removeInvalidFilters(fullFilters, ["three", "forth"])
|
||||
const expected: SearchFilters = {
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { three: "baz" },
|
||||
notEmpty: { forth: null },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
expect(result).toEqual(expected)
|
||||
})
|
||||
|
||||
it("keeps filter key numering", () => {
|
||||
const prefixedFilters: SearchFilters = {
|
||||
equal: { "1:one": "foo" },
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { "2:one": "foo2", "3:two": "bar" },
|
||||
notEmpty: { "4:one": null },
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { "5:three": "baz", two: "bar2" },
|
||||
notEmpty: { forth: null },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
$and: {
|
||||
conditions: [{ equal: { "6:one": "foo2" }, notEmpty: { one: null } }],
|
||||
},
|
||||
}
|
||||
|
||||
const result = removeInvalidFilters(prefixedFilters, [
|
||||
"one",
|
||||
"three",
|
||||
"forth",
|
||||
])
|
||||
expect(result).toEqual({
|
||||
equal: { "1:one": "foo" },
|
||||
$or: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { "2:one": "foo2" },
|
||||
notEmpty: { "4:one": null },
|
||||
$and: {
|
||||
conditions: [
|
||||
{
|
||||
equal: { "5:three": "baz" },
|
||||
notEmpty: { forth: null },
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
$and: {
|
||||
conditions: [{ equal: { "6:one": "foo2" }, notEmpty: { one: null } }],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("handles relationship filters", () => {
|
||||
const prefixedFilters: SearchFilters = {
|
||||
$or: {
|
||||
conditions: [
|
||||
{ equal: { "1:other.one": "foo" } },
|
||||
{
|
||||
equal: {
|
||||
"2:other.one": "foo2",
|
||||
"3:other.two": "bar",
|
||||
"4:other.three": "baz",
|
||||
},
|
||||
},
|
||||
{ equal: { "another.three": "baz2" } },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const result = removeInvalidFilters(prefixedFilters, [
|
||||
"other.one",
|
||||
"other.two",
|
||||
"another.three",
|
||||
])
|
||||
expect(result).toEqual({
|
||||
$or: {
|
||||
conditions: [
|
||||
{ equal: { "1:other.one": "foo" } },
|
||||
{ equal: { "2:other.one": "foo2", "3:other.two": "bar" } },
|
||||
{ equal: { "another.three": "baz2" } },
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("getQueryableFields", () => {
|
||||
const config = new TestConfiguration()
|
||||
|
||||
beforeAll(async () => {
|
||||
await config.init()
|
||||
})
|
||||
|
||||
it("returns table schema fields and _id", async () => {
|
||||
const table: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
age: { name: "age", type: FieldType.NUMBER },
|
||||
},
|
||||
})
|
||||
|
||||
const result = await getQueryableFields(Object.keys(table.schema), table)
|
||||
expect(result).toEqual(["_id", "name", "age"])
|
||||
})
|
||||
|
||||
it("excludes hidden fields", async () => {
|
||||
const table: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
age: { name: "age", type: FieldType.NUMBER, visible: false },
|
||||
},
|
||||
})
|
||||
|
||||
const result = await getQueryableFields(Object.keys(table.schema), table)
|
||||
expect(result).toEqual(["_id", "name"])
|
||||
})
|
||||
|
||||
it("includes relationship fields", async () => {
|
||||
const aux: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
name: "auxTable",
|
||||
schema: {
|
||||
title: { name: "title", type: FieldType.STRING },
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
},
|
||||
})
|
||||
|
||||
const table: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
aux: {
|
||||
name: "aux",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux._id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "table",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(table.schema), table)
|
||||
})
|
||||
expect(result).toEqual([
|
||||
"_id",
|
||||
"name",
|
||||
"aux.title",
|
||||
"auxTable.title",
|
||||
"aux.name",
|
||||
"auxTable.name",
|
||||
])
|
||||
})
|
||||
|
||||
it("excludes hidden relationship fields", async () => {
|
||||
const aux: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
name: "auxTable",
|
||||
schema: {
|
||||
title: { name: "title", type: FieldType.STRING, visible: false },
|
||||
name: { name: "name", type: FieldType.STRING, visible: true },
|
||||
},
|
||||
})
|
||||
|
||||
const table: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
aux: {
|
||||
name: "aux",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux._id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "table",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(table.schema), table)
|
||||
})
|
||||
expect(result).toEqual(["_id", "name", "aux.name", "auxTable.name"])
|
||||
})
|
||||
|
||||
it("excludes all relationship fields if hidden", async () => {
|
||||
const aux: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
name: "auxTable",
|
||||
schema: {
|
||||
title: { name: "title", type: FieldType.STRING, visible: false },
|
||||
name: { name: "name", type: FieldType.STRING, visible: true },
|
||||
},
|
||||
})
|
||||
|
||||
const table: Table = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
aux: {
|
||||
name: "aux",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux._id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "table",
|
||||
visible: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(table.schema), table)
|
||||
})
|
||||
expect(result).toEqual(["_id", "name"])
|
||||
})
|
||||
|
||||
describe("nested relationship", () => {
|
||||
describe("one-to-many", () => {
|
||||
let table: Table, aux1: Table, aux2: Table
|
||||
|
||||
beforeAll(async () => {
|
||||
const { _id: aux1Id } = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
name: "aux1Table",
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
},
|
||||
})
|
||||
const { _id: aux2Id } = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
name: "aux2Table",
|
||||
schema: {
|
||||
title: { name: "title", type: FieldType.STRING },
|
||||
aux1_1: {
|
||||
name: "aux1_1",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux1Id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "aux2_1",
|
||||
},
|
||||
aux1_2: {
|
||||
name: "aux1_2",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux1Id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "aux2_2",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const { _id: tableId } = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
aux1: {
|
||||
name: "aux1",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux1Id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "table",
|
||||
},
|
||||
aux2: {
|
||||
name: "aux2",
|
||||
type: FieldType.LINK,
|
||||
tableId: aux2Id!,
|
||||
relationshipType: RelationshipType.ONE_TO_MANY,
|
||||
fieldName: "table",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// We need to refech them to get the updated foreign keys
|
||||
aux1 = await config.api.table.get(aux1Id!)
|
||||
aux2 = await config.api.table.get(aux2Id!)
|
||||
table = await config.api.table.get(tableId!)
|
||||
})
|
||||
|
||||
it("includes nested relationship fields from main table", async () => {
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(table.schema), table)
|
||||
})
|
||||
expect(result).toEqual([
|
||||
"_id",
|
||||
"name",
|
||||
// aux1 primitive props
|
||||
"aux1.name",
|
||||
"aux1Table.name",
|
||||
|
||||
// aux2 primitive props
|
||||
"aux2.title",
|
||||
"aux2Table.title",
|
||||
])
|
||||
})
|
||||
|
||||
it("includes nested relationship fields from aux 1 table", async () => {
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(aux1.schema), aux1)
|
||||
})
|
||||
expect(result).toEqual([
|
||||
"_id",
|
||||
"name",
|
||||
|
||||
// aux2_1 primitive props
|
||||
"aux2_1.title",
|
||||
"aux2Table.title",
|
||||
|
||||
// aux2_2 primitive props
|
||||
"aux2_2.title",
|
||||
"aux2Table.title",
|
||||
|
||||
// table primitive props
|
||||
"table.name",
|
||||
"TestTable.name",
|
||||
])
|
||||
})
|
||||
|
||||
it("includes nested relationship fields from aux 2 table", async () => {
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(aux2.schema), aux2)
|
||||
})
|
||||
expect(result).toEqual([
|
||||
"_id",
|
||||
"title",
|
||||
|
||||
// aux1_1 primitive props
|
||||
"aux1_1.name",
|
||||
"aux1Table.name",
|
||||
|
||||
// aux1_2 primitive props
|
||||
"aux1_2.name",
|
||||
"aux1Table.name",
|
||||
|
||||
// table primitive props
|
||||
"table.name",
|
||||
"TestTable.name",
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("many-to-many", () => {
|
||||
let table: Table, aux: Table
|
||||
|
||||
beforeAll(async () => {
|
||||
const { _id: auxId } = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
name: "auxTable",
|
||||
schema: {
|
||||
title: { name: "title", type: FieldType.STRING },
|
||||
},
|
||||
})
|
||||
|
||||
const { _id: tableId } = await config.api.table.save({
|
||||
...structures.basicTable(),
|
||||
schema: {
|
||||
name: { name: "name", type: FieldType.STRING },
|
||||
aux: {
|
||||
name: "aux",
|
||||
type: FieldType.LINK,
|
||||
tableId: auxId!,
|
||||
relationshipType: RelationshipType.MANY_TO_MANY,
|
||||
fieldName: "table",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// We need to refech them to get the updated foreign keys
|
||||
aux = await config.api.table.get(auxId!)
|
||||
table = await config.api.table.get(tableId!)
|
||||
})
|
||||
|
||||
it("includes nested relationship fields from main table", async () => {
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(table.schema), table)
|
||||
})
|
||||
expect(result).toEqual([
|
||||
"_id",
|
||||
"name",
|
||||
|
||||
// deep 1 aux primitive props
|
||||
"aux.title",
|
||||
"auxTable.title",
|
||||
])
|
||||
})
|
||||
|
||||
it("includes nested relationship fields from aux table", async () => {
|
||||
const result = await config.doInContext(config.appId, () => {
|
||||
return getQueryableFields(Object.keys(aux.schema), aux)
|
||||
})
|
||||
expect(result).toEqual([
|
||||
"_id",
|
||||
"title",
|
||||
|
||||
// deep 1 dependency primitive props
|
||||
"table.name",
|
||||
"TestTable.name",
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -26,8 +26,13 @@ import {
|
|||
processOutputBBReferences,
|
||||
} from "./bbReferenceProcessor"
|
||||
import { isExternalTableID } from "../../integrations/utils"
|
||||
import { helpers } from "@budibase/shared-core"
|
||||
import {
|
||||
helpers,
|
||||
PROTECTED_EXTERNAL_COLUMNS,
|
||||
PROTECTED_INTERNAL_COLUMNS,
|
||||
} from "@budibase/shared-core"
|
||||
import { processString } from "@budibase/string-templates"
|
||||
import { isUserMetadataTable } from "../../api/controllers/row/utils"
|
||||
|
||||
export * from "./utils"
|
||||
export * from "./attachments"
|
||||
|
@ -53,9 +58,9 @@ export async function processAutoColumn(
|
|||
row: Row,
|
||||
opts?: AutoColumnProcessingOpts
|
||||
) {
|
||||
let noUser = !userId
|
||||
let isUserTable = table._id === InternalTables.USER_METADATA
|
||||
let now = new Date().toISOString()
|
||||
const noUser = !userId
|
||||
const isUserTable = table._id === InternalTables.USER_METADATA
|
||||
const now = new Date().toISOString()
|
||||
// if a row doesn't have a revision then it doesn't exist yet
|
||||
const creating = !row._rev
|
||||
// check its not user table, or whether any of the processing options have been disabled
|
||||
|
@ -111,7 +116,7 @@ async function processDefaultValues(table: Table, row: Row) {
|
|||
ctx.user = user
|
||||
}
|
||||
|
||||
for (let [key, schema] of Object.entries(table.schema)) {
|
||||
for (const [key, schema] of Object.entries(table.schema)) {
|
||||
if ("default" in schema && schema.default != null && row[key] == null) {
|
||||
const processed = await processString(schema.default, ctx)
|
||||
|
||||
|
@ -165,10 +170,10 @@ export async function inputProcessing(
|
|||
row: Row,
|
||||
opts?: AutoColumnProcessingOpts
|
||||
) {
|
||||
let clonedRow = cloneDeep(row)
|
||||
const clonedRow = cloneDeep(row)
|
||||
|
||||
const dontCleanseKeys = ["type", "_id", "_rev", "tableId"]
|
||||
for (let [key, value] of Object.entries(clonedRow)) {
|
||||
for (const [key, value] of Object.entries(clonedRow)) {
|
||||
const field = table.schema[key]
|
||||
// cleanse fields that aren't in the schema
|
||||
if (!field) {
|
||||
|
@ -268,13 +273,13 @@ export async function outputProcessing<T extends Row[] | Row>(
|
|||
}
|
||||
|
||||
// process complex types: attachments, bb references...
|
||||
for (let [property, column] of Object.entries(table.schema)) {
|
||||
for (const [property, column] of Object.entries(table.schema)) {
|
||||
if (
|
||||
column.type === FieldType.ATTACHMENTS ||
|
||||
column.type === FieldType.ATTACHMENT_SINGLE ||
|
||||
column.type === FieldType.SIGNATURE_SINGLE
|
||||
) {
|
||||
for (let row of enriched) {
|
||||
for (const row of enriched) {
|
||||
if (row[property] == null) {
|
||||
continue
|
||||
}
|
||||
|
@ -299,7 +304,7 @@ export async function outputProcessing<T extends Row[] | Row>(
|
|||
!opts.skipBBReferences &&
|
||||
column.type == FieldType.BB_REFERENCE
|
||||
) {
|
||||
for (let row of enriched) {
|
||||
for (const row of enriched) {
|
||||
row[property] = await processOutputBBReferences(
|
||||
row[property],
|
||||
column.subtype
|
||||
|
@ -309,14 +314,14 @@ export async function outputProcessing<T extends Row[] | Row>(
|
|||
!opts.skipBBReferences &&
|
||||
column.type == FieldType.BB_REFERENCE_SINGLE
|
||||
) {
|
||||
for (let row of enriched) {
|
||||
for (const row of enriched) {
|
||||
row[property] = await processOutputBBReference(
|
||||
row[property],
|
||||
column.subtype
|
||||
)
|
||||
}
|
||||
} else if (column.type === FieldType.DATETIME && column.timeOnly) {
|
||||
for (let row of enriched) {
|
||||
for (const row of enriched) {
|
||||
if (row[property] instanceof Date) {
|
||||
const hours = row[property].getUTCHours().toString().padStart(2, "0")
|
||||
const minutes = row[property]
|
||||
|
@ -343,14 +348,36 @@ export async function outputProcessing<T extends Row[] | Row>(
|
|||
)) as Row[]
|
||||
}
|
||||
// remove null properties to match internal API
|
||||
if (isExternalTableID(table._id!)) {
|
||||
for (let row of enriched) {
|
||||
for (let key of Object.keys(row)) {
|
||||
const isExternal = isExternalTableID(table._id!)
|
||||
if (isExternal) {
|
||||
for (const row of enriched) {
|
||||
for (const key of Object.keys(row)) {
|
||||
if (row[key] === null) {
|
||||
delete row[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUserMetadataTable(table._id!)) {
|
||||
const protectedColumns = isExternal
|
||||
? PROTECTED_EXTERNAL_COLUMNS
|
||||
: PROTECTED_INTERNAL_COLUMNS
|
||||
|
||||
const tableFields = Object.keys(table.schema).filter(
|
||||
f => table.schema[f].visible !== false
|
||||
)
|
||||
const fields = [...tableFields, ...protectedColumns].map(f =>
|
||||
f.toLowerCase()
|
||||
)
|
||||
for (const row of enriched) {
|
||||
for (const key of Object.keys(row)) {
|
||||
if (!fields.includes(key.toLowerCase())) {
|
||||
delete row[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (wasArray ? enriched : enriched[0]) as T
|
||||
}
|
||||
|
|
437
yarn.lock
437
yarn.lock
|
@ -4208,160 +4208,160 @@
|
|||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27"
|
||||
integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==
|
||||
|
||||
"@rollup/rollup-android-arm-eabi@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz#7746deb85e4a8fb54fbfda8ac5c102692f102476"
|
||||
integrity sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==
|
||||
"@rollup/rollup-android-arm-eabi@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.0.tgz#d941173f82f9b041c61b0dc1a2a91dcd06e4b31e"
|
||||
integrity sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203"
|
||||
integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==
|
||||
|
||||
"@rollup/rollup-android-arm64@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz#93de4d867709d3313794723b5afd91e1e174f906"
|
||||
integrity sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==
|
||||
"@rollup/rollup-android-arm64@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.0.tgz#7e7157c8543215245ceffc445134d9e843ba51c0"
|
||||
integrity sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096"
|
||||
integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==
|
||||
|
||||
"@rollup/rollup-darwin-arm64@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz#e41e6a81673260ab196e0f59462b9940a6ac03cd"
|
||||
integrity sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==
|
||||
"@rollup/rollup-darwin-arm64@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.0.tgz#f0a18a4fc8dc6eb1e94a51fa2adb22876f477947"
|
||||
integrity sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c"
|
||||
integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==
|
||||
|
||||
"@rollup/rollup-darwin-x64@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz#2b0a0aef6e8c5317d494cfc9076d7a16b099bdcb"
|
||||
integrity sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==
|
||||
"@rollup/rollup-darwin-x64@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.0.tgz#34b7867613e5cc42d2b85ddc0424228cc33b43f0"
|
||||
integrity sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8"
|
||||
integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz#e22319deb5367384ef315e66bc6de80d2bf2b3ae"
|
||||
integrity sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==
|
||||
"@rollup/rollup-linux-arm-gnueabihf@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.0.tgz#422b19ff9ae02b05d3395183d1d43b38c7c8be0b"
|
||||
integrity sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549"
|
||||
integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz#d5dd68f5d7ae21b345a5c87208c94e5c813f54b8"
|
||||
integrity sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==
|
||||
"@rollup/rollup-linux-arm-musleabihf@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.0.tgz#568aa29195ef6fc57ec6ed3f518923764406a8ee"
|
||||
integrity sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577"
|
||||
integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz#1703d3a418d33f8f025acaf93f39ca1efcd5b645"
|
||||
integrity sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==
|
||||
"@rollup/rollup-linux-arm64-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.0.tgz#22309c8bcba9a73114f69165c72bc94b2fbec085"
|
||||
integrity sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c"
|
||||
integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz#3f59c2c6e60f75ce8b1090bd841c555e3bb01f0e"
|
||||
integrity sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==
|
||||
"@rollup/rollup-linux-arm64-musl@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.0.tgz#c93c388af6d33f082894b8a60839d7265b2b9bc5"
|
||||
integrity sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf"
|
||||
integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz#3f99a0921596a6f539121a312df29af52a205f15"
|
||||
integrity sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==
|
||||
"@rollup/rollup-linux-powerpc64le-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.0.tgz#493c5e19e395cf3c6bd860c7139c8a903dea72b4"
|
||||
integrity sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9"
|
||||
integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz#c08fb3e629d50d2eac31329347cfc559a1cf81d1"
|
||||
integrity sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==
|
||||
"@rollup/rollup-linux-riscv64-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.0.tgz#a2eab4346fbe5909165ce99adb935ba30c9fb444"
|
||||
integrity sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec"
|
||||
integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz#173722cd745779d730d4b24d21386185e0e12de8"
|
||||
integrity sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==
|
||||
"@rollup/rollup-linux-s390x-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.0.tgz#0bc49a79db4345d78d757bb1b05e73a1b42fa5c3"
|
||||
integrity sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942"
|
||||
integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz#0af2b6541ab0f4954d2c4f96bcdc7947420dd28c"
|
||||
integrity sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==
|
||||
"@rollup/rollup-linux-x64-gnu@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.0.tgz#4fd36a6a41f3406d8693321b13d4f9b7658dd4b9"
|
||||
integrity sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d"
|
||||
integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==
|
||||
|
||||
"@rollup/rollup-linux-x64-musl@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz#f973f9552744764b221128f7c3629222216ace69"
|
||||
integrity sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==
|
||||
"@rollup/rollup-linux-x64-musl@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.0.tgz#10ebb13bd4469cbad1a5d9b073bd27ec8a886200"
|
||||
integrity sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf"
|
||||
integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz#21ac5ed84d914bc31821fec3dd909f7257cfb17b"
|
||||
integrity sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==
|
||||
"@rollup/rollup-win32-arm64-msvc@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.0.tgz#2fef1a90f1402258ef915ae5a94cc91a5a1d5bfc"
|
||||
integrity sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54"
|
||||
integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz#0cfe740063b35dcd5a62c4e243226631a846ce11"
|
||||
integrity sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==
|
||||
"@rollup/rollup-win32-ia32-msvc@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.0.tgz#a18ad47a95c5f264defb60acdd8c27569f816fc1"
|
||||
integrity sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.18.0":
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4"
|
||||
integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc@4.19.1":
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz#5f2c40d3f1b53ede80fb4e6964f840c0f8936832"
|
||||
integrity sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==
|
||||
"@rollup/rollup-win32-x64-msvc@4.21.0":
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.0.tgz#20c09cf44dcb082140cc7f439dd679fe4bba3375"
|
||||
integrity sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==
|
||||
|
||||
"@roxi/routify@2.18.0":
|
||||
version "2.18.0"
|
||||
|
@ -5127,6 +5127,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@spectrum-css/tabs/-/tabs-3.2.12.tgz#9b08f23d5aa881b3441af7757800c7173e5685ff"
|
||||
integrity sha512-rPFUW9SSW4+3/UJ3UrtY2/l3sQvlqB1fqxHLPDjgykvbfrnMejcCTNV4ZrFNHXpE/6+kGnk+yVViSPtWGwJzkA==
|
||||
|
||||
"@spectrum-css/tag@3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/tag/-/tag-3.0.0.tgz#b2e335dc526713b83f3e995e8d1d4fc84a3fc4df"
|
||||
integrity sha512-a9z7ZTAWPonkWRNY5kxVaO6bxu9de3qUZWJ9Bl1YBlwWc8Fy1L7XqT4Wq3pW+4sktUbUUqqPYPIXK9xEFDofEw==
|
||||
|
||||
"@spectrum-css/tags@3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@spectrum-css/tags/-/tags-3.0.2.tgz#5bf35fb79c97cd9344de485bd4626ad5b9f07757"
|
||||
|
@ -5874,11 +5879,11 @@
|
|||
integrity sha512-7GgtHCs/QZrBrDzgIJnQtuSvhFSwhyYSI2uafSwZoNt1iOGhEN5fwNrQMjtONyHm9+/LoA4453jH0CMYcr06Pg==
|
||||
|
||||
"@types/node@>=8.1.0":
|
||||
version "22.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.0.tgz#04862a2a71e62264426083abe1e27e87cac05a30"
|
||||
integrity sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==
|
||||
version "22.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.4.2.tgz#55fefb1c3dba2ecd7eb76738c6b80da75760523f"
|
||||
integrity sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==
|
||||
dependencies:
|
||||
undici-types "~6.11.1"
|
||||
undici-types "~6.19.2"
|
||||
|
||||
"@types/node@^18.11.18":
|
||||
version "18.19.10"
|
||||
|
@ -6878,39 +6883,34 @@ acorn-import-assertions@^1.9.0:
|
|||
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac"
|
||||
integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==
|
||||
|
||||
acorn-jsx-walk@2.0.0:
|
||||
acorn-jsx-walk@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx-walk/-/acorn-jsx-walk-2.0.0.tgz#a5ed648264e68282d7c2aead80216bfdf232573a"
|
||||
integrity sha512-uuo6iJj4D4ygkdzd6jPtcxs8vZgDX9YFIkqczGImoypX2fQ4dVImmu3UzA4ynixCIMTrEOWW+95M2HuBaCEOVA==
|
||||
|
||||
acorn-jsx@5.3.2, acorn-jsx@^5.3.2:
|
||||
acorn-jsx@^5.3.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn-loose@8.4.0:
|
||||
acorn-loose@^8.4.0:
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-loose/-/acorn-loose-8.4.0.tgz#26d3e219756d1e180d006f5bcc8d261a28530f55"
|
||||
integrity sha512-M0EUka6rb+QC4l9Z3T0nJEzNOO7JcoJlYMrBlyBCiFSXRyxjLKayd4TbQs2FDRWQU1h9FR7QVNHt+PEaoNL5rQ==
|
||||
dependencies:
|
||||
acorn "^8.11.0"
|
||||
|
||||
acorn-walk@8.3.3, acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0:
|
||||
version "8.3.3"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
|
||||
integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
|
||||
dependencies:
|
||||
acorn "^8.11.0"
|
||||
|
||||
acorn-walk@^7.1.1:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||
|
||||
acorn@8.12.1, acorn@^8.12.0:
|
||||
version "8.12.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||
acorn-walk@^8.0.2, acorn-walk@^8.1.1, acorn-walk@^8.2.0, acorn-walk@^8.3.3:
|
||||
version "8.3.3"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e"
|
||||
integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==
|
||||
dependencies:
|
||||
acorn "^8.11.0"
|
||||
|
||||
acorn@^5.2.1, acorn@^5.7.3:
|
||||
version "5.7.4"
|
||||
|
@ -6927,6 +6927,11 @@ acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.2.4, acorn@^
|
|||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c"
|
||||
integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==
|
||||
|
||||
acorn@^8.12.0, acorn@^8.12.1:
|
||||
version "8.12.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248"
|
||||
integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==
|
||||
|
||||
add-stream@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
|
||||
|
@ -6979,16 +6984,6 @@ ajv-formats@^2.0.2:
|
|||
dependencies:
|
||||
ajv "^8.0.0"
|
||||
|
||||
ajv@8.17.1:
|
||||
version "8.17.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
|
||||
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.3"
|
||||
fast-uri "^3.0.1"
|
||||
json-schema-traverse "^1.0.0"
|
||||
require-from-string "^2.0.2"
|
||||
|
||||
ajv@^6.12.3, ajv@^6.12.4:
|
||||
version "6.12.6"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
|
||||
|
@ -7009,6 +7004,16 @@ ajv@^8.0.0, ajv@^8.1.0, ajv@^8.4.0:
|
|||
require-from-string "^2.0.2"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
ajv@^8.17.1:
|
||||
version "8.17.1"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6"
|
||||
integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
|
||||
dependencies:
|
||||
fast-deep-equal "^3.1.3"
|
||||
fast-uri "^3.0.1"
|
||||
json-schema-traverse "^1.0.0"
|
||||
require-from-string "^2.0.2"
|
||||
|
||||
amdefine@>=0.0.4:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
||||
|
@ -8690,11 +8695,6 @@ combos@^0.2.0:
|
|||
resolved "https://registry.yarnpkg.com/combos/-/combos-0.2.0.tgz#dc31c5a899b42293d55fe19c064d3e6e207ba4f7"
|
||||
integrity sha512-Z6YfvgiTCERWJTj3wQiXamFhssdvz1n4ok447rS330lw3uL72WAx8IvrLU7xiE71uyb5WF8JEP+BWB5KhOoGeg==
|
||||
|
||||
commander@12.1.0:
|
||||
version "12.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
|
||||
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
|
||||
|
||||
commander@6.2.0:
|
||||
version "6.2.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75"
|
||||
|
@ -8710,6 +8710,11 @@ commander@^11.0.0:
|
|||
resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906"
|
||||
integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==
|
||||
|
||||
commander@^12.1.0:
|
||||
version "12.1.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
|
||||
integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
|
||||
|
||||
commander@^2.16.0, commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.5.0, commander@^2.7.1, commander@^2.8.1:
|
||||
version "2.20.3"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
|
||||
|
@ -9440,9 +9445,9 @@ dayjs@^1.10.8:
|
|||
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
|
||||
|
||||
dayjs@^1.8.15:
|
||||
version "1.11.12"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d"
|
||||
integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==
|
||||
version "1.11.13"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
|
||||
dc-polyfill@^0.1.2:
|
||||
version "0.1.3"
|
||||
|
@ -9780,32 +9785,32 @@ depd@^1.1.0, depd@~1.1.2:
|
|||
integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
|
||||
|
||||
dependency-cruiser@^16.3.7:
|
||||
version "16.3.10"
|
||||
resolved "https://registry.yarnpkg.com/dependency-cruiser/-/dependency-cruiser-16.3.10.tgz#fe26a50d5e10a4496bc2b70d027fca6ded48814f"
|
||||
integrity sha512-WkCnibHBfvaiaQ+S46LZ6h4AR6oj42Vsf5/0Vgtrwdwn7ZekMJdZ/ALoTwNp/RaGlKW+MbV/fhSZOvmhAWVWzQ==
|
||||
version "16.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dependency-cruiser/-/dependency-cruiser-16.4.0.tgz#a1b7d452acddf05045ae4f7942a2e9337aedad35"
|
||||
integrity sha512-la/NnD23m6esCox8KMiZ/pcmtec6G/r7LgnJvkBepcErdzlGaxWnyaxtpoYB3fgODrU/7E2u81/nX5FNu5zfyw==
|
||||
dependencies:
|
||||
acorn "8.12.1"
|
||||
acorn-jsx "5.3.2"
|
||||
acorn-jsx-walk "2.0.0"
|
||||
acorn-loose "8.4.0"
|
||||
acorn-walk "8.3.3"
|
||||
ajv "8.17.1"
|
||||
commander "12.1.0"
|
||||
enhanced-resolve "5.17.1"
|
||||
ignore "5.3.1"
|
||||
acorn "^8.12.1"
|
||||
acorn-jsx "^5.3.2"
|
||||
acorn-jsx-walk "^2.0.0"
|
||||
acorn-loose "^8.4.0"
|
||||
acorn-walk "^8.3.3"
|
||||
ajv "^8.17.1"
|
||||
commander "^12.1.0"
|
||||
enhanced-resolve "^5.17.1"
|
||||
ignore "^5.3.2"
|
||||
interpret "^3.1.1"
|
||||
is-installed-globally "1.0.0"
|
||||
json5 "2.2.3"
|
||||
memoize "10.0.0"
|
||||
picocolors "1.0.1"
|
||||
picomatch "4.0.2"
|
||||
prompts "2.4.2"
|
||||
is-installed-globally "^1.0.0"
|
||||
json5 "^2.2.3"
|
||||
memoize "^10.0.0"
|
||||
picocolors "^1.0.1"
|
||||
picomatch "^4.0.2"
|
||||
prompts "^2.4.2"
|
||||
rechoir "^0.8.0"
|
||||
safe-regex "2.1.1"
|
||||
safe-regex "^2.1.1"
|
||||
semver "^7.6.3"
|
||||
teamcity-service-messages "0.1.14"
|
||||
tsconfig-paths-webpack-plugin "4.1.0"
|
||||
watskeburt "4.1.0"
|
||||
teamcity-service-messages "^0.1.14"
|
||||
tsconfig-paths-webpack-plugin "^4.1.0"
|
||||
watskeburt "^4.1.0"
|
||||
|
||||
dependency-tree@^9.0.0:
|
||||
version "9.0.0"
|
||||
|
@ -10358,23 +10363,10 @@ electron-to-chromium@^1.4.284:
|
|||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.317.tgz#9a3d38a1a37f26a417d3d95dafe198ff11ed072b"
|
||||
integrity sha512-JhCRm9v30FMNzQSsjl4kXaygU+qHBD0Yh7mKxyjmF0V8VwYVB6qpBRX28GyAucrM9wDCpSUctT6FpMUQxbyKuA==
|
||||
|
||||
elliptic@^6.5.3:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||
integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
|
||||
dependencies:
|
||||
bn.js "^4.11.9"
|
||||
brorand "^1.1.0"
|
||||
hash.js "^1.0.0"
|
||||
hmac-drbg "^1.0.1"
|
||||
inherits "^2.0.4"
|
||||
minimalistic-assert "^1.0.1"
|
||||
minimalistic-crypto-utils "^1.0.1"
|
||||
|
||||
elliptic@^6.5.5:
|
||||
version "6.5.5"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded"
|
||||
integrity sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==
|
||||
elliptic@^6.5.3, elliptic@^6.5.5:
|
||||
version "6.5.7"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b"
|
||||
integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==
|
||||
dependencies:
|
||||
bn.js "^4.11.9"
|
||||
brorand "^1.1.0"
|
||||
|
@ -10477,7 +10469,7 @@ engine.io@~6.5.2:
|
|||
engine.io-parser "~5.2.1"
|
||||
ws "~8.17.1"
|
||||
|
||||
enhanced-resolve@5.17.1, enhanced-resolve@^5.7.0:
|
||||
enhanced-resolve@^5.17.1, enhanced-resolve@^5.7.0:
|
||||
version "5.17.1"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15"
|
||||
integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==
|
||||
|
@ -12852,9 +12844,9 @@ husky@^8.0.3:
|
|||
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
|
||||
|
||||
husky@^9.1.4:
|
||||
version "9.1.4"
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.4.tgz#926fd19c18d345add5eab0a42b2b6d9a80259b34"
|
||||
integrity sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==
|
||||
version "9.1.5"
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.5.tgz#2b6edede53ee1adbbd3a3da490628a23f5243b83"
|
||||
integrity sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==
|
||||
|
||||
ical-generator@4.1.0:
|
||||
version "4.1.0"
|
||||
|
@ -12928,17 +12920,12 @@ ignore-walk@^6.0.0:
|
|||
dependencies:
|
||||
minimatch "^7.4.2"
|
||||
|
||||
ignore@5.3.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
|
||||
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
|
||||
|
||||
ignore@^5.0.4, ignore@^5.2.0, ignore@^5.2.4:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
|
||||
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
|
||||
|
||||
ignore@^5.3.1:
|
||||
ignore@^5.3.1, ignore@^5.3.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||
|
@ -13389,14 +13376,6 @@ is-immutable-type@^4.0.0:
|
|||
ts-api-utils "^1.3.0"
|
||||
ts-declaration-location "^1.0.0"
|
||||
|
||||
is-installed-globally@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz#08952c43758c33d815692392f7f8437b9e436d5a"
|
||||
integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==
|
||||
dependencies:
|
||||
global-directory "^4.0.1"
|
||||
is-path-inside "^4.0.0"
|
||||
|
||||
is-installed-globally@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
|
||||
|
@ -13405,6 +13384,14 @@ is-installed-globally@^0.4.0:
|
|||
global-dirs "^3.0.0"
|
||||
is-path-inside "^3.0.2"
|
||||
|
||||
is-installed-globally@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-1.0.0.tgz#08952c43758c33d815692392f7f8437b9e436d5a"
|
||||
integrity sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==
|
||||
dependencies:
|
||||
global-directory "^4.0.1"
|
||||
is-path-inside "^4.0.0"
|
||||
|
||||
is-interactive@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
|
||||
|
@ -14516,11 +14503,6 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
|
|||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
|
||||
|
||||
json5@2.2.3, json5@^2.2.1, json5@^2.2.2, json5@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
json5@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
|
||||
|
@ -14528,6 +14510,11 @@ json5@^1.0.2:
|
|||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
json5@^2.2.1, json5@^2.2.2, json5@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
jsonc-parser@3.2.0, jsonc-parser@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
|
||||
|
@ -15872,7 +15859,7 @@ memdown@^5.1.0:
|
|||
ltgt "~2.2.0"
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
memoize@10.0.0:
|
||||
memoize@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/memoize/-/memoize-10.0.0.tgz#43fa66b2022363c7c50cf5dfab732a808a3d7147"
|
||||
integrity sha512-H6cBLgsi6vMWOcCpvVCdFFnl3kerEXbrYh9q+lY6VXvQSmM6CkmV08VOwT+WE2tzIEqRPFfAq3fm4v/UIW6mSA==
|
||||
|
@ -17860,26 +17847,26 @@ phin@^2.9.1:
|
|||
resolved "https://registry.yarnpkg.com/phin/-/phin-2.9.3.tgz#f9b6ac10a035636fb65dfc576aaaa17b8743125c"
|
||||
integrity sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==
|
||||
|
||||
picocolors@1.0.1, picocolors@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
|
||||
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
picomatch@4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
|
||||
integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
|
||||
picocolors@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1"
|
||||
integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
picomatch@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
|
||||
integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
|
||||
|
||||
pify@5.0.0, pify@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f"
|
||||
|
@ -18367,10 +18354,10 @@ postcss@^8.1.7, postcss@^8.2.9, postcss@^8.3.11, postcss@^8.4.12, postcss@^8.4.2
|
|||
picocolors "^1.0.0"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
postcss@^8.4.39:
|
||||
version "8.4.40"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8"
|
||||
integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q==
|
||||
postcss@^8.4.41:
|
||||
version "8.4.41"
|
||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.41.tgz#d6104d3ba272d882fe18fc07d15dc2da62fa2681"
|
||||
integrity sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==
|
||||
dependencies:
|
||||
nanoid "^3.3.7"
|
||||
picocolors "^1.0.1"
|
||||
|
@ -18407,9 +18394,9 @@ posthog-js@^1.118.0:
|
|||
preact "^10.19.3"
|
||||
|
||||
posthog-js@^1.13.4:
|
||||
version "1.150.1"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.150.1.tgz#ce2e0aa0dc30369bf1b1b9a38b9fbf25e5c01ba0"
|
||||
integrity sha512-jHSnqtAWkUQkiedQgHpD00+z8RUF0loDq7ORakBKfQjdntTJIEk16ewqTNRxnpE86guWDoy2J3iAqLgAYfFaLA==
|
||||
version "1.157.2"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.157.2.tgz#dc2515818ead408aefb900e90c535fb57beb1f59"
|
||||
integrity sha512-ATYKGs+Q51u26nHHhrhWNh1whqFm7j/rwQQYw+y6/YzNmRlo+YsqrGZji9nqXb9/4fo0ModDr+ZmuOI3hKkUXA==
|
||||
dependencies:
|
||||
fflate "^0.4.8"
|
||||
preact "^10.19.3"
|
||||
|
@ -18837,7 +18824,7 @@ promise.series@^0.2.0:
|
|||
resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd"
|
||||
integrity sha512-VWQJyU2bcDTgZw8kpfBpB/ejZASlCrzwz5f2hjb/zlujOEB4oeiAhHygAWq8ubsX2GVkD4kCU5V2dwOTaCY5EQ==
|
||||
|
||||
prompts@2.4.2, prompts@^2.0.1:
|
||||
prompts@^2.0.1, prompts@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
|
||||
integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
|
||||
|
@ -19003,9 +18990,9 @@ q@^1.1.2:
|
|||
integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
|
||||
|
||||
qs@^6.10.3:
|
||||
version "6.12.3"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.12.3.tgz#e43ce03c8521b9c7fd7f1f13e514e5ca37727754"
|
||||
integrity sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
|
||||
dependencies:
|
||||
side-channel "^1.0.6"
|
||||
|
||||
|
@ -19661,9 +19648,9 @@ rimraf@^4.4.1:
|
|||
glob "^9.2.0"
|
||||
|
||||
rimraf@^5.0.7:
|
||||
version "5.0.9"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.9.tgz#c3baa1b886eadc2ec7981a06a593c3d01134ffe9"
|
||||
integrity sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==
|
||||
version "5.0.10"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.10.tgz#23b9843d3dc92db71f96e1a2ce92e39fd2a8221c"
|
||||
integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==
|
||||
dependencies:
|
||||
glob "^10.3.7"
|
||||
|
||||
|
@ -19829,29 +19816,29 @@ rollup@^3.27.1:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rollup@^4.13.0:
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.19.1.tgz#21d865cd60d4a325172ce8b082e60caccd97b309"
|
||||
integrity sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==
|
||||
rollup@^4.20.0:
|
||||
version "4.21.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.21.0.tgz#28db5f5c556a5180361d35009979ccc749560b9d"
|
||||
integrity sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==
|
||||
dependencies:
|
||||
"@types/estree" "1.0.5"
|
||||
optionalDependencies:
|
||||
"@rollup/rollup-android-arm-eabi" "4.19.1"
|
||||
"@rollup/rollup-android-arm64" "4.19.1"
|
||||
"@rollup/rollup-darwin-arm64" "4.19.1"
|
||||
"@rollup/rollup-darwin-x64" "4.19.1"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.19.1"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.19.1"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.19.1"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.19.1"
|
||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.19.1"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.19.1"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.19.1"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.19.1"
|
||||
"@rollup/rollup-linux-x64-musl" "4.19.1"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.19.1"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.19.1"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.19.1"
|
||||
"@rollup/rollup-android-arm-eabi" "4.21.0"
|
||||
"@rollup/rollup-android-arm64" "4.21.0"
|
||||
"@rollup/rollup-darwin-arm64" "4.21.0"
|
||||
"@rollup/rollup-darwin-x64" "4.21.0"
|
||||
"@rollup/rollup-linux-arm-gnueabihf" "4.21.0"
|
||||
"@rollup/rollup-linux-arm-musleabihf" "4.21.0"
|
||||
"@rollup/rollup-linux-arm64-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-arm64-musl" "4.21.0"
|
||||
"@rollup/rollup-linux-powerpc64le-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-riscv64-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-s390x-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-x64-gnu" "4.21.0"
|
||||
"@rollup/rollup-linux-x64-musl" "4.21.0"
|
||||
"@rollup/rollup-win32-arm64-msvc" "4.21.0"
|
||||
"@rollup/rollup-win32-ia32-msvc" "4.21.0"
|
||||
"@rollup/rollup-win32-x64-msvc" "4.21.0"
|
||||
fsevents "~2.3.2"
|
||||
|
||||
rollup@^4.9.4, rollup@^4.9.6:
|
||||
|
@ -19954,7 +19941,7 @@ safe-regex-test@^1.0.3:
|
|||
es-errors "^1.3.0"
|
||||
is-regex "^1.1.4"
|
||||
|
||||
safe-regex@2.1.1:
|
||||
safe-regex@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2"
|
||||
integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==
|
||||
|
@ -21345,7 +21332,7 @@ tarn@^3.0.1, tarn@^3.0.2:
|
|||
resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693"
|
||||
integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==
|
||||
|
||||
teamcity-service-messages@0.1.14:
|
||||
teamcity-service-messages@^0.1.14:
|
||||
version "0.1.14"
|
||||
resolved "https://registry.yarnpkg.com/teamcity-service-messages/-/teamcity-service-messages-0.1.14.tgz#193d420a5e4aef8e5e50b8c39e7865e08fbb5d8a"
|
||||
integrity sha512-29aQwaHqm8RMX74u2o/h1KbMLP89FjNiMxD9wbF2BbWOnbM+q+d1sCEC+MqCc4QW3NJykn77OMpTFw/xTHIc0w==
|
||||
|
@ -21557,9 +21544,9 @@ tinypool@^0.4.0:
|
|||
integrity sha512-2ksntHOKf893wSAH4z/+JbPpi92esw8Gn9N2deXX+B0EO92hexAVI9GIZZPx7P5aYo5KULfeOSt3kMOmSOy6uA==
|
||||
|
||||
tinypool@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.0.tgz#a68965218e04f4ad9de037d2a1cd63cda9afb238"
|
||||
integrity sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-1.0.1.tgz#c64233c4fac4304e109a64340178760116dbe1fe"
|
||||
integrity sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==
|
||||
|
||||
tinyrainbow@^1.2.0:
|
||||
version "1.2.0"
|
||||
|
@ -21787,7 +21774,7 @@ tsconfck@^3.0.3:
|
|||
resolved "https://registry.yarnpkg.com/tsconfck/-/tsconfck-3.1.1.tgz#c7284913262c293b43b905b8b034f524de4a3162"
|
||||
integrity sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==
|
||||
|
||||
tsconfig-paths-webpack-plugin@4.1.0:
|
||||
tsconfig-paths-webpack-plugin@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz#3c6892c5e7319c146eee1e7302ed9e6f2be4f763"
|
||||
integrity sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==
|
||||
|
@ -22102,10 +22089,10 @@ undici-types@~5.26.4:
|
|||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
undici-types@~6.11.1:
|
||||
version "6.11.1"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197"
|
||||
integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==
|
||||
undici-types@~6.19.2:
|
||||
version "6.19.8"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
|
||||
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
|
||||
|
||||
undici@^4.14.1:
|
||||
version "4.16.0"
|
||||
|
@ -22469,13 +22456,13 @@ vite-tsconfig-paths@^4.3.2:
|
|||
fsevents "~2.3.2"
|
||||
|
||||
vite@^5.0.0:
|
||||
version "5.3.5"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.5.tgz#b847f846fb2b6cb6f6f4ed50a830186138cb83d8"
|
||||
integrity sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA==
|
||||
version "5.4.2"
|
||||
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.2.tgz#8acb6ec4bfab823cdfc1cb2d6c53ed311bc4e47e"
|
||||
integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
|
||||
dependencies:
|
||||
esbuild "^0.21.3"
|
||||
postcss "^8.4.39"
|
||||
rollup "^4.13.0"
|
||||
postcss "^8.4.41"
|
||||
rollup "^4.20.0"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
|
@ -22587,7 +22574,7 @@ walker@^1.0.8:
|
|||
dependencies:
|
||||
makeerror "1.0.12"
|
||||
|
||||
watskeburt@4.1.0:
|
||||
watskeburt@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/watskeburt/-/watskeburt-4.1.0.tgz#3c0227669be646a97424b631164b1afe3d4d5344"
|
||||
integrity sha512-KkY5H51ajqy9HYYI+u9SIURcWnqeVVhdH0I+ab6aXPGHfZYxgRCwnR6Lm3+TYB6jJVt5jFqw4GAKmwf1zHmGQw==
|
||||
|
@ -22610,9 +22597,9 @@ web-streams-polyfill@^3.2.1:
|
|||
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
||||
|
||||
web-vitals@^4.0.1:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.2.tgz#e883245180b95e175eb75a5ca8903b1a11597d7a"
|
||||
integrity sha512-nYfoOqb4EmElljyXU2qdeE76KsvoHdftQKY4DzA9Aw8DervCg2bG634pHLrJ/d6+B4mE3nWTSJv8Mo7B2mbZkw==
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.3.tgz#270c4baecfbc6ec6fc15da1989e465e5f9b94fb7"
|
||||
integrity sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==
|
||||
|
||||
webfinger@^0.4.2:
|
||||
version "0.4.2"
|
||||
|
|
Loading…
Reference in New Issue