@tellescope/react-components 1.232.0 → 1.232.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cjs/Forms/forms.v2.d.ts +1 -1
- package/lib/cjs/Forms/hooks.d.ts.map +1 -1
- package/lib/cjs/Forms/hooks.js +24 -0
- package/lib/cjs/Forms/hooks.js.map +1 -1
- package/lib/cjs/Forms/inputs.d.ts +4 -1
- package/lib/cjs/Forms/inputs.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.js +100 -26
- package/lib/cjs/Forms/inputs.js.map +1 -1
- package/lib/cjs/Forms/inputs.v2.d.ts +5 -7
- package/lib/cjs/Forms/inputs.v2.d.ts.map +1 -1
- package/lib/cjs/Forms/inputs.v2.js +7 -234
- package/lib/cjs/Forms/inputs.v2.js.map +1 -1
- package/lib/esm/CMS/components.d.ts +0 -1
- package/lib/esm/CMS/components.d.ts.map +1 -1
- package/lib/esm/Forms/form_responses.d.ts +0 -1
- package/lib/esm/Forms/form_responses.d.ts.map +1 -1
- package/lib/esm/Forms/forms.d.ts +3 -3
- package/lib/esm/Forms/forms.v2.d.ts +4 -4
- package/lib/esm/Forms/hooks.d.ts +0 -1
- package/lib/esm/Forms/hooks.d.ts.map +1 -1
- package/lib/esm/Forms/hooks.js +24 -0
- package/lib/esm/Forms/hooks.js.map +1 -1
- package/lib/esm/Forms/inputs.d.ts +5 -2
- package/lib/esm/Forms/inputs.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.js +101 -27
- package/lib/esm/Forms/inputs.js.map +1 -1
- package/lib/esm/Forms/inputs.v2.d.ts +6 -8
- package/lib/esm/Forms/inputs.v2.d.ts.map +1 -1
- package/lib/esm/Forms/inputs.v2.js +7 -234
- package/lib/esm/Forms/inputs.v2.js.map +1 -1
- package/lib/esm/controls.d.ts +2 -2
- package/lib/esm/inputs.d.ts +1 -1
- package/lib/esm/inputs.native.d.ts +0 -1
- package/lib/esm/inputs.native.d.ts.map +1 -1
- package/lib/esm/state.d.ts +315 -315
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/Forms/hooks.tsx +33 -5
- package/src/Forms/inputs.tsx +151 -29
- package/src/Forms/inputs.v2.tsx +9 -299
package/src/Forms/inputs.v2.tsx
CHANGED
|
@@ -1754,310 +1754,20 @@ export const DropdownInput = ({ field, value, onChange }: FormInputProps<'Dropdo
|
|
|
1754
1754
|
)
|
|
1755
1755
|
}
|
|
1756
1756
|
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
records: DatabaseRecord[],
|
|
1761
|
-
lastId?: string,
|
|
1762
|
-
} | {
|
|
1763
|
-
done: undefined,
|
|
1764
|
-
records: undefined,
|
|
1765
|
-
lastId?: string,
|
|
1766
|
-
}
|
|
1767
|
-
} = {}
|
|
1768
|
-
const preventRefetch: Record<string, boolean> = {}
|
|
1769
|
-
|
|
1770
|
-
const LOAD_CHOICES_LIMIT = 500
|
|
1771
|
-
const useDatabaseChoices = ({ databaseId='', field, otherAnswers } : { databaseId?: string, field: FormField, otherAnswers?: DatabaseSelectResponse[] }) => {
|
|
1772
|
-
const session = useResolvedSession()
|
|
1773
|
-
const [renderCount, setRenderCount] = useState(0)
|
|
1774
|
-
|
|
1775
|
-
// todo: make searchable, don't load all
|
|
1776
|
-
useEffect(() => {
|
|
1777
|
-
if (choicesForDatabase[databaseId]?.done) return
|
|
1778
|
-
if (renderCount > 100) return // limit to 50000 entries / prevent infinite looping
|
|
1779
|
-
const choices = choicesForDatabase[databaseId]?.records ?? []
|
|
1780
|
-
const lastId = choicesForDatabase[databaseId]?.lastId
|
|
1781
|
-
|
|
1782
|
-
if (preventRefetch[databaseId + field.id + lastId]) return
|
|
1783
|
-
preventRefetch[databaseId + field.id + lastId] = true
|
|
1784
|
-
|
|
1785
|
-
session.api.form_fields.load_choices_from_database({
|
|
1786
|
-
fieldId: field.id,
|
|
1787
|
-
lastId,
|
|
1788
|
-
limit: LOAD_CHOICES_LIMIT,
|
|
1789
|
-
databaseId, // overrides fieldId, supports using Database question in Table Input
|
|
1790
|
-
})
|
|
1791
|
-
.then(({ choices: newChoices }) => {
|
|
1792
|
-
choicesForDatabase[databaseId] = {
|
|
1793
|
-
lastId: newChoices?.[newChoices.length - 1]?.id,
|
|
1794
|
-
records: [...choices, ...newChoices]
|
|
1795
|
-
.sort((c1, c2) => (
|
|
1796
|
-
label_for_database_record(field, c1)
|
|
1797
|
-
.localeCompare(label_for_database_record(field, c2))
|
|
1798
|
-
)
|
|
1799
|
-
),
|
|
1800
|
-
done: newChoices.length < LOAD_CHOICES_LIMIT,
|
|
1801
|
-
}
|
|
1802
|
-
setRenderCount(r => r + 1)
|
|
1803
|
-
})
|
|
1804
|
-
.catch(err => {
|
|
1805
|
-
console.error(err)
|
|
1806
|
-
preventRefetch[databaseId + field.id + lastId] = false
|
|
1807
|
-
})
|
|
1808
|
-
}, [session, field, databaseId, renderCount])
|
|
1809
|
-
|
|
1810
|
-
const addChoice = useCallback((record: DatabaseRecord) => {
|
|
1811
|
-
if (!choicesForDatabase[databaseId]) {
|
|
1812
|
-
choicesForDatabase[databaseId] = {
|
|
1813
|
-
done: false,
|
|
1814
|
-
records: [],
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
choicesForDatabase[databaseId].records!.push(record)
|
|
1818
|
-
}, [choicesForDatabase, databaseId])
|
|
1819
|
-
|
|
1820
|
-
return {
|
|
1821
|
-
addChoice,
|
|
1822
|
-
doneLoading: choicesForDatabase[databaseId]?.done ?? false,
|
|
1823
|
-
choices: [
|
|
1824
|
-
...choicesForDatabase[databaseId]?.records ?? [],
|
|
1825
|
-
...(otherAnswers || []).map(v => ({
|
|
1826
|
-
id: v.text,
|
|
1827
|
-
databaseId,
|
|
1828
|
-
values: [{ label: field.options?.databaseLabel || '', type: 'Text', value: v.text }],
|
|
1829
|
-
}) as Pick<DatabaseRecord, 'id' | 'values' | 'databaseId'>)
|
|
1830
|
-
],
|
|
1831
|
-
renderCount,
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
const label_for_database_record = (field: FormField, record?: Pick<DatabaseRecord, 'values'>) => {
|
|
1837
|
-
if (!record) return ''
|
|
1838
|
-
|
|
1839
|
-
const addedLabels = (
|
|
1840
|
-
(field.options?.databaseLabels || [])
|
|
1841
|
-
.map(l => record.values.find(v => v.label === l)?.value?.toString())
|
|
1842
|
-
.filter(v => v?.trim())
|
|
1843
|
-
) as string[]
|
|
1844
|
-
|
|
1845
|
-
return (
|
|
1846
|
-
(record.values.find(v => v.label === field.options?.databaseLabel)?.value?.toString() ?? '')
|
|
1847
|
-
+ (
|
|
1848
|
-
addedLabels.length
|
|
1849
|
-
? ` (${addedLabels.join(', ')})`
|
|
1850
|
-
: ''
|
|
1851
|
-
)
|
|
1852
|
-
)
|
|
1853
|
-
}
|
|
1757
|
+
// DatabaseSelectInput logic is shared with inputs.tsx to avoid duplication
|
|
1758
|
+
// Import the interface and component from the shared implementation
|
|
1759
|
+
import { AddToDatabaseProps as AddToDatabasePropsImported, DatabaseSelectInput as SharedDatabaseSelectInput } from './inputs'
|
|
1854
1760
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
const existing = (
|
|
1858
|
-
(_value || [])
|
|
1859
|
-
.filter(v => typeof v === 'string' || v.recordId === v.text)
|
|
1860
|
-
.map(v => typeof v === 'string' ? { databaseId: '', recordId: v, text: v } : v)
|
|
1861
|
-
)
|
|
1862
|
-
if (typing) {
|
|
1863
|
-
existing.push({ text: typing, databaseId: '', recordId: typing })
|
|
1864
|
-
}
|
|
1865
|
-
|
|
1866
|
-
return existing
|
|
1867
|
-
} catch(err) { console.error(err) }
|
|
1868
|
-
|
|
1869
|
-
return []
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
export interface AddToDatabaseProps {
|
|
1873
|
-
databaseId: string,
|
|
1874
|
-
onAdd: (record: DatabaseRecord) => void
|
|
1875
|
-
}
|
|
1761
|
+
// Re-export the interface for external use
|
|
1762
|
+
export type AddToDatabaseProps = AddToDatabasePropsImported
|
|
1876
1763
|
|
|
1877
|
-
|
|
1764
|
+
// Wrap the shared DatabaseSelectInput component with v2-specific props
|
|
1765
|
+
export const DatabaseSelectInput = (props: FormInputProps<'Database Select'> & {
|
|
1878
1766
|
responses: FormResponseValue[],
|
|
1879
1767
|
AddToDatabase?: React.JSXElementConstructor<AddToDatabaseProps>,
|
|
1880
1768
|
}) => {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
databaseId: field.options?.databaseId,
|
|
1884
|
-
field,
|
|
1885
|
-
otherAnswers: get_other_answers(_value, field?.options?.other ? typing : undefined),
|
|
1886
|
-
})
|
|
1887
|
-
|
|
1888
|
-
const value = React.useMemo(() => {
|
|
1889
|
-
try {
|
|
1890
|
-
// if the value is a string (some single answer that was save), make sure we coerce to array
|
|
1891
|
-
const __value = typeof _value === 'string' ? [_value] : _value
|
|
1892
|
-
return (
|
|
1893
|
-
(__value?.map(v =>
|
|
1894
|
-
choices.find(c =>
|
|
1895
|
-
c.id === v.recordId || (typeof v === 'string' && label_for_database_record(field, c) === v)
|
|
1896
|
-
)
|
|
1897
|
-
)?.filter(v => v!) ?? []) as DatabaseRecord[]
|
|
1898
|
-
)
|
|
1899
|
-
} catch(err) {
|
|
1900
|
-
console.error('Error resolving database answers for _value', err)
|
|
1901
|
-
return []
|
|
1902
|
-
}
|
|
1903
|
-
}, [_value, choices, field])
|
|
1904
|
-
|
|
1905
|
-
const filterResponse = useMemo(() => (
|
|
1906
|
-
field.options?.databaseFilter?.fieldId
|
|
1907
|
-
? responses.find(r => r.fieldId === field.options?.databaseFilter?.fieldId)?.answer?.value
|
|
1908
|
-
: undefined
|
|
1909
|
-
), [responses, field.options?.databaseFilter])
|
|
1910
|
-
|
|
1911
|
-
// State filtering logic similar to Insurance component
|
|
1912
|
-
const addressQuestion = useMemo(() => responses?.find(r => {
|
|
1913
|
-
if (r.answer.type !== 'Address') return false
|
|
1914
|
-
if (r.field.intakeField !== 'Address') return false
|
|
1915
|
-
|
|
1916
|
-
// make sure state is actually defined (in case of multiple address questions, where 1+ are blank)
|
|
1917
|
-
if (!r.answer.value?.state) return false
|
|
1918
|
-
|
|
1919
|
-
return true
|
|
1920
|
-
}), [responses])
|
|
1921
|
-
|
|
1922
|
-
const state = useMemo(() => (
|
|
1923
|
-
field.options?.filterByEnduserState
|
|
1924
|
-
? ((addressQuestion?.answer?.type === 'Address' ? addressQuestion?.answer?.value?.state : undefined) || enduser?.state)
|
|
1925
|
-
: undefined
|
|
1926
|
-
), [enduser?.state, addressQuestion, field.options?.filterByEnduserState])
|
|
1927
|
-
|
|
1928
|
-
const filteredChoicesWithPotentialDuplicates = useMemo(() => {
|
|
1929
|
-
if (!choices) return []
|
|
1930
|
-
if (!filterResponse) return choices
|
|
1931
|
-
if (!field?.options?.databaseFilter?.databaseLabel)
|
|
1932
|
-
if (!value || value.length === 0) return choices
|
|
1933
|
-
|
|
1934
|
-
return (
|
|
1935
|
-
choices
|
|
1936
|
-
.filter(c => {
|
|
1937
|
-
const v = c.values.find(_v => _v.label === field.options?.databaseFilter?.databaseLabel)?.value
|
|
1938
|
-
if (!v) return true
|
|
1939
|
-
|
|
1940
|
-
// use .text on r values to handle Database Select types as filter source (in addition to basic text and list of text)
|
|
1941
|
-
|
|
1942
|
-
if (typeof v === 'object') {
|
|
1943
|
-
return !!(
|
|
1944
|
-
Object.values(v).find(oVal => (
|
|
1945
|
-
typeof oVal === 'string' || typeof oVal === 'number'
|
|
1946
|
-
? (
|
|
1947
|
-
Array.isArray(filterResponse)
|
|
1948
|
-
? (filterResponse as any[]).find(r => r === oVal.toString() || (typeof r === 'object' && r.text === oVal))
|
|
1949
|
-
: (typeof filterResponse === 'string' || typeof filterResponse === 'number')
|
|
1950
|
-
? filterResponse.toString() === oVal.toString()
|
|
1951
|
-
: false
|
|
1952
|
-
)
|
|
1953
|
-
: false
|
|
1954
|
-
))
|
|
1955
|
-
)
|
|
1956
|
-
}
|
|
1957
|
-
|
|
1958
|
-
if (typeof v === 'string' || typeof v === 'number') {
|
|
1959
|
-
return !!(
|
|
1960
|
-
Array.isArray(filterResponse)
|
|
1961
|
-
? (filterResponse as any[]).find(r => r === v.toString() || (typeof r === 'object' && r.text === v))
|
|
1962
|
-
: (typeof filterResponse === 'string' || typeof filterResponse === 'number')
|
|
1963
|
-
? filterResponse.toString() === v.toString()
|
|
1964
|
-
: (typeof filterResponse === 'object' && (filterResponse as Address).city === v.toString()) ? true
|
|
1965
|
-
: (typeof filterResponse === 'object' && (filterResponse as Address).state === v.toString()) ? true
|
|
1966
|
-
: (typeof filterResponse === 'object' && (filterResponse as Address).zipCode === v.toString()) ? true
|
|
1967
|
-
: false
|
|
1968
|
-
)
|
|
1969
|
-
}
|
|
1970
|
-
|
|
1971
|
-
return false
|
|
1972
|
-
})
|
|
1973
|
-
)
|
|
1974
|
-
}, [choices, filterResponse, field.options?.databaseFilter, value])
|
|
1975
|
-
|
|
1976
|
-
// Apply state filtering as a secondary filter (doesn't modify existing logic)
|
|
1977
|
-
const stateFilteredChoices = useMemo(() => {
|
|
1978
|
-
if (!field.options?.filterByEnduserState || !state) {
|
|
1979
|
-
return filteredChoicesWithPotentialDuplicates
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
return filteredChoicesWithPotentialDuplicates.filter(c => {
|
|
1983
|
-
const recordState = c.values.find(v => v.label?.trim()?.toLowerCase() === 'state')?.value?.toString() || ''
|
|
1984
|
-
return !recordState || recordState === state
|
|
1985
|
-
})
|
|
1986
|
-
}, [filteredChoicesWithPotentialDuplicates, field.options?.filterByEnduserState, state])
|
|
1987
|
-
|
|
1988
|
-
const filteredChoices = useMemo(() => {
|
|
1989
|
-
const filtered = []
|
|
1990
|
-
|
|
1991
|
-
const uniques = new Set<string>([])
|
|
1992
|
-
for (const c of stateFilteredChoices) {
|
|
1993
|
-
const text = label_for_database_record(field, c)
|
|
1994
|
-
if (uniques.has(text)) continue // duplicate found
|
|
1995
|
-
|
|
1996
|
-
uniques.add(text)
|
|
1997
|
-
filtered.push(c)
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
return filtered
|
|
2001
|
-
}, [field, stateFilteredChoices])
|
|
2002
|
-
|
|
2003
|
-
if (!doneLoading) return <LinearProgress />
|
|
2004
|
-
return (
|
|
2005
|
-
<>
|
|
2006
|
-
<Autocomplete id={field.id} freeSolo={false} size={size}
|
|
2007
|
-
componentsProps={{ popper: { sx: { wordBreak: "break-word" } } } }
|
|
2008
|
-
options={filteredChoices} multiple={true}
|
|
2009
|
-
getOptionLabel={o => (
|
|
2010
|
-
Array.isArray(o) // edge case
|
|
2011
|
-
? ''
|
|
2012
|
-
: label_for_database_record(field, o)
|
|
2013
|
-
)}
|
|
2014
|
-
value={value}
|
|
2015
|
-
disabled={disabled}
|
|
2016
|
-
onChange={(_, v) => {
|
|
2017
|
-
if (v.length && onDatabaseSelect) {
|
|
2018
|
-
onDatabaseSelect(
|
|
2019
|
-
field.options?.radio
|
|
2020
|
-
? [v[v.length - 1]] // if radio, only last selected
|
|
2021
|
-
: v
|
|
2022
|
-
)
|
|
2023
|
-
}
|
|
2024
|
-
return onChange(
|
|
2025
|
-
(
|
|
2026
|
-
!field.options?.radio
|
|
2027
|
-
? v.map(_v => ({
|
|
2028
|
-
databaseId: field.options?.databaseId!,
|
|
2029
|
-
recordId: _v.id,
|
|
2030
|
-
text: label_for_database_record(field, _v),
|
|
2031
|
-
}))
|
|
2032
|
-
: [{
|
|
2033
|
-
databaseId: field.options?.databaseId!,
|
|
2034
|
-
recordId: v[v.length -1]?.id ?? '',
|
|
2035
|
-
text: label_for_database_record(field, v[v.length - 1]),
|
|
2036
|
-
}]
|
|
2037
|
-
),
|
|
2038
|
-
field.id,
|
|
2039
|
-
)
|
|
2040
|
-
}}
|
|
2041
|
-
inputValue={typing}
|
|
2042
|
-
onInputChange={(e, v) => e && setTyping(v)}
|
|
2043
|
-
renderInput={params => <TextField {...params} InputProps={{ ...params.InputProps, sx: defaultInputProps.sx }} />}
|
|
2044
|
-
// use custom Chip to ensure very long entries break properly (whitespace: normal)
|
|
2045
|
-
renderTags={(value, getTagProps) =>
|
|
2046
|
-
value.map((value, index) => (
|
|
2047
|
-
<Chip
|
|
2048
|
-
label={<Typography style={{whiteSpace: 'normal'}}>{Array.isArray(value) ? '' : label_for_database_record(field, value)}</Typography>}
|
|
2049
|
-
{...getTagProps({ index })}
|
|
2050
|
-
sx={{height:"100%", py: 0.5 }}
|
|
2051
|
-
/>
|
|
2052
|
-
))
|
|
2053
|
-
}
|
|
2054
|
-
/>
|
|
2055
|
-
|
|
2056
|
-
{AddToDatabase && field?.options?.allowAddToDatabase && (
|
|
2057
|
-
<AddToDatabase databaseId={field.options?.databaseId!} onAdd={addChoice} />
|
|
2058
|
-
)}
|
|
2059
|
-
</>
|
|
2060
|
-
)
|
|
1769
|
+
// Pass all props plus v2-specific defaultInputProps to the shared component
|
|
1770
|
+
return <SharedDatabaseSelectInput {...props} inputProps={defaultInputProps} />
|
|
2061
1771
|
}
|
|
2062
1772
|
|
|
2063
1773
|
type DisplayTermsResult = { displayTermsList: { term: string[] } }
|