mirror of
https://github.com/nocodb/nocodb.git
synced 2026-05-02 22:36:39 +00:00
684 lines
23 KiB
Vue
684 lines
23 KiB
Vue
<script setup lang="ts">
|
|
import type {
|
|
ConditionBuilderValue,
|
|
FilterCombinator,
|
|
FilterConditionValue,
|
|
FilterDataType,
|
|
FilterOperator,
|
|
FilterOperatorType,
|
|
FormBuilderConditionBuilderElement,
|
|
} from 'nocodb-sdk'
|
|
|
|
interface Props {
|
|
element: FormBuilderConditionBuilderElement
|
|
modelValue?: ConditionBuilderValue | null
|
|
disabled?: boolean
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
disabled: false,
|
|
})
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
const vModel = useVModel(props, 'modelValue', emit)
|
|
|
|
const workflowContext = inject(WorkflowVariableInj, null)
|
|
|
|
const { getFieldOptions, getIsLoadingFieldOptions, loadOptions } = useFormBuilderHelperOrThrow()
|
|
|
|
// Dropdown state
|
|
const isDropdownOpen = ref(false)
|
|
|
|
// Track property input mode per condition: 'select' or 'manual'
|
|
const propertyInputMode = ref<Record<string, 'select' | 'manual'>>({})
|
|
|
|
// Define all available operators
|
|
const ALL_OPERATORS: FilterOperator[] = [
|
|
// String operators
|
|
{ id: 'string:equals', name: 'equals', type: 'string', operation: 'equals' },
|
|
{ id: 'string:notEquals', name: 'not equals', type: 'string', operation: 'notEquals' },
|
|
{ id: 'string:contains', name: 'contains', type: 'string', operation: 'contains' },
|
|
{ id: 'string:notContains', name: 'does not contain', type: 'string', operation: 'notContains' },
|
|
{ id: 'string:startsWith', name: 'starts with', type: 'string', operation: 'startsWith' },
|
|
{ id: 'string:endsWith', name: 'ends with', type: 'string', operation: 'endsWith' },
|
|
{ id: 'string:regex', name: 'matches regex', type: 'string', operation: 'regex' },
|
|
{ id: 'string:exists', name: 'exists', type: 'string', operation: 'exists', singleValue: true },
|
|
{ id: 'string:notExists', name: 'does not exist', type: 'string', operation: 'notExists', singleValue: true },
|
|
{ id: 'string:empty', name: 'is empty', type: 'string', operation: 'empty', singleValue: true },
|
|
{ id: 'string:notEmpty', name: 'is not empty', type: 'string', operation: 'notEmpty', singleValue: true },
|
|
|
|
// Number operators
|
|
{ id: 'number:equals', name: 'equals', type: 'number', operation: 'equals' },
|
|
{ id: 'number:notEquals', name: 'not equals', type: 'number', operation: 'notEquals' },
|
|
{ id: 'number:gt', name: 'greater than', type: 'number', operation: 'gt' },
|
|
{ id: 'number:gte', name: 'greater than or equal', type: 'number', operation: 'gte' },
|
|
{ id: 'number:lt', name: 'less than', type: 'number', operation: 'lt' },
|
|
{ id: 'number:lte', name: 'less than or equal', type: 'number', operation: 'lte' },
|
|
{ id: 'number:exists', name: 'exists', type: 'number', operation: 'exists', singleValue: true },
|
|
{ id: 'number:notExists', name: 'does not exist', type: 'number', operation: 'notExists', singleValue: true },
|
|
{ id: 'number:empty', name: 'is empty', type: 'number', operation: 'empty', singleValue: true },
|
|
{ id: 'number:notEmpty', name: 'is not empty', type: 'number', operation: 'notEmpty', singleValue: true },
|
|
|
|
// DateTime operators
|
|
{ id: 'dateTime:equals', name: 'equals', type: 'dateTime', operation: 'equals' },
|
|
{ id: 'dateTime:notEquals', name: 'not equals', type: 'dateTime', operation: 'notEquals' },
|
|
{ id: 'dateTime:after', name: 'after', type: 'dateTime', operation: 'after' },
|
|
{ id: 'dateTime:before', name: 'before', type: 'dateTime', operation: 'before' },
|
|
{ id: 'dateTime:exists', name: 'exists', type: 'dateTime', operation: 'exists', singleValue: true },
|
|
{ id: 'dateTime:notExists', name: 'does not exist', type: 'dateTime', operation: 'notExists', singleValue: true },
|
|
{ id: 'dateTime:empty', name: 'is empty', type: 'dateTime', operation: 'empty', singleValue: true },
|
|
{ id: 'dateTime:notEmpty', name: 'is not empty', type: 'dateTime', operation: 'notEmpty', singleValue: true },
|
|
|
|
// Boolean operators
|
|
{ id: 'boolean:isTrue', name: 'is true', type: 'boolean', operation: 'isTrue', singleValue: true },
|
|
{ id: 'boolean:isFalse', name: 'is false', type: 'boolean', operation: 'isFalse', singleValue: true },
|
|
{ id: 'boolean:exists', name: 'exists', type: 'boolean', operation: 'exists', singleValue: true },
|
|
{ id: 'boolean:notExists', name: 'does not exist', type: 'boolean', operation: 'notExists', singleValue: true },
|
|
|
|
// Any type operators
|
|
{ id: 'any:equals', name: 'equals', type: 'any', operation: 'equals' },
|
|
{ id: 'any:notEquals', name: 'not equals', type: 'any', operation: 'notEquals' },
|
|
{ id: 'any:exists', name: 'exists', type: 'any', operation: 'exists', singleValue: true },
|
|
{ id: 'any:notExists', name: 'does not exist', type: 'any', operation: 'notExists', singleValue: true },
|
|
{ id: 'any:empty', name: 'is empty', type: 'any', operation: 'empty', singleValue: true },
|
|
{ id: 'any:notEmpty', name: 'is not empty', type: 'any', operation: 'notEmpty', singleValue: true },
|
|
]
|
|
|
|
// Default operator by type
|
|
const DEFAULT_OPERATOR_BY_TYPE: Record<FilterDataType, string> = {
|
|
string: 'string:equals',
|
|
number: 'number:equals',
|
|
boolean: 'boolean:isTrue',
|
|
dateTime: 'dateTime:equals',
|
|
array: 'any:exists',
|
|
object: 'any:exists',
|
|
any: 'any:equals',
|
|
}
|
|
|
|
// Get available operators based on element configuration
|
|
const availableOperators = computed(() => {
|
|
let operators = ALL_OPERATORS
|
|
|
|
// Filter by allowed types
|
|
if (props.element.allowedTypes?.length) {
|
|
operators = operators.filter((op) => props.element.allowedTypes!.includes(op.type) || op.type === 'any')
|
|
}
|
|
|
|
// Filter by supported operators
|
|
if (props.element.supportedOperators?.length) {
|
|
operators = operators.filter((op) => props.element.supportedOperators!.includes(op.operation))
|
|
}
|
|
|
|
return operators
|
|
})
|
|
|
|
// Group operators by type for dropdown
|
|
const operatorsByType = computed(() => {
|
|
const grouped: Record<string, FilterOperator[]> = {}
|
|
availableOperators.value.forEach((op) => {
|
|
if (!grouped[op.type]) {
|
|
grouped[op.type] = []
|
|
}
|
|
grouped[op.type].push(op)
|
|
})
|
|
return grouped
|
|
})
|
|
|
|
// Get operator by ID
|
|
function getOperator(type: FilterDataType, operation: FilterOperatorType): FilterOperator | undefined {
|
|
return availableOperators.value.find((op) => op.type === type && op.operation === operation)
|
|
}
|
|
|
|
// Initialize conditions
|
|
const conditions = ref<FilterConditionValue[]>([])
|
|
const combinator = ref<FilterCombinator>('and')
|
|
|
|
// Track internal updates
|
|
const isInternalUpdate = ref(false)
|
|
|
|
// Initialize from modelValue
|
|
function initializeConditions() {
|
|
if (!vModel.value) {
|
|
conditions.value = []
|
|
combinator.value = 'and'
|
|
return
|
|
}
|
|
|
|
combinator.value = vModel.value.combinator || 'and'
|
|
conditions.value = (vModel.value.conditions || []).map((c) => ({
|
|
...c,
|
|
id: c.id || generateRandomUUID(),
|
|
}))
|
|
}
|
|
|
|
// Watch for external changes
|
|
watch(
|
|
() => props.modelValue,
|
|
() => {
|
|
if (isInternalUpdate.value) return
|
|
initializeConditions()
|
|
},
|
|
{ immediate: true, deep: true },
|
|
)
|
|
|
|
// Sync back to modelValue
|
|
watch(
|
|
[conditions, combinator],
|
|
() => {
|
|
isInternalUpdate.value = true
|
|
vModel.value = {
|
|
combinator: combinator.value,
|
|
conditions: conditions.value,
|
|
}
|
|
nextTick(() => {
|
|
isInternalUpdate.value = false
|
|
})
|
|
},
|
|
{ deep: true },
|
|
)
|
|
|
|
// Get workflow variables
|
|
const workflowVariables = computed(() => {
|
|
if (!workflowContext?.selectedNodeId?.value || !workflowContext?.getAvailableVariablesFlat) {
|
|
return []
|
|
}
|
|
return workflowContext.getAvailableVariablesFlat(workflowContext.selectedNodeId.value)
|
|
})
|
|
|
|
const groupedVariables = computed(() => {
|
|
if (!workflowContext?.selectedNodeId?.value || !workflowContext?.getAvailableVariables) {
|
|
return []
|
|
}
|
|
return workflowContext.getAvailableVariables(workflowContext.selectedNodeId.value)
|
|
})
|
|
|
|
// Add new condition
|
|
function addCondition() {
|
|
if (props.element.maxConditions && conditions.value.length >= props.element.maxConditions) {
|
|
return
|
|
}
|
|
|
|
const defaultType = props.element.allowedTypes?.[0] || 'string'
|
|
const defaultOperatorId = DEFAULT_OPERATOR_BY_TYPE[defaultType]
|
|
const defaultOperator = availableOperators.value.find((op) => op.id === defaultOperatorId) || availableOperators.value[0]
|
|
|
|
conditions.value.push({
|
|
id: generateRandomUUID(),
|
|
leftValue: '',
|
|
operator: {
|
|
type: defaultOperator?.type || 'string',
|
|
operation: defaultOperator?.operation || 'equals',
|
|
},
|
|
rightValue: '',
|
|
})
|
|
}
|
|
|
|
// Remove condition
|
|
function removeCondition(index: number) {
|
|
conditions.value.splice(index, 1)
|
|
}
|
|
|
|
// Update condition
|
|
function updateCondition(index: number, field: keyof FilterConditionValue, value: unknown) {
|
|
if (field === 'operator' && typeof value === 'string') {
|
|
// Parse operator ID (e.g., 'string:equals')
|
|
const [type, operation] = value.split(':') as [FilterDataType, FilterOperatorType]
|
|
conditions.value[index].operator = { type, operation }
|
|
} else {
|
|
;(conditions.value[index] as Record<string, unknown>)[field] = value
|
|
}
|
|
}
|
|
|
|
// Get operator select options
|
|
const operatorOptions = computed(() => {
|
|
const options: Array<{ label: string; value: string; group?: string }> = []
|
|
|
|
Object.entries(operatorsByType.value).forEach(([type, ops]) => {
|
|
ops.forEach((op) => {
|
|
options.push({
|
|
label: op.name,
|
|
value: op.id,
|
|
group: type,
|
|
})
|
|
})
|
|
})
|
|
|
|
return options
|
|
})
|
|
|
|
// Check if operator is single-value (no right side needed)
|
|
function isSingleValueOperator(condition: FilterConditionValue): boolean {
|
|
const operator = getOperator(condition.operator.type, condition.operator.operation)
|
|
return operator?.singleValue || false
|
|
}
|
|
|
|
// Can add more conditions
|
|
const canAddCondition = computed(() => {
|
|
if (props.disabled) return false
|
|
return !(props.element.maxConditions && conditions.value.length >= props.element.maxConditions)
|
|
})
|
|
|
|
// Property options support
|
|
const hasPropertyOptions = computed(() => {
|
|
return !!(props.element.propertyOptions?.length || props.element.fetchPropertyOptionsKey)
|
|
})
|
|
|
|
// Get property options (static or fetched)
|
|
const propertyOptions = computed(() => {
|
|
// Static options from element config
|
|
if (props.element.propertyOptions?.length) {
|
|
return props.element.propertyOptions
|
|
}
|
|
// Fetched options
|
|
if (props.element.fetchPropertyOptionsKey && props.element.model) {
|
|
return getFieldOptions(`${props.element.model}_properties`) || []
|
|
}
|
|
return []
|
|
})
|
|
|
|
const isLoadingPropertyOptions = computed(() => {
|
|
if (!props.element.fetchPropertyOptionsKey || !props.element.model) return false
|
|
return getIsLoadingFieldOptions(`${props.element.model}_properties`)
|
|
})
|
|
|
|
// Get input mode for a condition
|
|
function getPropertyInputMode(conditionId: string): 'select' | 'manual' {
|
|
return propertyInputMode.value[conditionId] || (hasPropertyOptions.value ? 'select' : 'manual')
|
|
}
|
|
|
|
// Toggle property input mode for a condition
|
|
function togglePropertyInputMode(conditionId: string) {
|
|
const current = getPropertyInputMode(conditionId)
|
|
propertyInputMode.value[conditionId] = current === 'select' ? 'manual' : 'select'
|
|
}
|
|
|
|
// Load property options if fetchPropertyOptionsKey is set
|
|
async function loadPropertyOptions() {
|
|
if (props.element.fetchPropertyOptionsKey && props.element.model) {
|
|
const elementForFetch = {
|
|
...props.element,
|
|
model: `${props.element.model}_properties`,
|
|
fetchOptionsKey: props.element.fetchPropertyOptionsKey,
|
|
}
|
|
await loadOptions(elementForFetch as any)
|
|
}
|
|
}
|
|
|
|
// Load options on mount
|
|
onMounted(() => {
|
|
loadPropertyOptions()
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="nc-condition-builder">
|
|
<NcListDropdown
|
|
v-model:visible="isDropdownOpen"
|
|
:disabled="disabled"
|
|
placement="bottomLeft"
|
|
overlay-class-name="nc-condition-builder-dropdown"
|
|
>
|
|
<!-- Trigger: Compact condition count display -->
|
|
<div
|
|
class="nc-conditions-count flex-1"
|
|
:class="{
|
|
'text-nc-content-brand': conditions.length > 0,
|
|
'text-nc-content-gray-muted': conditions.length === 0,
|
|
}"
|
|
>
|
|
{{ conditions.length > 0 ? `${conditions.length} condition${conditions.length !== 1 ? 's' : ''}` : 'No conditions' }}
|
|
</div>
|
|
<GeneralIcon
|
|
icon="ncChevronDown"
|
|
class="flex-none w-4 h-4"
|
|
:class="{
|
|
'text-nc-content-brand': conditions.length > 0,
|
|
'text-nc-content-gray-muted': conditions.length === 0,
|
|
}"
|
|
/>
|
|
|
|
<!-- Dropdown overlay: Full condition builder -->
|
|
<template #overlay>
|
|
<div class="nc-condition-builder-dropdown-container">
|
|
<!-- Empty state -->
|
|
<div v-if="conditions.length === 0" class="p-4">
|
|
<NcButton type="text" size="small" :disabled="disabled" @click="addCondition">
|
|
<template #icon>
|
|
<GeneralIcon icon="ncPlus" class="w-4 h-4" />
|
|
</template>
|
|
Add condition
|
|
</NcButton>
|
|
<div class="text-nc-content-gray-muted mt-2 ml-0.5">No conditions added</div>
|
|
</div>
|
|
|
|
<!-- Conditions list -->
|
|
<div v-else class="p-3">
|
|
<div class="space-y-1.5 mb-3">
|
|
<template v-for="(condition, index) in conditions" :key="condition.id">
|
|
<!-- Single connected row like If node -->
|
|
<div class="flex flex-nowrap gap-0 nc-filter-wrapper">
|
|
<!-- Where / AND / OR -->
|
|
<div v-if="index === 0" class="flex items-center !min-w-18 !max-w-18 nc-filter-where-label">Where</div>
|
|
<NcSelect
|
|
v-else
|
|
:value="combinator"
|
|
:disabled="index !== 1"
|
|
class="h-full !max-w-18 !min-w-18 capitalize"
|
|
dropdown-class-name="nc-dropdown-filter-logical-op"
|
|
@update:value="combinator = $event"
|
|
>
|
|
<a-select-option value="and">
|
|
<div class="flex items-center justify-between gap-2">
|
|
<span class="capitalize">And</span>
|
|
<GeneralIcon
|
|
v-if="combinator === 'and'"
|
|
id="nc-selected-item-icon"
|
|
icon="ncCheck"
|
|
class="text-primary w-4 h-4"
|
|
/>
|
|
</div>
|
|
</a-select-option>
|
|
<a-select-option value="or">
|
|
<div class="flex items-center justify-between gap-2">
|
|
<span class="capitalize">Or</span>
|
|
<GeneralIcon
|
|
v-if="combinator === 'or'"
|
|
id="nc-selected-item-icon"
|
|
icon="ncCheck"
|
|
class="text-primary w-4 h-4"
|
|
/>
|
|
</div>
|
|
</a-select-option>
|
|
</NcSelect>
|
|
|
|
<!-- Field Input -->
|
|
<div v-if="!element.fixedLeftValue" class="nc-filter-field-input min-w-36 max-w-36 flex items-center">
|
|
<template v-if="hasPropertyOptions && getPropertyInputMode(condition.id) === 'select'">
|
|
<NcButton
|
|
type="text"
|
|
size="xs"
|
|
class="nc-property-mode-toggle flex-shrink-0 ml-1"
|
|
:disabled="disabled"
|
|
@click="togglePropertyInputMode(condition.id)"
|
|
>
|
|
<GeneralIcon icon="ncList" class="text-nc-content-gray-muted w-3.5 h-3.5" />
|
|
</NcButton>
|
|
<NcSelect
|
|
:value="condition.leftValue || undefined"
|
|
:disabled="disabled"
|
|
:loading="isLoadingPropertyOptions"
|
|
:placeholder="element.propertyPlaceholder || 'Property'"
|
|
class="nc-property-select flex-1"
|
|
dropdown-class-name="nc-dropdown-filter-property"
|
|
show-search
|
|
allow-clear
|
|
:filter-option="(input: string, option: any) => option.label?.toLowerCase()?.includes(input.toLowerCase())"
|
|
@update:value="updateCondition(index, 'leftValue', $event || '')"
|
|
>
|
|
<a-select-option v-for="opt in propertyOptions" :key="opt.value" :value="opt.value">
|
|
<div class="flex items-center justify-between gap-2">
|
|
<span class="truncate">{{ opt.label }}</span>
|
|
<GeneralIcon
|
|
v-if="condition.leftValue === opt.value"
|
|
id="nc-selected-item-icon"
|
|
icon="ncCheck"
|
|
class="text-primary w-4 h-4 flex-shrink-0"
|
|
/>
|
|
</div>
|
|
</a-select-option>
|
|
</NcSelect>
|
|
</template>
|
|
|
|
<!-- Manual mode (WorkflowInput) -->
|
|
<template v-else>
|
|
<!-- Mode toggle button (only show if hasPropertyOptions) -->
|
|
<NcButton
|
|
v-if="hasPropertyOptions"
|
|
type="text"
|
|
size="xs"
|
|
class="nc-property-mode-toggle flex-shrink-0 ml-1"
|
|
:disabled="disabled"
|
|
@click="togglePropertyInputMode(condition.id)"
|
|
>
|
|
<GeneralIcon icon="ncCode" class="text-nc-content-gray-muted w-3.5 h-3.5" />
|
|
</NcButton>
|
|
<NcFormBuilderInputWorkflowInput
|
|
:model-value="condition.leftValue || ''"
|
|
:placeholder="element.propertyPlaceholder || 'Property'"
|
|
:variables="workflowVariables"
|
|
:grouped-variables="groupedVariables"
|
|
:read-only="disabled"
|
|
class="nc-property-input flex-1 h-8"
|
|
@update:model-value="updateCondition(index, 'leftValue', $event)"
|
|
/>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Operator -->
|
|
<NcSelect
|
|
:value="`${condition.operator.type}:${condition.operator.operation}`"
|
|
:options="operatorOptions"
|
|
:disabled="disabled"
|
|
class="nc-filter-comparison-op !min-w-26.75"
|
|
dropdown-class-name="nc-dropdown-filter-comp-op"
|
|
@update:value="updateCondition(index, 'operator', $event)"
|
|
/>
|
|
|
|
<!-- Value Input (if needed) -->
|
|
<div
|
|
v-if="!isSingleValueOperator(condition)"
|
|
class="nc-filter-value-input flex items-center flex-grow min-w-34"
|
|
>
|
|
<NcFormBuilderInputWorkflowInput
|
|
:model-value="condition.rightValue || ''"
|
|
placeholder="Enter value"
|
|
:variables="workflowVariables"
|
|
:grouped-variables="groupedVariables"
|
|
:read-only="disabled"
|
|
class="nc-value-input flex-1 h-8"
|
|
@update:model-value="updateCondition(index, 'rightValue', $event)"
|
|
/>
|
|
</div>
|
|
<div v-else class="flex-grow"></div>
|
|
|
|
<!-- Remove Button -->
|
|
<NcButton
|
|
v-if="!disabled"
|
|
type="text"
|
|
size="small"
|
|
class="nc-filter-item-remove-btn self-center"
|
|
@click="removeCondition(index)"
|
|
>
|
|
<GeneralIcon icon="deleteListItem" />
|
|
</NcButton>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
|
|
<!-- Add condition button -->
|
|
<div v-if="canAddCondition" class="nc-condition-builder-add">
|
|
<NcButton type="text" size="small" :disabled="disabled" @click="addCondition">
|
|
<template #icon>
|
|
<GeneralIcon icon="ncPlus" class="w-4 h-4" />
|
|
</template>
|
|
Add condition
|
|
</NcButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</NcListDropdown>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.nc-condition-builder-dropdown {
|
|
@apply !min-w-[560px] !max-w-[700px];
|
|
|
|
.ant-dropdown-menu {
|
|
@apply !p-0;
|
|
}
|
|
}
|
|
|
|
.nc-dropdown-filter-property {
|
|
@apply !min-w-[180px];
|
|
}
|
|
</style>
|
|
|
|
<style scoped lang="scss">
|
|
.nc-condition-builder {
|
|
@apply w-full;
|
|
|
|
.nc-condition-builder-dropdown-container {
|
|
@apply max-h-[400px] overflow-y-auto;
|
|
}
|
|
|
|
.nc-condition-builder-add {
|
|
@apply self-start;
|
|
}
|
|
}
|
|
|
|
.nc-filter-wrapper {
|
|
@apply bg-nc-bg-default !rounded-lg border-1px border-nc-border-gray-medium w-full items-center;
|
|
|
|
& > *,
|
|
.nc-filter-field-input {
|
|
@apply !border-none;
|
|
}
|
|
|
|
& > :not(:last-child):not(:empty) {
|
|
border-right: 1px solid var(--nc-border-gray-medium) !important;
|
|
border-bottom-right-radius: 0 !important;
|
|
border-top-right-radius: 0 !important;
|
|
}
|
|
|
|
& > :not(:first-child) {
|
|
border-bottom-left-radius: 0 !important;
|
|
border-top-left-radius: 0 !important;
|
|
}
|
|
|
|
& > :last-child {
|
|
@apply relative;
|
|
&::after {
|
|
content: '';
|
|
@apply absolute h-full w-1px bg-nc-border-gray-medium -left-1px top-0;
|
|
}
|
|
}
|
|
|
|
:deep(::placeholder) {
|
|
@apply text-sm tracking-normal;
|
|
}
|
|
|
|
:deep(::-ms-input-placeholder) {
|
|
@apply text-sm tracking-normal;
|
|
}
|
|
|
|
:deep(input) {
|
|
@apply text-sm;
|
|
}
|
|
|
|
:deep(.nc-select:not(.ant-select-disabled):hover) {
|
|
&,
|
|
.ant-select-selector {
|
|
@apply bg-nc-bg-gray-extralight;
|
|
}
|
|
}
|
|
|
|
:deep(.nc-select),
|
|
:deep(.nc-select.ant-select) {
|
|
.ant-select-selector {
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
border-radius: 0 !important;
|
|
}
|
|
|
|
.ant-select-disabled {
|
|
border-radius: 0 !important;
|
|
}
|
|
|
|
// Hide search icon when using show-search
|
|
&.ant-select-show-search .ant-select-selector .ant-select-selection-search-input {
|
|
@apply !h-full;
|
|
}
|
|
}
|
|
|
|
:deep(.ant-select-selector) {
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
border-radius: 0 !important;
|
|
}
|
|
|
|
:deep(.ant-input) {
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
border-radius: 0 !important;
|
|
}
|
|
}
|
|
|
|
.nc-filter-where-label {
|
|
@apply text-nc-content-gray-disabled pl-3;
|
|
}
|
|
|
|
.nc-filter-item-remove-btn {
|
|
@apply text-nc-content-gray-subtle2 hover:text-nc-content-gray;
|
|
}
|
|
|
|
.nc-filter-field-input {
|
|
.nc-property-mode-toggle {
|
|
@apply flex-shrink-0 !border-r-1;
|
|
}
|
|
|
|
:deep(.nc-select),
|
|
:deep(.nc-select.ant-select) {
|
|
@apply h-8;
|
|
|
|
.ant-select-selector {
|
|
@apply !h-8 !min-h-8;
|
|
border: none !important;
|
|
box-shadow: none !important;
|
|
border-radius: 0 !important;
|
|
}
|
|
|
|
.ant-select-arrow {
|
|
@apply text-nc-content-gray-muted;
|
|
}
|
|
|
|
.ant-select-clear {
|
|
@apply bg-nc-bg-default;
|
|
}
|
|
}
|
|
|
|
:deep(.nc-workflow-input) {
|
|
.ProseMirror {
|
|
@apply !h-8 !min-h-8 border-none !py-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
.nc-filter-value-input {
|
|
:deep(.nc-workflow-input) {
|
|
.ProseMirror {
|
|
@apply !h-8 !min-h-8 border-none !py-1 !pr-8;
|
|
}
|
|
|
|
.nc-workflow-input-insert-btn {
|
|
@apply !-top-0.5;
|
|
}
|
|
}
|
|
}
|
|
|
|
:deep(.nc-workflow-input) {
|
|
.ProseMirror {
|
|
@apply !h-8 !min-h-8 border-none !py-1;
|
|
}
|
|
|
|
.nc-workflow-input-insert-btn {
|
|
@apply !-top-0.5;
|
|
}
|
|
}
|
|
|
|
:deep(.ant-select-selector) {
|
|
@apply !min-h-8;
|
|
}
|
|
</style>
|