Merge branch 'master' into feature/filter-component

This commit is contained in:
deanhannigan 2025-05-06 10:51:01 +01:00 committed by GitHub
commit 380562a1dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 135 additions and 97 deletions

View File

@ -11,7 +11,7 @@
export let hoverColor: string | undefined = undefined
export let tooltip: string | undefined = undefined
export let tooltipPosition: TooltipPosition = TooltipPosition.Bottom
export let tooltipType = TooltipType.Default
export let tooltipType: TooltipType = TooltipType.Default
export let tooltipColor: string | undefined = undefined
export let tooltipWrap: boolean = true
export let newStyles: boolean = false

View File

@ -1,7 +1,7 @@
<script lang="ts">
export let horizontal: boolean = false
export let paddingX: "S" | "M" | "L" | "XL" | "XXL" = "M"
export let paddingY: "S" | "M" | "L" | "XL" | "XXL" = "M"
export let paddingY: "none" | "S" | "M" | "L" | "XL" | "XXL" = "M"
export let noPadding: boolean = false
export let gap: "XXS" | "XS" | "S" | "M" | "L" | "XL" = "M"
export let noGap: boolean = false

View File

@ -1,9 +1,9 @@
import { ActionMenu } from "./types"
import { ModalContext } from "./types"
import { ActionMenu, ModalContext, ScrollContext } from "./types"
declare module "svelte" {
export function getContext(key: "actionMenu"): ActionMenu | undefined
export function getContext(key: "bbui-modal"): ModalContext
export function getContext(key: "scroll"): ScrollContext
}
export const Modal = "bbui-modal"

View File

@ -1,3 +1,4 @@
export * from "./actionMenu"
export * from "./envDropdown"
export * from "./modalContext"
export * from "./scrollContext"

View File

@ -0,0 +1,3 @@
export interface ScrollContext {
scrollTo: (bounds: DOMRect) => void
}

View File

@ -1,15 +1,15 @@
<script>
<script lang="ts">
import { tick } from "svelte"
import { Icon, Body } from "@budibase/bbui"
import { keyUtils } from "@/helpers/keyUtils"
export let title
export let placeholder
export let value
export let onAdd
export let search
export let title: string
export let placeholder: string
export let value: string
export let onAdd: () => void
export let search: boolean
let searchInput
let searchInput: HTMLInputElement
const openSearch = async () => {
search = true
@ -22,7 +22,7 @@
value = ""
}
const onKeyDown = e => {
const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") {
closeSearch()
}

View File

@ -1,46 +1,46 @@
<script>
<script lang="ts">
import { Icon, TooltipType, TooltipPosition } from "@budibase/bbui"
import { createEventDispatcher, getContext } from "svelte"
import { helpers } from "@budibase/shared-core"
import { UserAvatars } from "@budibase/frontend-core"
import type { UIUser } from "@budibase/types"
export let icon
export let iconTooltip
export let withArrow = false
export let withActions = true
export let showActions = false
export let indentLevel = 0
export let text
export let border = true
export let selected = false
export let opened = false
export let draggable = false
export let iconText
export let iconColor
export let scrollable = false
export let highlighted = false
export let rightAlignIcon = false
export let id
export let showTooltip = false
export let selectedBy = null
export let compact = false
export let hovering = false
export let disabled = false
export let icon: string | null
export let iconTooltip: string = ""
export let withArrow: boolean = false
export let withActions: boolean = true
export let showActions: boolean = false
export let indentLevel: number = 0
export let text: string
export let border: boolean = true
export let selected: boolean = false
export let opened: boolean = false
export let draggable: boolean = false
export let iconText: string = ""
export let iconColor: string = ""
export let scrollable: boolean = false
export let highlighted: boolean = false
export let rightAlignIcon: boolean = false
export let id: string = ""
export let showTooltip: boolean = false
export let selectedBy: UIUser[] | null = null
export let compact: boolean = false
export let hovering: boolean = false
export let disabled: boolean = false
const scrollApi = getContext("scroll")
const dispatch = createEventDispatcher()
let contentRef
let contentRef: HTMLDivElement
$: selected && contentRef && scrollToView()
$: style = getStyle(indentLevel, selectedBy)
$: style = getStyle(indentLevel)
const onClick = () => {
scrollToView()
dispatch("click")
}
const onIconClick = e => {
const onIconClick = (e: Event) => {
e.stopPropagation()
dispatch("iconClick")
}
@ -53,11 +53,8 @@
scrollApi.scrollTo(bounds)
}
const getStyle = (indentLevel, selectedBy) => {
const getStyle = (indentLevel: number) => {
let style = `padding-left:calc(${indentLevel * 14}px);`
if (selectedBy) {
style += `--selected-by-color:${helpers.getUserColor(selectedBy)};`
}
return style
}
</script>

View File

@ -1,25 +1,25 @@
<script>
<script lang="ts">
import { ModalContent, Input } from "@budibase/bbui"
import sanitizeUrl from "@/helpers/sanitizeUrl"
import { get } from "svelte/store"
import { screenStore } from "@/stores/builder"
export let onConfirm
export let onCancel
export let route
export let role
export let onConfirm: (_data: { route: string }) => Promise<void>
export let onCancel: (() => Promise<void>) | undefined = undefined
export let route: string
export let role: string | undefined
export let confirmText = "Continue"
const appPrefix = "/app"
let touched = false
let error
let modal
let error: string | undefined
let modal: ModalContent
$: appUrl = route
? `${window.location.origin}${appPrefix}${route}`
: `${window.location.origin}${appPrefix}`
const routeChanged = event => {
const routeChanged = (event: { detail: string }) => {
if (!event.detail.startsWith("/")) {
route = "/" + event.detail
}
@ -28,11 +28,11 @@
if (routeExists(route)) {
error = "This URL is already taken for this access role"
} else {
error = null
error = undefined
}
}
const routeExists = url => {
const routeExists = (url: string) => {
if (!role) {
return false
}
@ -58,7 +58,7 @@
onConfirm={confirmScreenDetails}
{onCancel}
cancelText={"Back"}
disabled={!route || error || !touched}
disabled={!route || !!error || !touched}
>
<form on:submit|preventDefault={() => modal.confirm()}>
<Input

View File

@ -11,7 +11,10 @@
$componentStore.selectedComponentId,
"RefreshDatasource",
{ includeSelf: nested }
)
).concat({
readableBinding: "All data providers",
runtimeBinding: "all",
})
</script>
<div class="root">

View File

@ -109,13 +109,12 @@
/>
{/each}
</List>
<div>
<Button secondary icon="Add" on:click={addOAuth2Configuration}
>Add OAuth2</Button
>
</div>
{/if}
<div>
<Button secondary icon="Add" on:click={addOAuth2Configuration}
>Add OAuth2</Button
>
</div>
</DetailPopover>
<style>

View File

@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { Modal, Helpers, notifications, Icon } from "@budibase/bbui"
import {
navigationStore,
@ -14,13 +14,14 @@
import { makeComponentUnique } from "@/helpers/components"
import { capitalise } from "@/helpers"
import ConfirmDialog from "@/components/common/ConfirmDialog.svelte"
import type { Screen } from "@budibase/types"
export let screen
let confirmDeleteDialog
let screenDetailsModal
let confirmDeleteDialog: ConfirmDialog
let screenDetailsModal: Modal
const createDuplicateScreen = async ({ route }) => {
const createDuplicateScreen = async ({ route }: { route: string }) => {
// Create a dupe and ensure it is unique
let duplicateScreen = Helpers.cloneDeep(screen)
delete duplicateScreen._id
@ -57,7 +58,7 @@
$: noPaste = !$componentStore.componentToPaste
const pasteComponent = mode => {
const pasteComponent = (mode: "inside") => {
try {
componentStore.paste(screen.props, mode, screen)
} catch (error) {
@ -65,7 +66,7 @@
}
}
const openContextMenu = (e, screen) => {
const openContextMenu = (e: MouseEvent, screen: Screen) => {
e.preventDefault()
e.stopPropagation()
@ -96,7 +97,7 @@
},
]
contextMenuStore.open(screen._id, items, { x: e.clientX, y: e.clientY })
contextMenuStore.open(screen._id!, items, { x: e.clientX, y: e.clientY })
}
</script>

View File

@ -1,16 +1,17 @@
<script>
<script lang="ts">
import { Layout } from "@budibase/bbui"
import { sortedScreens } from "@/stores/builder"
import ScreenNavItem from "./ScreenNavItem.svelte"
import { goto } from "@roxi/routify"
import { getVerticalResizeActions } from "@/components/common/resizable"
import NavHeader from "@/components/common/NavHeader.svelte"
import type { Screen } from "@budibase/types"
const [resizable, resizableHandle] = getVerticalResizeActions()
let searching = false
let searchValue = ""
let screensContainer
let screensContainer: HTMLDivElement
let scrolling = false
$: filteredScreens = getFilteredScreens($sortedScreens, searchValue)
@ -25,13 +26,13 @@
}
}
const getFilteredScreens = (screens, searchValue) => {
const getFilteredScreens = (screens: Screen[], searchValue: string) => {
return screens.filter(screen => {
return !searchValue || screen.routing.route.includes(searchValue)
})
}
const handleScroll = e => {
const handleScroll = (e: any) => {
scrolling = e.target.scrollTop !== 0
}
</script>
@ -62,7 +63,6 @@
<div
role="separator"
disabled={searching}
class="divider"
class:disabled={searching}
use:resizableHandle

View File

@ -6,9 +6,12 @@ interface Position {
}
interface MenuItem {
label: string
icon?: string
action: () => void
name: string
keyBind: string | null
visible: boolean
disabled: boolean
callback: () => void
}
interface ContextMenuState {

View File

@ -13,6 +13,7 @@ import {
UnsavedUser,
} from "@budibase/types"
import { BudiStore } from "../BudiStore"
import { notifications } from "@budibase/bbui"
interface UserInfo {
email: string
@ -43,6 +44,7 @@ class UserStore extends BudiStore<UserState> {
try {
return await API.getUser(userId)
} catch (err) {
notifications.error("Error fetching user")
return null
}
}

View File

@ -22,6 +22,7 @@
environmentStore,
sidePanelStore,
modalStore,
dataSourceStore,
} from "@/stores"
import NotificationDisplay from "./overlay/NotificationDisplay.svelte"
import ConfirmationDisplay from "./overlay/ConfirmationDisplay.svelte"
@ -47,11 +48,18 @@
import SnippetsProvider from "./context/SnippetsProvider.svelte"
import EmbedProvider from "./context/EmbedProvider.svelte"
import DNDSelectionIndicators from "./preview/DNDSelectionIndicators.svelte"
import { ActionTypes } from "@/constants"
// Provide contexts
const context = createContextStore()
setContext("sdk", SDK)
setContext("component", writable({ id: null, ancestors: [] }))
setContext("context", createContextStore())
setContext("context", context)
// Seed context with an action to refresh all datasources
context.actions.provideAction("all", ActionTypes.RefreshDatasource, () => {
dataSourceStore.actions.refreshAll()
})
let dataLoaded = false
let permissionError = false

View File

@ -42,13 +42,15 @@
const goToPortal = () => {
window.location.href = isBuilder ? "/builder/portal/apps" : "/builder/apps"
}
$: user = $authStore as User
</script>
{#if $authStore}
<ActionMenu align={compact ? "right" : "left"}>
<svelte:fragment slot="control">
<div class="container">
<UserAvatar user={$authStore} size="M" showTooltip={false} />
<UserAvatar {user} size="M" showTooltip={false} />
{#if !compact}
<div class="text">
<div class="name">

View File

@ -115,9 +115,18 @@ export const createDataSourceStore = () => {
})
}
const refreshAll = () => {
get(store).forEach(instance => instance.refresh())
}
return {
subscribe: store.subscribe,
actions: { registerDataSource, unregisterInstance, invalidateDataSource },
actions: {
registerDataSource,
unregisterInstance,
invalidateDataSource,
refreshAll,
},
}
}

View File

@ -1,16 +1,17 @@
<script>
<script lang="ts">
import { Avatar, AbsTooltip, TooltipPosition } from "@budibase/bbui"
import { helpers } from "@budibase/shared-core"
import type { User } from "@budibase/types"
export let user
export let size = "S"
export let tooltipPosition = TooltipPosition.Top
export let showTooltip = true
export let user: User
export let size: "XS" | "S" | "M" = "S"
export let tooltipPosition: TooltipPosition = TooltipPosition.Top
export let showTooltip: boolean = true
</script>
{#if user}
<AbsTooltip
text={showTooltip ? helpers.getUserLabel(user) : null}
text={showTooltip ? helpers.getUserLabel(user) : ""}
position={tooltipPosition}
color={helpers.getUserColor(user)}
>

View File

@ -1,27 +1,31 @@
<script>
<script lang="ts">
import { UserAvatar } from "@budibase/frontend-core"
import { TooltipPosition, Avatar } from "@budibase/bbui"
import type { UIUser } from "@budibase/types"
export let users = []
export let order = "ltr"
export let size = "S"
export let tooltipPosition = TooltipPosition.Top
type OrderType = "ltr" | "rtl"
$: uniqueUsers = unique(users, order)
export let users: UIUser[] = []
export let order: OrderType = "ltr"
export let size: "XS" | "S" = "S"
export let tooltipPosition: TooltipPosition = TooltipPosition.Top
$: uniqueUsers = unique(users)
$: avatars = getAvatars(uniqueUsers, order)
const unique = users => {
let uniqueUsers = {}
users?.forEach(user => {
const unique = (users: UIUser[]) => {
const uniqueUsers: Record<string, UIUser> = {}
users.forEach(user => {
uniqueUsers[user.email] = user
})
return Object.values(uniqueUsers)
}
const getAvatars = (users, order) => {
const avatars = users.slice(0, 3)
type Overflow = { _id: "overflow"; label: string }
const getAvatars = (users: UIUser[], order: OrderType) => {
const avatars: (Overflow | UIUser)[] = users.slice(0, 3)
if (users.length > 3) {
const overflow = {
const overflow: Overflow = {
_id: "overflow",
label: `+${users.length - 3}`,
}
@ -31,17 +35,22 @@
avatars.unshift(overflow)
}
}
return avatars.map((user, idx) => ({
...user,
zIndex: order === "ltr" ? idx : uniqueUsers.length - idx,
}))
}
function isUser(value: Overflow | UIUser): value is UIUser {
return value._id !== "overflow"
}
</script>
<div class="avatars">
{#each avatars as user}
<span style="z-index:{user.zIndex};">
{#if user._id === "overflow"}
{#if !isUser(user)}
<Avatar
{size}
initials={user.label}