Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State explorer panel #15365

Merged
merged 43 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
99537ae
Find components with state vars and allow user to select
PClmnt Jan 14, 2025
7439dd9
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 14, 2025
7bd718b
work to highlight setting when selected
PClmnt Jan 15, 2025
5c54c43
convert bbui combobox and popover to ts
PClmnt Jan 15, 2025
8763f52
add ability to highlight multiple settings
PClmnt Jan 15, 2025
094881e
clear highlight state as part of component select
PClmnt Jan 15, 2025
e14bf00
some tidy up
PClmnt Jan 16, 2025
7cb422f
find components that are updating state
PClmnt Jan 16, 2025
3cdcc44
further popover changes / ts updates
PClmnt Jan 17, 2025
38e182d
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 17, 2025
1074850
add ability for user to add default state value
PClmnt Jan 21, 2025
417631c
tweaking around user default values
PClmnt Jan 21, 2025
c199b68
some duplicate code
PClmnt Jan 21, 2025
8cab28d
add link for each individual setting
PClmnt Jan 23, 2025
56018b1
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 23, 2025
3ac7c90
refactor and allow for condition and style settings
PClmnt Jan 23, 2025
ad748aa
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 23, 2025
28c59d1
rogue string
PClmnt Jan 23, 2025
513e31d
some tidy up and revert highlightedSetting to obj
PClmnt Jan 23, 2025
20de1b7
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 24, 2025
7c52166
typing
PClmnt Jan 24, 2025
4d4fb3a
fix types
PClmnt Jan 24, 2025
0a2f883
fix bad merge
PClmnt Jan 24, 2025
0ac2f88
fixes
PClmnt Jan 24, 2025
4b7270a
add onRowClick to components updating state
PClmnt Jan 24, 2025
eb93277
further updates
PClmnt Jan 24, 2025
43b7dc5
pr feedback
PClmnt Jan 25, 2025
09f4db6
finding of screen onload
PClmnt Jan 27, 2025
09bb958
finally tracked down issue with urlStateSync causing update
PClmnt Jan 27, 2025
44d9a51
handle js bindings in default value
PClmnt Jan 27, 2025
987b3e8
fix types
PClmnt Jan 28, 2025
b7dbe2e
tidy up
PClmnt Jan 28, 2025
9530235
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 28, 2025
a100099
some further ux updates
PClmnt Jan 28, 2025
680f52a
remove link
PClmnt Jan 29, 2025
6c76675
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 29, 2025
8a0c9ae
lint
PClmnt Jan 29, 2025
cb47e74
Merge branch 'state-and-bindings-panels' of github.com:Budibase/budib…
aptkingston Jan 29, 2025
734be9f
Merge branch 'state-and-bindings-panels' of github.com:Budibase/budib…
aptkingston Jan 29, 2025
532b740
Design tweaks
aptkingston Jan 29, 2025
9d217e8
Merge remote-tracking branch 'refs/remotes/origin/state-and-bindings-…
PClmnt Jan 29, 2025
bf2b5a5
typing issue
PClmnt Jan 29, 2025
404f983
Merge remote-tracking branch 'origin/budi-8980-create-state-panel' in…
PClmnt Jan 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 68 additions & 41 deletions packages/bbui/src/Form/Core/Picker.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<script>
<script lang="ts" context="module">
type O = any
type V = any
</script>

<script lang="ts">
import "@spectrum-css/picker/dist/index-vars.css"
import "@spectrum-css/popover/dist/index-vars.css"
import "@spectrum-css/menu/dist/index-vars.css"
Expand All @@ -11,43 +16,55 @@
import Tags from "../../Tags/Tags.svelte"
import Tag from "../../Tags/Tag.svelte"
import ProgressCircle from "../../ProgressCircle/ProgressCircle.svelte"
import { PopoverAlignment } from "../../constants"

export let id = null
export let disabled = false
export let fieldText = ""
export let fieldIcon = ""
export let fieldColour = ""
export let isPlaceholder = false
export let placeholderOption = null
export let options = []
export let isOptionSelected = () => false
export let isOptionEnabled = () => true
export let onSelectOption = () => {}
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let getOptionIcon = () => null
export let id: string | undefined = undefined
export let disabled: boolean = false
export let fieldText: string = ""
export let fieldIcon: string = ""
export let fieldColour: string = ""
export let isPlaceholder: boolean = false
export let placeholderOption: string | undefined | boolean = undefined
export let options: O[] = []
export let isOptionSelected = (option: O) => option as unknown as boolean
export let isOptionEnabled = (option: O, _index?: number) =>
option as unknown as boolean
export let onSelectOption: (_value: V) => void = () => {}
export let getOptionLabel = (option: O, _index?: number) => `${option}`
export let getOptionValue = (option: O, _index?: number) =>
option as unknown as V
export let getOptionIcon = (option: O, _index?: number) =>
option as unknown as O
export let useOptionIconImage = false
export let getOptionColour = () => null
export let getOptionSubtitle = () => null
export let open = false
export let readonly = false
export let quiet = false
export let autoWidth = false
export let autocomplete = false
export let sort = false
export let searchTerm = null
export let customPopoverHeight
export let align = "left"
export let footer = null
export let customAnchor = null
export let loading
export let onOptionMouseenter = () => {}
export let onOptionMouseleave = () => {}
export let getOptionColour = (option: O, _index?: number) =>
option as unknown as O
export let getOptionSubtitle = (option: O, _index?: number) =>
option as unknown as O
export let open: boolean = false
export let readonly: boolean = false
export let quiet: boolean = false
export let autoWidth: boolean | undefined = false
export let autocomplete: boolean = false
export let sort: boolean = false
export let searchTerm: string | null = null
export let customPopoverHeight: string | undefined = undefined
export let align: PopoverAlignment | undefined = PopoverAlignment.Left
export let footer: string | undefined = undefined
export let customAnchor: HTMLElement | undefined = undefined
export let loading: boolean = false
export let onOptionMouseenter: (
_e: MouseEvent,
_option: any
) => void = () => {}
export let onOptionMouseleave: (
_e: MouseEvent,
_option: any
) => void = () => {}

const dispatch = createEventDispatcher()

let button
let component
let button: any
let component: any

$: sortedOptions = getSortedOptions(options, getOptionLabel, sort)
$: filteredOptions = getFilteredOptions(
Expand All @@ -56,7 +73,7 @@
getOptionLabel
)

const onClick = e => {
const onClick = (e: MouseEvent) => {
e.preventDefault()
e.stopPropagation()
dispatch("click")
Expand All @@ -67,7 +84,11 @@
open = !open
}

const getSortedOptions = (options, getLabel, sort) => {
const getSortedOptions = (
options: any[],
getLabel: (_option: any) => string,
sort: boolean
) => {
if (!options?.length || !Array.isArray(options)) {
return []
}
Expand All @@ -81,17 +102,21 @@
})
}

const getFilteredOptions = (options, term, getLabel) => {
const getFilteredOptions = (
options: any[],
term: string | null,
getLabel: (_option: any) => string
) => {
if (autocomplete && term) {
const lowerCaseTerm = term.toLowerCase()
return options.filter(option => {
return options.filter((option: any) => {
return `${getLabel(option)}`.toLowerCase().includes(lowerCaseTerm)
})
}
return options
}

const onScroll = e => {
const onScroll = (e: any) => {
const scrollPxThreshold = 100
const scrollPositionFromBottom =
e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop
Expand Down Expand Up @@ -151,18 +176,20 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<Popover
anchor={customAnchor ? customAnchor : button}
align={align || "left"}
align={align || PopoverAlignment.Left}
{open}
on:close={() => (open = false)}
useAnchorWidth={!autoWidth}
maxWidth={autoWidth ? 400 : null}
maxWidth={autoWidth ? 400 : undefined}
customHeight={customPopoverHeight}
maxHeight={360}
>
<div
class="popover-content"
class:auto-width={autoWidth}
use:clickOutside={() => (open = false)}
use:clickOutside={() => {
open = false
}}
>
{#if autocomplete}
<Search
Expand Down
16 changes: 8 additions & 8 deletions packages/bbui/src/Form/Core/Search.svelte
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
<script>
<script lang="ts">
import "@spectrum-css/search/dist/index-vars.css"
import { createEventDispatcher } from "svelte"

export let value = null
export let placeholder = null
export let value: any = null
export let placeholder: string | undefined = undefined
export let disabled = false
export let id = null
export let updateOnChange = true
export let quiet = false
export let inputRef
export let inputRef: HTMLElement | undefined = undefined

const dispatch = createEventDispatcher()
let focus = false

const updateValue = value => {
const updateValue = (value: any) => {
dispatch("change", value)
}

const onFocus = () => {
focus = true
}

const onBlur = event => {
const onBlur = (event: any) => {
focus = false
updateValue(event.target.value)
}

const onInput = event => {
const onInput = (event: any) => {
if (!updateOnChange) {
return
}
updateValue(event.target.value)
}

const updateValueOnEnter = event => {
const updateValueOnEnter = (event: any) => {
if (event.key === "Enter") {
updateValue(event.target.value)
}
Expand Down
77 changes: 46 additions & 31 deletions packages/bbui/src/Form/Core/Select.svelte
Original file line number Diff line number Diff line change
@@ -1,58 +1,73 @@
<script>
<script lang="ts" context="module">
type O = any
type V = any
</script>

<script lang="ts">
import { createEventDispatcher } from "svelte"
import Picker from "./Picker.svelte"
import { PopoverAlignment } from "../../constants"

export let value = null
export let id = null
export let placeholder = "Choose an option"
export let disabled = false
export let options = []
export let getOptionLabel = option => option
export let getOptionValue = option => option
export let getOptionIcon = () => null
export let getOptionColour = () => null
export let getOptionSubtitle = () => null
export let compare = null
export let value: V | null = null
export let id: string | undefined = undefined
export let placeholder: string | boolean = "Choose an option"
export let disabled: boolean = false
export let options: O[] = []
export let getOptionLabel = (option: O, _index?: number) => `${option}`
export let getOptionValue = (option: O, _index?: number) =>
option as unknown as V
export let getOptionIcon = (option: O, _index?: number) =>
option as unknown as string
export let getOptionColour = (option: O, _index?: number) =>
option as unknown as string
export let getOptionSubtitle = (option: O, _index?: number) =>
option as unknown as string
export let compare = (option: O, value: V) => option === value
export let useOptionIconImage = false
export let isOptionEnabled
export let readonly = false
export let quiet = false
export let autoWidth = false
export let autocomplete = false
export let sort = false
export let align
export let footer = null
export let open = false
export let tag = null
export let searchTerm = null
export let loading
export let isOptionEnabled = (option: O, _index?: number) =>
option as unknown as boolean
export let readonly: boolean = false
export let quiet: boolean = false
export let autoWidth: boolean = false
export let autocomplete: boolean = false
export let sort: boolean = false
export let align: PopoverAlignment | undefined = PopoverAlignment.Left
export let footer: string | undefined = undefined
export let open: boolean = false
export let searchTerm: string | undefined = undefined
export let loading: boolean | undefined = undefined
export let onOptionMouseenter = () => {}
export let onOptionMouseleave = () => {}
export let customPopoverHeight: string | undefined = undefined

const dispatch = createEventDispatcher()

$: fieldText = getFieldText(value, options, placeholder)
$: fieldIcon = getFieldAttribute(getOptionIcon, value, options)
$: fieldColour = getFieldAttribute(getOptionColour, value, options)

function compareOptionAndValue(option, value) {
function compareOptionAndValue(option: O, value: V) {
return typeof compare === "function"
? compare(option, value)
: option === value
}

const getFieldAttribute = (getAttribute, value, options) => {
const getFieldAttribute = (getAttribute: any, value: V[], options: O[]) => {
// Wait for options to load if there is a value but no options
if (!options?.length) {
return ""
}
const index = options.findIndex((option, idx) =>
const index = options.findIndex((option: any, idx: number) =>
compareOptionAndValue(getOptionValue(option, idx), value)
)
return index !== -1 ? getAttribute(options[index], index) : null
}

const getFieldText = (value, options, placeholder) => {
const getFieldText = (
value: any,
options: any,
placeholder: boolean | string
) => {
if (value == null || value === "") {
// Explicit false means use no placeholder and allow an empty fields
if (placeholder === false) {
Expand All @@ -67,7 +82,7 @@
)
}

const selectOption = value => {
const selectOption = (value: V) => {
dispatch("change", value)
open = false
}
Expand Down Expand Up @@ -98,14 +113,14 @@
{isOptionEnabled}
{autocomplete}
{sort}
{tag}
{onOptionMouseenter}
{onOptionMouseleave}
isPlaceholder={value == null || value === ""}
placeholderOption={placeholder === false
? null
? undefined
: placeholder || "Choose an option"}
isOptionSelected={option => compareOptionAndValue(option, value)}
onSelectOption={selectOption}
{loading}
{customPopoverHeight}
/>
Loading
Loading