-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add x-mark-icon * ItemList component * Move `ItemRow` to a separate file * Fix svelte ci failure * Use <for> control flow * Refactor type and rename onChange handlers to handle... * Tweaks for mitosis parsing * Use ItemList component * Remove array parsing for redirectUrl * Make ItemList handler generic for any field, fix around mitosis gotchas * Use ItemList component elsewhere * Update button text * Disable delete button for first item * Validate duplicate entries * Pass errorCallback * Label support, Display duplicate entry error inline, style tweaks, disable delete if only 1 item remains * Style error text * Tweak prop name * Style fixes and use shared Button component * Pass label, type and fix hint text * Style tweaks for delete button * Warning triangle plus style tweaks for error message * Adjust font-size * Export `ItemList` for external use
- Loading branch information
Showing
12 changed files
with
281 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { SVGProps } from '../types'; | ||
|
||
export default function XMarkIcon(props: { svgAttrs?: SVGProps }) { | ||
return ( | ||
<svg fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor' {...props.svgAttrs}> | ||
<path stroke-linecap='round' stroke-linejoin='round' d='M6 18 18 6M6 6l12 12' /> | ||
</svg> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export { default as ClipboardButton } from './ClipboardButton/index.lite'; | ||
export { default as InputWithCopyButton } from './inputs/InputWithCopyButton/index.lite'; | ||
export { default as Themer } from './Themer/index.lite'; | ||
export { default as ItemList } from './inputs/ItemList/index.lite'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import XMarkIcon from '../../icons/XMarkIcon.lite'; | ||
import styles from '../index.module.css'; | ||
import itemStyles from './index.module.css'; | ||
|
||
type ItemRowProps = { | ||
inputType: 'text' | 'url' | 'number' | 'password'; | ||
item: string; | ||
index: number; | ||
handleItemUpdate: (newItem: string, index: number) => void; | ||
handleItemDelete: (index: number) => void; | ||
handleBlur: (index: number) => void; | ||
isDuplicateItem?: boolean; | ||
disableDelete?: boolean; | ||
disabled?: boolean; | ||
classNames: { input: string }; | ||
}; | ||
|
||
export default function ItemRow(props: ItemRowProps) { | ||
return ( | ||
<div class={itemStyles.row}> | ||
<input | ||
type={props.inputType || 'text'} | ||
class={`${props.classNames.input} ${styles['input-sm']} ${itemStyles['input']}`} | ||
name='item' | ||
value={props.item} | ||
onChange={(event) => props.handleItemUpdate(event.target.value, props.index)} | ||
onBlur={(event) => props.handleBlur(props.index)} | ||
required | ||
disabled={props.disabled} | ||
/> | ||
<button | ||
type='button' | ||
onClick={(event) => props.handleItemDelete(props.index)} | ||
disabled={props.disableDelete}> | ||
<XMarkIcon svgAttrs={{ class: itemStyles['svg'] }} /> | ||
</button> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import { For, Show, useStore } from '@builder.io/mitosis'; | ||
import ItemRow from './ItemRow.lite'; | ||
import styles from '../index.module.css'; | ||
import listStyles from './index.module.css'; | ||
import cssClassAssembler from '../../../sso/utils/cssClassAssembler'; | ||
import Button from '../../Button/index.lite'; | ||
import ExclamationTriangle from '../../icons/ExclamationTriangle.lite'; | ||
|
||
type ItemListProps = { | ||
label: string; | ||
inputType: 'text' | 'url' | 'number' | 'password'; | ||
classNames?: { label?: string; input?: string }; | ||
currentlist: string | string[]; | ||
fieldName: string; | ||
handleItemListUpdate: (fieldName: string, newList: string[]) => void; | ||
}; | ||
|
||
export default function ItemList(props: ItemListProps) { | ||
const state = useStore({ | ||
duplicateEntryIndex: undefined as undefined | number, | ||
get list() { | ||
return Array.isArray(props.currentlist) ? props.currentlist : [props.currentlist]; | ||
}, | ||
addAnother: () => { | ||
props.handleItemListUpdate(props.fieldName, [...state.list, '']); | ||
}, | ||
handleItemUpdate: (newItem: string, index: number) => { | ||
const newList = [...state.list]; | ||
newList[index] = newItem; | ||
props.handleItemListUpdate(props.fieldName, newList); | ||
}, | ||
checkDuplicates(index: number) { | ||
const _item = state.list[index]; | ||
// search backwards | ||
const _firstIndex = state.list.indexOf(_item); | ||
if (_firstIndex !== index) { | ||
state.duplicateEntryIndex = index; | ||
return; | ||
} else if (state.duplicateEntryIndex === index) { | ||
state.duplicateEntryIndex = undefined; | ||
} | ||
// search forwards | ||
const _nextIndex = state.list.slice(index + 1).indexOf(_item); | ||
if (_nextIndex !== -1) { | ||
state.duplicateEntryIndex = index; | ||
} else if (state.duplicateEntryIndex === index) { | ||
state.duplicateEntryIndex = undefined; | ||
} | ||
}, | ||
handleItemDelete: (index: number) => { | ||
const _itemToDelete = state.list[index]; | ||
if ( | ||
state.duplicateEntryIndex !== undefined && | ||
(state.duplicateEntryIndex === index || _itemToDelete === state.list[state.duplicateEntryIndex]) | ||
) { | ||
state.duplicateEntryIndex = undefined; | ||
} | ||
props.handleItemListUpdate( | ||
props.fieldName, | ||
state.list.filter((_, i) => i !== index) | ||
); | ||
}, | ||
get cssClass() { | ||
return { | ||
label: cssClassAssembler(props.classNames?.label, styles.label), | ||
input: cssClassAssembler(props.classNames?.input, styles.input), | ||
}; | ||
}, | ||
}); | ||
|
||
return ( | ||
<fieldset class={styles.fieldset}> | ||
<legend class={state.cssClass.label}>{props.label}</legend> | ||
<div class={listStyles.rowContainer}> | ||
<For each={state.list}> | ||
{(item, index) => ( | ||
<div key={index}> | ||
<ItemRow | ||
inputType={props.inputType} | ||
item={item} | ||
index={index} | ||
isDuplicateItem={state.duplicateEntryIndex === index} | ||
handleItemUpdate={state.handleItemUpdate} | ||
handleItemDelete={state.handleItemDelete} | ||
disableDelete={index === 0 && state.list.length === 1} | ||
handleBlur={state.checkDuplicates} | ||
disabled={state.duplicateEntryIndex !== undefined && state.duplicateEntryIndex !== index} | ||
classNames={{ input: state.cssClass.input }} | ||
/> | ||
<Show when={state.duplicateEntryIndex === index}> | ||
<span class={listStyles.error}> | ||
<ExclamationTriangle svgAttrs={{ class: listStyles['svg'], 'aria-hidden': true }} /> | ||
Duplicate entries not allowed. | ||
</span> | ||
</Show> | ||
</div> | ||
)} | ||
</For> | ||
<div> | ||
<Button | ||
type='button' | ||
variant='outline' | ||
classNames={listStyles['add']} | ||
onClick={(event) => state.addAnother()} | ||
name='Add URL' | ||
disabled={state.duplicateEntryIndex !== undefined} | ||
/> | ||
</div> | ||
</div> | ||
</fieldset> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
.rowContainer { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
margin-top: 0.5rem; | ||
} | ||
|
||
.error { | ||
margin-top: 0.25rem; | ||
display: flex; | ||
align-items: center; | ||
gap: 0.5rem; | ||
font-size: 0.75rem; | ||
line-height: 0.5rem; | ||
color: #ef4444; | ||
} | ||
|
||
.row { | ||
display: flex; | ||
align-items: center; | ||
gap: 0.75rem; | ||
} | ||
|
||
.input { | ||
flex: 1; | ||
font-size: 0.875rem !important; | ||
} | ||
|
||
.add { | ||
height: 2rem; | ||
} | ||
|
||
.svg { | ||
height: 1.25rem; | ||
width: 1.25rem; | ||
color: #ef4444; | ||
} | ||
|
||
:disabled .svg { | ||
color: grey; | ||
cursor: not-allowed; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.