PR feedback
This commit is contained in:
parent
08ca33563d
commit
e0d2c70611
|
@ -206,9 +206,15 @@ export class ComponentStore extends BudiStore {
|
||||||
setting?.type?.startsWith("filter")
|
setting?.type?.startsWith("filter")
|
||||||
)
|
)
|
||||||
for (let setting of filterableTypes || []) {
|
for (let setting of filterableTypes || []) {
|
||||||
enrichedComponent[setting.key] = utils.processSearchFilters(
|
const isLegacy = Array.isArray(enrichedComponent[setting.key])
|
||||||
enrichedComponent[setting.key]
|
|
||||||
)
|
if (isLegacy) {
|
||||||
|
const processedSetting = utils.processSearchFilters(
|
||||||
|
enrichedComponent[setting.key]
|
||||||
|
)
|
||||||
|
enrichedComponent[setting.key] = processedSetting
|
||||||
|
migrated = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return migrated
|
return migrated
|
||||||
}
|
}
|
||||||
|
@ -575,9 +581,7 @@ export class ComponentStore extends BudiStore {
|
||||||
const patchResult = patchFn(component, screen)
|
const patchResult = patchFn(component, screen)
|
||||||
|
|
||||||
// Post processing
|
// Post processing
|
||||||
const migrated = null
|
const migrated = this.migrateSettings(component)
|
||||||
|
|
||||||
this.migrateSettings(component)
|
|
||||||
|
|
||||||
// Returning an explicit false signifies that we should skip this
|
// Returning an explicit false signifies that we should skip this
|
||||||
// update. If we migrated something, ensure we never skip.
|
// update. If we migrated something, ensure we never skip.
|
||||||
|
|
|
@ -19,9 +19,7 @@
|
||||||
let queryExtensions = {}
|
let queryExtensions = {}
|
||||||
let localFilters
|
let localFilters
|
||||||
|
|
||||||
$: if (filter) {
|
$: localFilters = filter ? Helpers.cloneDeep(filter) : null
|
||||||
localFilters = Helpers.cloneDeep(filter)
|
|
||||||
}
|
|
||||||
|
|
||||||
$: defaultQuery = QueryUtils.buildQuery(localFilters)
|
$: defaultQuery = QueryUtils.buildQuery(localFilters)
|
||||||
|
|
||||||
|
@ -131,7 +129,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const extendQuery = (defaultQuery, extensions) => {
|
const extendQuery = (defaultQuery, extensions) => {
|
||||||
return {
|
const extended = {
|
||||||
[LogicalOperator.AND]: {
|
[LogicalOperator.AND]: {
|
||||||
conditions: [
|
conditions: [
|
||||||
...(defaultQuery ? [defaultQuery] : []),
|
...(defaultQuery ? [defaultQuery] : []),
|
||||||
|
@ -140,6 +138,11 @@
|
||||||
},
|
},
|
||||||
onEmptyFilter: EmptyFilterOption.RETURN_NONE,
|
onEmptyFilter: EmptyFilterOption.RETURN_NONE,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are no conditions applied at all, clear the request.
|
||||||
|
return extended[LogicalOperator.AND]?.conditions?.length > 0
|
||||||
|
? extended
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
const setUpAutoRefresh = autoRefresh => {
|
const setUpAutoRefresh = autoRefresh => {
|
||||||
|
|
|
@ -58,8 +58,13 @@
|
||||||
return { value: entry, label: Helpers.capitalise(entry) }
|
return { value: entry, label: Helpers.capitalise(entry) }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const onEmptyLabelling = {
|
||||||
|
[OnEmptyFilter.RETURN_ALL]: "All rows",
|
||||||
|
[OnEmptyFilter.RETURN_NONE]: "No rows",
|
||||||
|
}
|
||||||
|
|
||||||
const onEmptyOptions = Object.values(OnEmptyFilter).map(entry => {
|
const onEmptyOptions = Object.values(OnEmptyFilter).map(entry => {
|
||||||
return { value: entry, label: Helpers.capitalise(entry) }
|
return { value: entry, label: onEmptyLabelling[entry] }
|
||||||
})
|
})
|
||||||
|
|
||||||
const context = getContext("context")
|
const context = getContext("context")
|
||||||
|
@ -151,7 +156,7 @@
|
||||||
|
|
||||||
const getGroupPrefix = groupIdx => {
|
const getGroupPrefix = groupIdx => {
|
||||||
if (groupIdx == 0) {
|
if (groupIdx == 0) {
|
||||||
return "Where"
|
return "When"
|
||||||
}
|
}
|
||||||
const operatorMapping = {
|
const operatorMapping = {
|
||||||
[FilterOperator.ANY]: "or",
|
[FilterOperator.ANY]: "or",
|
||||||
|
@ -191,16 +196,17 @@
|
||||||
if (targetFilter) {
|
if (targetFilter) {
|
||||||
if (deleteFilter) {
|
if (deleteFilter) {
|
||||||
targetGroup.filters.splice(filterIdx, 1)
|
targetGroup.filters.splice(filterIdx, 1)
|
||||||
|
|
||||||
|
// Clear the group entirely if no valid filters remain
|
||||||
|
if (targetGroup.filters.length === 0) {
|
||||||
|
editable.groups.splice(groupIdx, 1)
|
||||||
|
}
|
||||||
} else if (filter) {
|
} else if (filter) {
|
||||||
targetGroup.filters[filterIdx] = filter
|
targetGroup.filters[filterIdx] = filter
|
||||||
}
|
}
|
||||||
} else if (targetGroup) {
|
} else if (targetGroup) {
|
||||||
if (deleteGroup) {
|
if (deleteGroup) {
|
||||||
if (editable.groups.length > 1) {
|
editable.groups.splice(groupIdx, 1)
|
||||||
editable.groups.splice(groupIdx, 1)
|
|
||||||
} else {
|
|
||||||
editable = {}
|
|
||||||
}
|
|
||||||
} else if (addFilter) {
|
} else if (addFilter) {
|
||||||
targetGroup.filters.push({
|
targetGroup.filters.push({
|
||||||
valueType: FilterValueType.VALUE,
|
valueType: FilterValueType.VALUE,
|
||||||
|
@ -239,6 +245,9 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the request to null if the groups are emptied
|
||||||
|
editable = editable.groups.length ? editable : null
|
||||||
|
|
||||||
dispatch("change", editable)
|
dispatch("change", editable)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -294,7 +303,7 @@
|
||||||
placeholder={false}
|
placeholder={false}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span>of the following filters are met:</span>
|
<span>of the following filters are matched:</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="group-actions">
|
<div class="group-actions">
|
||||||
<Icon
|
<Icon
|
||||||
|
@ -386,49 +395,51 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="filters-footer">
|
<div class="filters-footer">
|
||||||
{#if behaviourFilters && editableFilters?.groups?.length}
|
<Layout noPadding>
|
||||||
<div class="empty-filter">
|
{#if behaviourFilters && editableFilters?.groups?.length}
|
||||||
<span>Return</span>
|
<div class="empty-filter">
|
||||||
<span class="empty-filter-picker">
|
<span>Return</span>
|
||||||
<Select
|
<span class="empty-filter-picker">
|
||||||
value={editableFilters?.onEmptyFilter}
|
<Select
|
||||||
options={onEmptyOptions}
|
value={editableFilters?.onEmptyFilter}
|
||||||
getOptionLabel={opt => opt.label}
|
options={onEmptyOptions}
|
||||||
getOptionValue={opt => opt.value}
|
getOptionLabel={opt => opt.label}
|
||||||
on:change={e => {
|
getOptionValue={opt => opt.value}
|
||||||
handleFilterChange({
|
on:change={e => {
|
||||||
onEmptyFilter: e.detail,
|
handleFilterChange({
|
||||||
})
|
onEmptyFilter: e.detail,
|
||||||
}}
|
})
|
||||||
placeholder={false}
|
}}
|
||||||
|
placeholder={false}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>when all filters are empty</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="add-group">
|
||||||
|
<Button
|
||||||
|
icon="AddCircle"
|
||||||
|
size="M"
|
||||||
|
secondary
|
||||||
|
on:click={() => {
|
||||||
|
handleFilterChange({
|
||||||
|
addGroup: true,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add filter group
|
||||||
|
</Button>
|
||||||
|
<a
|
||||||
|
href="https://docs.budibase.com/docs/searchfilter-data"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
name="HelpOutline"
|
||||||
|
color="var(--spectrum-global-color-gray-600)"
|
||||||
/>
|
/>
|
||||||
</span>
|
</a>
|
||||||
<span>when all filters are empty</span>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</Layout>
|
||||||
<div class="add-group">
|
|
||||||
<Button
|
|
||||||
icon="AddCircle"
|
|
||||||
size="M"
|
|
||||||
secondary
|
|
||||||
on:click={() => {
|
|
||||||
handleFilterChange({
|
|
||||||
addGroup: true,
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Add filter group
|
|
||||||
</Button>
|
|
||||||
<a
|
|
||||||
href="https://docs.budibase.com/docs/searchfilter-data"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
name="HelpOutline"
|
|
||||||
color="var(--spectrum-global-color-gray-600)"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<Body size="S">None of the table column can be used for filtering.</Body>
|
<Body size="S">None of the table column can be used for filtering.</Body>
|
||||||
|
@ -446,7 +457,7 @@
|
||||||
.empty-filter,
|
.empty-filter,
|
||||||
.group-options {
|
.group-options {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--spacing-xl);
|
gap: var(--spacing-m);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,14 +472,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-filter-picker {
|
.empty-filter-picker {
|
||||||
width: 80px;
|
width: 92px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-groups {
|
.filter-groups {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--spacing-xl);
|
gap: var(--spacing-xl);
|
||||||
/* overflow-x: scroll; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.group {
|
.group {
|
||||||
|
@ -489,7 +499,7 @@
|
||||||
.filter {
|
.filter {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
grid-template-columns: minmax(150px, 1fr) 170px 200px 40px 40px;
|
grid-template-columns: minmax(150px, 1fr) 170px minmax(200px, 1fr) 40px 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filters-footer {
|
.filters-footer {
|
||||||
|
|
|
@ -20,12 +20,9 @@
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
export let allowBindings = false
|
export let allowBindings = false
|
||||||
export let schemaFields
|
export let schemaFields
|
||||||
|
|
||||||
// Export these to another field type
|
|
||||||
export let panel
|
export let panel
|
||||||
export let toReadable
|
export let toReadable
|
||||||
export let toRuntime
|
export let toRuntime
|
||||||
// Only required if you're in the builder, which we aint.
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const { OperatorOptions, FilterValueType } = Constants
|
const { OperatorOptions, FilterValueType } = Constants
|
||||||
|
@ -48,17 +45,21 @@
|
||||||
return schemaFields.find(field => field.name === filter.field)
|
return schemaFields.find(field => field.name === filter.field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const drawerOnChange = e => {
|
||||||
|
drawerValue = e.detail
|
||||||
|
}
|
||||||
|
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
fieldValue = e.detail
|
fieldValue = e.detail
|
||||||
dispatch("change", {
|
dispatch("change", {
|
||||||
value: toRuntime(bindings, fieldValue),
|
value: toRuntime ? toRuntime(bindings, fieldValue) : fieldValue,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onConfirm = () => {
|
const onConfirmBinding = () => {
|
||||||
dispatch("change", {
|
dispatch("change", {
|
||||||
value: fieldValue,
|
value: toRuntime ? toRuntime(bindings, drawerValue) : drawerValue,
|
||||||
valueType: fieldValue ? FilterValueType.BINDING : FilterValueType.VALUE,
|
valueType: drawerValue ? FilterValueType.BINDING : FilterValueType.VALUE,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +140,7 @@
|
||||||
cta
|
cta
|
||||||
slot="buttons"
|
slot="buttons"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
onConfirm()
|
onConfirmBinding()
|
||||||
bindingDrawer.hide()
|
bindingDrawer.hide()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -153,7 +154,7 @@
|
||||||
allowJS
|
allowJS
|
||||||
allowHelpers
|
allowHelpers
|
||||||
allowHBS
|
allowHBS
|
||||||
on:change={onChange}
|
on:change={drawerOnChange}
|
||||||
{bindings}
|
{bindings}
|
||||||
/>
|
/>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
|
@ -1,14 +1,6 @@
|
||||||
import DataFetch from "./DataFetch.js"
|
import DataFetch from "./DataFetch.js"
|
||||||
|
|
||||||
export default class CustomFetch extends DataFetch {
|
export default class CustomFetch extends DataFetch {
|
||||||
determineFeatureFlags() {
|
|
||||||
return {
|
|
||||||
supportsSearch: false,
|
|
||||||
supportsSort: false,
|
|
||||||
supportsPagination: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the correct Budibase type for a JS value
|
// Gets the correct Budibase type for a JS value
|
||||||
getType(value) {
|
getType(value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
|
|
@ -183,7 +183,7 @@ export default class DataFetch {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the query
|
// Build the query
|
||||||
let query = this.options.query || null
|
let query = this.options.query
|
||||||
|
|
||||||
if (!query && this.features.supportsSearch) {
|
if (!query && this.features.supportsSearch) {
|
||||||
query = buildQuery(filter || defaultQuery)
|
query = buildQuery(filter || defaultQuery)
|
||||||
|
|
|
@ -307,11 +307,11 @@ export class ColumnSplitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds a JSON query from the filter structure generated in the builder
|
* Builds a JSON query from the filter a SearchFilter definition
|
||||||
* @param filter the builder filter structure
|
* @param filter the builder filter structure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const builderFilter = (expression: SearchFilter) => {
|
const buildCondition = (expression: SearchFilter) => {
|
||||||
// Filter body
|
// Filter body
|
||||||
let query: SearchFilters = {
|
let query: SearchFilters = {
|
||||||
string: {},
|
string: {},
|
||||||
|
@ -378,13 +378,15 @@ const builderFilter = (expression: SearchFilter) => {
|
||||||
value = `${value}`?.toLowerCase() === "true"
|
value = `${value}`?.toLowerCase() === "true"
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
["contains", "notContains", "containsAny"].includes(operator) &&
|
["contains", "notContains", "containsAny"].includes(
|
||||||
|
operator.toLocaleString()
|
||||||
|
) &&
|
||||||
type === "array" &&
|
type === "array" &&
|
||||||
typeof value === "string"
|
typeof value === "string"
|
||||||
) {
|
) {
|
||||||
value = value.split(",")
|
value = value.split(",")
|
||||||
}
|
}
|
||||||
if (operator.startsWith("range") && query.range) {
|
if (operator.toLocaleString().startsWith("range") && query.range) {
|
||||||
const minint =
|
const minint =
|
||||||
SqlNumberTypeRangeMap[externalType as keyof typeof SqlNumberTypeRangeMap]
|
SqlNumberTypeRangeMap[externalType as keyof typeof SqlNumberTypeRangeMap]
|
||||||
?.min || Number.MIN_SAFE_INTEGER
|
?.min || Number.MIN_SAFE_INTEGER
|
||||||
|
@ -497,13 +499,15 @@ export const buildQueryLegacy = (
|
||||||
value = `${value}`?.toLowerCase() === "true"
|
value = `${value}`?.toLowerCase() === "true"
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
["contains", "notContains", "containsAny"].includes(operator) &&
|
["contains", "notContains", "containsAny"].includes(
|
||||||
|
operator.toLocaleString()
|
||||||
|
) &&
|
||||||
type === "array" &&
|
type === "array" &&
|
||||||
typeof value === "string"
|
typeof value === "string"
|
||||||
) {
|
) {
|
||||||
value = value.split(",")
|
value = value.split(",")
|
||||||
}
|
}
|
||||||
if (operator.startsWith("range") && query.range) {
|
if (operator.toLocaleString().startsWith("range") && query.range) {
|
||||||
const minint =
|
const minint =
|
||||||
SqlNumberTypeRangeMap[
|
SqlNumberTypeRangeMap[
|
||||||
externalType as keyof typeof SqlNumberTypeRangeMap
|
externalType as keyof typeof SqlNumberTypeRangeMap
|
||||||
|
@ -555,28 +559,40 @@ export const buildQueryLegacy = (
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a **SearchFilterGroup** filter definition into a grouped
|
||||||
|
* search query of type **SearchFilters**
|
||||||
|
*
|
||||||
|
* Legacy support remains for the old **SearchFilter[]** format.
|
||||||
|
* These will be migrated to an appropriate **SearchFilters** object, if encountered
|
||||||
|
*
|
||||||
|
* @param filter
|
||||||
|
*
|
||||||
|
* @returns {SearchFilters}
|
||||||
|
*/
|
||||||
|
|
||||||
export const buildQuery = (
|
export const buildQuery = (
|
||||||
filter?: SearchFilterGroup | SearchFilter[]
|
filter?: SearchFilterGroup | SearchFilter[]
|
||||||
): SearchFilters | undefined => {
|
): SearchFilters | undefined => {
|
||||||
if (!filter) {
|
const parsedFilter: SearchFilterGroup | undefined =
|
||||||
return
|
processSearchFilters(filter)
|
||||||
}
|
|
||||||
|
|
||||||
const parsedFilter = processSearchFilters(filter)
|
|
||||||
|
|
||||||
if (!parsedFilter) {
|
if (!parsedFilter) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const operatorMap = {
|
const operatorMap: { [key in FilterGroupLogicalOperator]: LogicalOperator } =
|
||||||
[FilterGroupLogicalOperator.ALL]: LogicalOperator.AND,
|
{
|
||||||
[FilterGroupLogicalOperator.ANY]: LogicalOperator.OR,
|
[FilterGroupLogicalOperator.ALL]: LogicalOperator.AND,
|
||||||
}
|
[FilterGroupLogicalOperator.ANY]: LogicalOperator.OR,
|
||||||
|
}
|
||||||
|
|
||||||
const globalOnEmpty = parsedFilter.onEmptyFilter
|
const globalOnEmpty = parsedFilter.onEmptyFilter
|
||||||
? parsedFilter.onEmptyFilter
|
? parsedFilter.onEmptyFilter
|
||||||
: null
|
: null
|
||||||
const globalOperator = operatorMap[parsedFilter.logicalOperator]
|
|
||||||
|
const globalOperator: LogicalOperator =
|
||||||
|
operatorMap[parsedFilter.logicalOperator as FilterGroupLogicalOperator]
|
||||||
|
|
||||||
const coreRequest: SearchFilters = {
|
const coreRequest: SearchFilters = {
|
||||||
...(globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {}),
|
...(globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {}),
|
||||||
|
@ -585,7 +601,7 @@ export const buildQuery = (
|
||||||
return {
|
return {
|
||||||
[operatorMap[group.logicalOperator]]: {
|
[operatorMap[group.logicalOperator]]: {
|
||||||
conditions: group.filters
|
conditions: group.filters
|
||||||
?.map(x => builderFilter(x))
|
?.map(x => buildCondition(x))
|
||||||
.filter(filter => filter),
|
.filter(filter => filter),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,8 @@ export function trimOtherProps(object: any, allowedProps: string[]) {
|
||||||
* @param {SearchFilter[] | SearchFilterGroup} filters
|
* @param {SearchFilter[] | SearchFilterGroup} filters
|
||||||
*/
|
*/
|
||||||
export const processSearchFilters = (
|
export const processSearchFilters = (
|
||||||
filters: SearchFilter[] | SearchFilterGroup
|
filters: SearchFilter[] | SearchFilterGroup | undefined
|
||||||
) => {
|
): SearchFilterGroup | undefined => {
|
||||||
if (!filters) {
|
if (!filters) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ export const processSearchFilters = (
|
||||||
|
|
||||||
return migratedSetting
|
return migratedSetting
|
||||||
} else if (!filters?.groups) {
|
} else if (!filters?.groups) {
|
||||||
return null
|
return
|
||||||
}
|
}
|
||||||
return filters
|
return filters
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue