Skip to content

Commit

Permalink
Add support for customizable Snap UI Input
Browse files Browse the repository at this point in the history
  • Loading branch information
david0xd committed Sep 4, 2024
1 parent ddf93de commit 890a0ad
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 13 deletions.
55 changes: 42 additions & 13 deletions ui/components/app/snaps/snap-ui-renderer/components/field.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import {
FieldElement,
InputElement,
ButtonElement,
JSXElement,
DropdownElement,
RadioGroupElement,
CheckboxElement,
SelectorElement,
} from '@metamask/snaps-sdk/jsx';
import { getJsxChildren } from '@metamask/snaps-utils';
import { button as buttonFn } from './button';
import { getPrimaryChildElementIndex, mapToTemplate } from '../utils';
import { dropdown as dropdownFn } from './dropdown';
import { radioGroup as radioGroupFn } from './radioGroup';
import { checkbox as checkboxFn } from './checkbox';
Expand All @@ -23,7 +22,10 @@ export const field: UIComponentFactory<FieldElement> = ({
}) => {
// For fields we don't render the Input itself, we just adapt SnapUIInput.
const children = getJsxChildren(element);
const child = children[0] as JSXElement;
const primaryChildIndex = getPrimaryChildElementIndex(
children as JSXElement[],
);
const child = children[primaryChildIndex] as JSXElement;

switch (child.type) {
case 'FileInput': {
Expand All @@ -42,14 +44,34 @@ export const field: UIComponentFactory<FieldElement> = ({
}

case 'Input': {
const input = child as InputElement;
const button = children[1] as ButtonElement;
const buttonMapped =
button &&
buttonFn({
const getLeftAccessory = () => {
return mapToTemplate({
...params,
element: children[0] as JSXElement,
});
};

const getRightAccessory = (accessoryIndex: number) => {
return mapToTemplate({
...params,
element: button,
} as UIComponentParams<ButtonElement>);
element: children[accessoryIndex] as JSXElement,
});
};

const input = child as InputElement;

const leftAccessoryMapped =
primaryChildIndex > 0 ? getLeftAccessory() : undefined;

let rightAccessoryIndex: number | undefined;
if (children[2]) {
rightAccessoryIndex = 2;
} else if (primaryChildIndex === 0 && children[1]) {
rightAccessoryIndex = 1;
}
const rightAccessoryMapped = rightAccessoryIndex
? getRightAccessory(rightAccessoryIndex)
: undefined;

return {
element: 'SnapUIInput',
Expand All @@ -66,10 +88,17 @@ export const field: UIComponentFactory<FieldElement> = ({
helpText: element.props.error,
},
propComponents: {
endAccessory: buttonMapped && {
...buttonMapped,
startAccessory: leftAccessoryMapped && {
...leftAccessoryMapped,
props: {
...leftAccessoryMapped.props,
padding: 0,
},
},
endAccessory: rightAccessoryMapped && {
...rightAccessoryMapped,
props: {
...buttonMapped.props,
...rightAccessoryMapped.props,
padding: 0,
},
},
Expand Down
22 changes: 22 additions & 0 deletions ui/components/app/snaps/snap-ui-renderer/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,25 @@ export const mapTextToTemplate = (

return mapToTemplate({ ...params, element });
}) as NonEmptyArray<UIComponent | string>;

/**
* Registry of element types that are used within Field element.
*/
export const FIELD_ELEMENT_TYPES = [
'FileInput',
'Input',
'Dropdown',
'RadioGroup',
'Checkbox',
'Selector',
];

/**
* Search for the element that is considered to be primary child element of a Field.
*
* @param children - Children elements specified within Field element.
* @returns Number, representing index of a primary field in the array of children elements.
*/
export const getPrimaryChildElementIndex = (children: JSXElement[]) => {
return children.findIndex((c) => FIELD_ELEMENT_TYPES.includes(c.type));
};

0 comments on commit 890a0ad

Please sign in to comment.