Skip to content

Commit 2629cea

Browse files
committed
Add ability to fliter input classes on each input component or globally on Sole.config.filterInputClass
1 parent 27d6276 commit 2629cea

File tree

10 files changed

+41
-16
lines changed

10 files changed

+41
-16
lines changed

src/components/CheckboxInput.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
type="checkbox"
88
:checked="modelValue"
99
@input="$emit('update:modelValue', (($event as InputEvent).target as HTMLInputElement).checked)"
10-
:class="['focus:ring-indigo-500 h-4 w-4 text-indigo-600 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-800',inputClass]"
10+
:class="cls"
1111
v-bind="omit($attrs, ['class'])">
1212
</div>
1313
<div class="ml-3 text-sm">
@@ -29,6 +29,7 @@ import type { ApiState } from "../types"
2929
import type { CheckboxInputProps, CheckboxInputEmits } from '@/components/types'
3030
import { errorResponse, humanize, omit, toPascalCase } from "@servicestack/client"
3131
import { computed, inject } from "vue"
32+
import { filterClass } from "./css";
3233
3334
const props = defineProps<CheckboxInputProps>()
3435
@@ -38,4 +39,6 @@ const useLabel = computed(() => props.label ?? humanize(toPascalCase(props.id)))
3839
3940
let ctx: ApiState|undefined = inject('ApiState', undefined)
4041
const errorField = computed(() => errorResponse.call({ responseStatus: props.status ?? ctx?.error.value }, props.id))
42+
43+
const cls = computed(() => filterClass(['focus:ring-indigo-500 h-4 w-4 text-indigo-600 rounded border-gray-300 dark:border-gray-600 dark:bg-gray-800',props.inputClass], 'CheckboxInput', props.filterClass))
4144
</script>

src/components/FileInput.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { computed, inject, onUnmounted, ref } from 'vue'
6363
import { errorResponse, humanize, lastLeftPart, lastRightPart, toPascalCase } from '@servicestack/client'
6464
import { useConfig } from '@/use/config'
6565
import { filePathUri, getMimeType, formatBytes, fileImageUri, flush } from '@/use/files'
66+
import { filterClass } from './css'
6667
6768
const props = defineProps<FileInputProps>()
6869
@@ -92,10 +93,10 @@ const usePlaceholder = computed(() => props.placeholder ?? useLabel.value)
9293
let ctx: ApiState|undefined = inject('ApiState', undefined)
9394
const errorField = computed(() => errorResponse.call({ responseStatus: props.status ?? ctx?.error.value }, props.id))
9495
95-
const cls = computed(() => ['block w-full sm:text-sm rounded-md dark:text-white dark:bg-gray-900 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-violet-50 dark:file:bg-violet-900 file:text-violet-700 dark:file:text-violet-200 hover:file:bg-violet-100 dark:hover:file:bg-violet-800', errorField.value
96+
const cls = computed(() => filterClass(['block w-full sm:text-sm rounded-md dark:text-white dark:bg-gray-900 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-violet-50 dark:file:bg-violet-900 file:text-violet-700 dark:file:text-violet-200 hover:file:bg-violet-100 dark:hover:file:bg-violet-800', errorField.value
9697
? 'pr-10 border-red-300 text-red-900 placeholder-red-300 focus:outline-none focus:ring-red-500 focus:border-red-500'
9798
: 'text-slate-500 dark:text-slate-400'
98-
, props.inputClass])
99+
, props.inputClass], 'FileInput', props.filterClass))
99100
100101
const onChange = (e:Event) => {
101102
let f = e.target as HTMLInputElement

src/components/MarkdownInput.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
<script setup lang="ts">
7272
import type { MarkdownInputProps, MarkdownInputEmits } from '@/components/types'
7373
import { computed, inject, nextTick, onMounted, ref, getCurrentInstance } from 'vue'
74-
import { input } from "./css"
74+
import { filterClass, input } from "./css"
7575
import { errorResponse, humanize, toPascalCase } from "@servicestack/client"
7676
import type { ApiState, MarkdownInputOptions, ResponseStatus } from '@/types'
7777
import { asOptions } from '@/use/utils'
@@ -96,9 +96,9 @@ const showOptions = computed<{[k:string]:boolean}>(() => props.hide ? asOptions
9696
function show(target:MarkdownInputOptions) { return showOptions.value[target] }
9797
9898
99-
const cls = computed(() => ['shadow-sm font-mono' + input.base.replace('rounded-md',''), errorField.value
99+
const cls = computed(() => filterClass(['shadow-sm font-mono' + input.base.replace('rounded-md',''), errorField.value
100100
? 'text-red-900 focus:ring-red-500 focus:border-red-500 border-red-300'
101-
: 'text-gray-900 ' + input.valid, props.inputClass])
101+
: 'text-gray-900 ' + input.valid, props.inputClass], 'MarkdownInput', props.filterClass))
102102
const btnCls = "w-5 h-5 cursor-pointer select-none text-gray-700 dark:text-gray-300 hover:text-indigo-600 dark:hover:text-indigo-400"
103103
104104
const txt = ref()

src/components/SelectInput.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
<template>
22
<div :class="[$attrs.class]">
33
<label v-if="useLabel" :for="id" :class="`block text-sm font-medium text-gray-700 dark:text-gray-300 ${labelClass??''}`">{{ useLabel }}</label>
4-
<select :id="id" :name="id" :class="['mt-1 block w-full pl-3 pr-10 py-2 text-base focus:outline-none sm:text-sm rounded-md dark:text-white dark:bg-gray-900 dark:border-gray-600 disabled:bg-slate-50 dark:disabled:bg-slate-900 disabled:text-slate-500 disabled:border-slate-200 dark:disabled:border-slate-700 disabled:shadow-none',
5-
!errorField ? 'shadow-sm border-gray-300 text-gray-900 focus:ring-indigo-500 focus:border-indigo-500' : 'border-red-300 text-red-900 focus:ring-red-500 focus:border-red-500',inputClass]"
4+
<select :id="id" :name="id" :class="cls"
65
:value="modelValue"
76
@input="$emit('update:modelValue', value($event.target))"
87
:aria-invalid="errorField != null"
@@ -25,6 +24,7 @@ import type { ApiState } from "@/types"
2524
import type { SelectInputProps } from '@/components/types'
2625
import { computed, inject } from "vue"
2726
import { errorResponse, humanize, omit, toPascalCase } from "@servicestack/client"
27+
import { filterClass } from "./css";
2828
2929
const value = (e:EventTarget|null) => (e as HTMLSelectElement).value //workaround IDE type-check error
3030
@@ -40,4 +40,7 @@ const kvpValues = computed(() => props.entries || (props.values
4040
: props.options
4141
? Object.keys(props.options).map(key => ({ key, value:props.options[key] }))
4242
: []))
43+
44+
const cls = computed(() => filterClass(['mt-1 block w-full pl-3 pr-10 py-2 text-base focus:outline-none sm:text-sm rounded-md dark:text-white dark:bg-gray-900 dark:border-gray-600 disabled:bg-slate-50 dark:disabled:bg-slate-900 disabled:text-slate-500 disabled:border-slate-200 dark:disabled:border-slate-700 disabled:shadow-none',
45+
!errorField ? 'shadow-sm border-gray-300 text-gray-900 focus:ring-indigo-500 focus:border-indigo-500' : 'border-red-300 text-red-900 focus:ring-red-500 focus:border-red-500',props.inputClass], 'SelectInput', props.filterClass))
4346
</script>

src/components/TagInput.vue

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
<div v-if="errorField" class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
4646
<svg class="h-5 w-5 text-red-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
47-
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
47+
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
4848
</svg>
4949
</div>
5050
</div>
@@ -65,6 +65,7 @@ import type { ApiState } from "@/types"
6565
import type { TagInputProps, TagInputEmits } from '@/components/types'
6666
import { $1, errorResponse, humanize, map, omit, toPascalCase, trimEnd } from "@servicestack/client"
6767
import { computed, inject, ref } from "vue"
68+
import { filterClass } from "./css";
6869
6970
const props = withDefaults(defineProps<TagInputProps>(), {
7071
modelValue: () => [],
@@ -104,10 +105,10 @@ const useLabel = computed(() => props.label ?? humanize(toPascalCase(props.id)))
104105
let ctx: ApiState|undefined = inject('ApiState', undefined)
105106
const errorField = computed(() => errorResponse.call({ responseStatus: props.status ?? ctx?.error.value }, props.id))
106107
107-
const cls = computed(() => ['w-full cursor-text flex flex-wrap sm:text-sm rounded-md dark:text-white dark:bg-gray-900 border focus-within:border-transparent focus-within:ring-1 focus-within:outline-none', errorField.value
108+
const cls = computed(() => filterClass(['w-full cursor-text flex flex-wrap sm:text-sm rounded-md dark:text-white dark:bg-gray-900 border focus-within:border-transparent focus-within:ring-1 focus-within:outline-none', errorField.value
108109
? 'pr-10 border-red-300 text-red-900 placeholder-red-300 focus-within:outline-none focus-within:ring-red-500 focus-within:border-red-500'
109110
: 'shadow-sm border-gray-300 dark:border-gray-600 focus-within:ring-indigo-500 focus-within:border-indigo-500'
110-
, props.inputClass])
111+
, props.inputClass], 'TagInput', props.filterClass))
111112
112113
const removeTag = (tag:string) => updateValue(modelArray.value.filter(x => x != tag))
113114

src/components/TextInput.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import type { ApiState } from "@/types"
3939
import type { TextInputProps, TextInputExpose } from '@/components/types'
4040
import { errorResponse, humanize, omit, toPascalCase } from "@servicestack/client"
4141
import { computed, inject, ref } from "vue"
42-
import { input } from './css'
42+
import { input, filterClass } from './css'
4343
import { textInputValue } from '@/use/utils'
4444
4545
const value = (e:EventTarget|null) => (e as HTMLInputElement).value //workaround IDE type-check error
@@ -68,5 +68,5 @@ function fixShadow(cls:string) {
6868
let ctx: ApiState|undefined = inject('ApiState', undefined)
6969
const errorField = computed(() => errorResponse.call({ responseStatus: props.status ?? ctx?.error.value }, props.id))
7070
71-
const cls = computed(() => [input.base, errorField.value ? input.invalid : fixShadow(input.valid), props.inputClass])
71+
const cls = computed(() => filterClass([input.base, errorField.value ? input.invalid : fixShadow(input.valid), props.inputClass], 'TextInput', props.filterClass))
7272
</script>

src/components/TextareaInput.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import type { ApiState } from "@/types"
2828
import type { TextareaInputProps } from '@/components/types'
2929
import { errorResponse, humanize, omit, toPascalCase } from "@servicestack/client"
3030
import { computed, inject } from "vue"
31-
import { input } from "./css"
31+
import { input, filterClass } from "./css"
3232
3333
const value = (e:EventTarget|null) => (e as HTMLInputElement).value //workaround IDE type-check error
3434
@@ -40,7 +40,7 @@ const usePlaceholder = computed(() => props.placeholder ?? useLabel.value)
4040
let ctx: ApiState|undefined = inject('ApiState', undefined)
4141
const errorField = computed(() => errorResponse.call({ responseStatus: props.status ?? ctx?.error.value }, props.id))
4242
43-
const cls = computed(() => ['shadow-sm ' + input.base, errorField.value
43+
const cls = computed(() => filterClass(['shadow-sm ' + input.base, errorField.value
4444
? 'text-red-900 focus:ring-red-500 focus:border-red-500 border-red-300'
45-
: 'text-gray-900 ' + input.valid, props.inputClass])
45+
: 'text-gray-900 ' + input.valid, props.inputClass], 'TextareaInput', props.filterClass))
4646
</script>

src/components/css.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { TableStyleOptions, FormStyle, TableStyle } from '@/types'
2+
import { Sole } from '@/use/config'
23

34
function hasTableStyle(style:TableStyleOptions, target:TableStyle) {
45
return Array.isArray(style)
@@ -100,3 +101,11 @@ export const grid = {
100101
export const dummy = {
101102
colspans: "col-span-3 sm:col-span-3"
102103
}
104+
105+
export function filterClass(cls:(string|undefined)[], type:string, fn?:((cls:string) => string)) {
106+
const joinCls = cls.filter(x => x).join(' ')
107+
fn ??= (Sole.config.filterInputClass == null ? undefined : cls => Sole.config.filterInputClass!(cls, type))
108+
return fn
109+
? fn(joinCls)
110+
: joinCls
111+
}

src/components/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface TextInputProps {
1414
id: string
1515
type?: string
1616
inputClass?: string
17+
filterClass?:(cls:string) => string
1718
label?: string
1819
labelClass?: string
1920
help?: string
@@ -28,6 +29,7 @@ export interface TextareaInputProps {
2829
status?: ResponseStatus|null
2930
id: string
3031
inputClass?: string
32+
filterClass?:(cls:string) => string
3133
label?: string
3234
labelClass?: string
3335
help?: string
@@ -40,6 +42,7 @@ export interface SelectInputProps {
4042
id: string
4143
modelValue?: string
4244
inputClass?: string
45+
filterClass?:(cls:string) => string
4346
label?: string
4447
labelClass?: string
4548
options?: any
@@ -74,6 +77,7 @@ export interface CheckboxInputProps {
7477
status?: ResponseStatus
7578
id: string
7679
inputClass?: string
80+
filterClass?:(cls:string) => string
7781
label?: string
7882
labelClass?: string
7983
help?: string
@@ -85,6 +89,7 @@ export interface FileInputProps {
8589
status?: ResponseStatus|null
8690
id: string
8791
inputClass?: string
92+
filterClass?:(cls:string) => string
8893
label?: string
8994
labelClass?: string
9095
help?: string
@@ -190,6 +195,7 @@ export interface TagInputProps {
190195
id: string
191196
type?: string
192197
inputClass?: string
198+
filterClass?:(cls:string) => string
193199
label?: string
194200
labelClass?: string
195201
help?: string
@@ -570,6 +576,7 @@ export interface MarkdownInputProps {
570576
status?: ResponseStatus|null
571577
id: string
572578
inputClass?: string
579+
filterClass?:(cls:string) => string
573580
label?: string
574581
labelClass?: string
575582
help?: string

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export interface UiConfig {
109109
apiResolver?:(name:string) => MetadataOperationType|null
110110
typeResolver?:(name:string,namespace?:string|null) => MetadataType|null
111111
inputValue?:(type:string,value:any) => string|null
112+
filterInputClass?:(cls:string, type:string) => string,
112113
autoQueryGridDefaults?: AutoQueryGridDefaults
113114
storage?:Storage
114115
tableIcon?:ImageInfo

0 commit comments

Comments
 (0)