diff --git a/player/react/src/lib/components/index.ts b/player/react/src/lib/components/index.ts index a94397b61..48cd0d611 100644 --- a/player/react/src/lib/components/index.ts +++ b/player/react/src/lib/components/index.ts @@ -20,6 +20,7 @@ import * as searchinput from './ui-search-input'; import * as text from './ui-text'; import * as textarea from './ui-text-area'; import * as textinput from './ui-text-input'; +import * as timepicker from './ui-time-picker'; import * as link from './ui-link'; import * as loading from './ui-loading'; import * as inlineLoading from './ui-inline-loading'; @@ -59,6 +60,7 @@ export const allComponents = { text, textarea, textinput, + timepicker, overflowMenu, // Tiles tile, diff --git a/player/react/src/lib/components/ui-time-picker.tsx b/player/react/src/lib/components/ui-time-picker.tsx new file mode 100644 index 000000000..e39c7a5f2 --- /dev/null +++ b/player/react/src/lib/components/ui-time-picker.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { TimePickerSelect, TimePicker, SelectItem } from '@carbon/react'; +import { commonSlots, slotsDisabled } from '../common-slots'; +import { SendSignal } from '../types'; + +export interface TimePickerState { + id: string | number; + codeContext?: { + name: string; + }; + type: string; + placeholder: string; + disabled?: boolean; + invalid?: boolean; + invalidText?: string; + light?: boolean; + size?: string; + value?: string; + label?: string; + timezones?: []; + isShowTimePeriod?: boolean; + timePeriod?: []; +} + +export const type = 'time-picker'; + +export const signals = ['valueChange', 'click', 'timezoneValueChange', 'timePeriodValueChange']; + +export const slots = { + ...commonSlots, + ...slotsDisabled, + invalid: 'boolean', + setInvalid: (state: TimePickerState) => ({ + ...state, + invalid: true + }), + setValid: (state: TimePickerState) => ({ + ...state, + invalid: false + }), + toggleInvalid: (state: TimePickerState) => ({ + ...state, + invalid: !state.invalid + }), + light: 'boolean', + setLight: (state: TimePickerState) => ({ + ...state, + light: true + }), + setDark: (state: TimePickerState) => ({ + ...state, + light: false + }), + toggleLight: (state: TimePickerState) => ({ + ...state, + light: !state.light + }), + placeholder: 'string', + type: 'string', + invalidText: 'string', + label: 'string', + size: 'string', + value: 'string' +}; + +export const UITimePicker = ({ state, sendSignal }: { + state: TimePickerState; + setState: (state: any) => void; + setGlobalState: (state: any) => void; + sendSignal: SendSignal; +}) => { + if (state.type !== 'time-picker') { + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>; + } + + return { + sendSignal(state.id, 'click'); + }} + onChange={(event: any) => { + sendSignal(state.id, 'valueChange', [event.value], { ...state, value: event.value }); + }}> + {state.isShowTimePeriod && + { + sendSignal(state.id, 'timePeriodValueChange', [event.value], { ...state, value: event.value }); + }}> + + + + } + {state.timezones?.length && + { + sendSignal(state.id, 'timezoneValueChange', [event.value], { ...state, value: event.value }); + }}> + { + state.timezones.map((step: any, index: number) => + ) + } + + } + ; +}; diff --git a/player/react/src/lib/utils.tsx b/player/react/src/lib/utils.tsx index 7c335518a..eea35a319 100644 --- a/player/react/src/lib/utils.tsx +++ b/player/react/src/lib/utils.tsx @@ -33,6 +33,7 @@ import { UITextAreaInput } from './components/ui-text-area'; import { UITextInput } from './components/ui-text-input'; import { UITile } from './components/ui-tile'; import { UITileFold } from './components/ui-tile-fold'; +import { UITimePicker } from './components/ui-time-picker'; import { UIToggle } from './components/ui-toggle'; import { kebabCase } from 'lodash'; import { SendSignal } from './types'; @@ -283,6 +284,9 @@ export const renderComponents = ( case 'text-input': return ; + case 'time-picker': + return ; + case 'overflow-menu': return ; diff --git a/sdk/react/src/lib/assets/component-icons/time-picker.svg b/sdk/react/src/lib/assets/component-icons/time-picker.svg new file mode 100644 index 000000000..68d9ebc69 --- /dev/null +++ b/sdk/react/src/lib/assets/component-icons/time-picker.svg @@ -0,0 +1,7 @@ + + + + + + hh:mm + \ No newline at end of file diff --git a/sdk/react/src/lib/fragment-components/a-time-picker.tsx b/sdk/react/src/lib/fragment-components/a-time-picker.tsx new file mode 100644 index 000000000..4e101c185 --- /dev/null +++ b/sdk/react/src/lib/fragment-components/a-time-picker.tsx @@ -0,0 +1,487 @@ +import React from 'react'; +import { AComponent, ComponentInfo } from './a-component'; +import image from './../assets/component-icons/time-picker.svg'; +import { TextInput, Checkbox, Dropdown, TimePicker, TimePickerSelect, SelectItem } from '@carbon/react'; +import { DraggableTileList, angularClassNamesFromComponentObj, nameStringToVariableString, reactClassNamesFromComponentObj } from '../helpers'; +import { css, cx } from 'emotion'; +import { styleObjectToString } from '@carbon-builder/player-react'; + +const preventCheckEventStyle = css` + pointer-events: none; +`; + +export const ATimePickerSettingsUI = ({ selectedComponent, setComponent }: any) => { + const size = [ + { id: 'sm', text: 'Small' }, + { id: 'md', text: 'Medium' }, + { id: 'lg', text: 'Large' } + ]; + + const updateListItems = (key: string, value: any, index: number) => { + const step = { + ...selectedComponent.timezones[index], + [key]: value + }; + setComponent({ + ...selectedComponent, + timezones: [ + ...selectedComponent.timezones.slice(0, index), + step, + ...selectedComponent.timezones.slice(index + 1) + ] + }); + }; + + const template = (item: any, index: number) => { + return <> + { + updateListItems('text', event.currentTarget.value, index); + }} /> + { + updateListItems('value', event.currentTarget.value, index); + }} /> + { + updateListItems('selected', checked, index); + }} /> + ; + }; + + const updateStepList = (newList: any[]) => { + setComponent({ + ...selectedComponent, + timezones: newList + }); + }; + return <> + setComponent({ + ...selectedComponent, + isShowTimePeriod: checked + })} /> + setComponent({ + ...selectedComponent, + light: checked + })} /> + setComponent({ + ...selectedComponent, + disabled: checked + })} /> + setComponent({ + ...selectedComponent, + hideLabel: checked + })} /> + setComponent({ + ...selectedComponent, + invalid: checked + })} /> + item.id === selectedComponent.size)} + itemToString={(item: any) => (item ? item.text : '')} + onChange={(event: any) => setComponent({ + ...selectedComponent, + size: event.selectedItem.id + })} /> + setComponent({ + ...selectedComponent, + label: event.currentTarget.value + })} /> + setComponent({ + ...selectedComponent, + invalidText: event.currentTarget.value + })} /> + setComponent({ + ...selectedComponent, + placeholder: event.currentTarget.value + })} /> + + ; +}; + +export const ATimePickerCodeUI = ({ selectedComponent, setComponent }: any) => setComponent({ + ...selectedComponent, + codeContext: { + ...selectedComponent.codeContext, + name: event.currentTarget.value + } + })} +/>; + +export const ATimePicker = ({ + componentObj, + ...rest +}: any) => { + return ( + + { + cc.id).join(' '), + css`${styleObjectToString(componentObj.style)}` + )} + disabled={componentObj.disabled} + invalid={componentObj.invalid} + invalidText={componentObj.invalidText} + placeholder={componentObj.placeholder} + hideLabel={componentObj.hideLabel} + labelText={componentObj.label} + light={componentObj.light} + size={componentObj.size}> + {componentObj.isShowTimePeriod && + + { + componentObj.timePeriod.map((step: any, index: number) => + ) + } + + } + {componentObj.timezones.length > 0 && + + { + componentObj.timezones.map((step: any, index: number) => + ) + } + + } + + } + + ); +}; + +export const componentInfo: ComponentInfo = { + component: ATimePicker, + settingsUI: ATimePickerSettingsUI, + codeUI: ATimePickerCodeUI, + keywords: ['timepicker', 'time', 'picker'], + name: 'Time Picker', + type: 'time-picker', + defaultComponentObj: { + type: 'time-picker', + invalidText: 'A valid value is required', + placeholder: 'hh:mm', + label: 'Select a time', + isShowTimePeriod: false, + timePeriod: [ + { + value:'AM', + text:'AM', + selected: true + }, + { + value:'PM', + text:'PM', + selected: false + } + ], + value: '', + timezones: [] + }, + image, + codeExport: { + angular: { + latest: { + inputs: ({ json }) => `@Input() ${nameStringToVariableString(json.codeContext?.name)}Value = "${json.value}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Label = "${json.label}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Theme: "light" | "dark" = "${json.light ? 'light' : 'dark'}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}IsInvalid = ${json.invalid ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Placeholder = "${json.placeholder}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Size: "sm" | "md" | "lg" = "${json.size ?? 'md'}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}HideLabel = ${json.hideLabel ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}InvalidText = "${json.invalidText}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}IsDisabled = ${json.disabled ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}IsShowTimePeriod = ${json.isShowTimePeriod ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}TimePeriod: {value: string; text: string; selected: boolean}[] + = ${JSON.stringify(json.timePeriod)}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Timezones: {value: string; text: string; selected: boolean}[] + = ${JSON.stringify(json.timezones)}`, + outputs: ({ json }) => `@Output() ${nameStringToVariableString(json.codeContext?.name)}ValueChange = new EventEmitter(); + @Output() ${nameStringToVariableString(json.codeContext?.name)}TimeperiodChange = new EventEmitter(); + @Output() ${nameStringToVariableString(json.codeContext?.name)}TimezoneChange = new EventEmitter();`, + imports: ['TimePickerModule', 'TimePickerSelectModule'], + code: ({ json }) => { + return ` + + + + + + + `; + } + }, + v10: { + inputs: ({ json }) => `@Input() ${nameStringToVariableString(json.codeContext?.name)}Value = "${json.value}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Label = "${json.label}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Theme: "light" | "dark" = "${json.light ? 'light' : 'dark'}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}IsInvalid = ${json.invalid ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Placeholder = "${json.placeholder}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Size: "sm" | "md" | "lg" = "${json.size ?? 'md'}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}HideLabel = ${json.hideLabel ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}InvalidText = "${json.invalidText}"; + @Input() ${nameStringToVariableString(json.codeContext?.name)}IsDisabled = ${json.disabled ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}IsShowTimePeriod = ${json.isShowTimePeriod ?? false}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}TimePeriod: {value: string; text: string; selected: boolean}[] + = ${JSON.stringify(json.timePeriod)}; + @Input() ${nameStringToVariableString(json.codeContext?.name)}Timezones: {value: string; text: string; selected: boolean}[] + = ${JSON.stringify(json.timezones)}`, + outputs: ({ json }) => `@Output() ${nameStringToVariableString(json.codeContext?.name)}ValueChange = new EventEmitter(); + @Output() ${nameStringToVariableString(json.codeContext?.name)}TimeperiodChange = new EventEmitter(); + @Output() ${nameStringToVariableString(json.codeContext?.name)}TimezoneChange = new EventEmitter();`, + imports: ['TimePickerModule', 'TimePickerSelectModule'], + code: ({ json }) => { + return ` + > + + + + + + `; + } + } + }, + react: { + latest: { + imports: ({ json }) => ['TimePicker', + ...((json.timePeriod && json.isShowTimePeriod) || json.timezones.length ? ['TimePickerSelect', 'SelectItem'] : [])], + code: ({ json }) => { + return ` handleInputChange({ + target: { + name: "${json.codeContext?.name}", + value: event.target.value + } + })}> + ${(json.timePeriod && json.isShowTimePeriod) ? + ` handleInputChange({ + target: { + name: "${json.codeContext?.name}TimeZone", + value: event.target.value + } + })}> + ${json.timePeriod.map((step: any) => (`` + )).join('\n')} + ` : '' + } + ${json.timezones.length ? + ` handleInputChange({ + target: { + name: "${json.codeContext?.name}TimeZone", + value: event.target.value + } + })}> + ${json.timezones.map((step: any) => (`` + )).join('\n')} + ` : '' + } + `; + } + }, + v10: { + imports: ({ json }) => ['TimePicker', + ...((json.timePeriod && json.isShowTimePeriod) || json.timezones.length ? ['TimePickerSelect', 'SelectItem'] : [])], + code: ({ json }) => { + return ` handleInputChange({ + target: { + name: "${json.codeContext?.name}", + value: event.target.value + } + })}> + ${(json.timePeriod && json.isShowTimePeriod) ? + ` handleInputChange({ + target: { + name: "${json.codeContext?.name}TimeZone", + value: event.target.value + } + })}> + ${json.timePeriod.map((step: any) => (`` + )).join('\n')} + ` : '' + } + ${json.timezones.length ? + ` handleInputChange({ + target: { + name: "${json.codeContext?.name}TimeZone", + value: event.target.value + } + })}> + ${json.timezones.map((step: any) => (`` + )).join('\n')} + ` : '' + } + `; + } + } + } + } +}; diff --git a/sdk/react/src/lib/fragment-components/index.ts b/sdk/react/src/lib/fragment-components/index.ts index e4308a3fe..a570ec40e 100644 --- a/sdk/react/src/lib/fragment-components/index.ts +++ b/sdk/react/src/lib/fragment-components/index.ts @@ -21,6 +21,7 @@ import * as searchinput from './a-searchinput'; import * as text from './a-text'; import * as textarea from './a-text-area'; import * as textinput from './a-text-input'; +import * as timepicker from './a-time-picker'; import * as link from './a-link'; import * as loading from './a-loading'; import * as inlineLoading from './a-inline-loading'; @@ -60,6 +61,7 @@ export { ATag, ATagCodeUI, ATagSettingsUI } from './a-tag'; export { AText, ATextCodeUI, ATextSettingsUI } from './a-text'; export { ATextArea, ATextAreaSettingsUI, ATextAreaCodeUI } from './a-text-area'; export { ATextInput, ATextInputSettingsUI, ATextInputCodeUI } from './a-text-input'; +export { ATimePicker, ATimePickerSettingsUI, ATimePickerCodeUI } from './a-time-picker'; export { AOverflowMenu, AOverflowMenuCodeUI, AOverflowMenuSettingsUI } from './a-overflow-menu'; export { ARadio, ARadioSettingsUI, ARadioCodeUI } from './a-radio'; export { ARadioGroup, ARadioGroupSettingsUI, ARadioGroupCodeUI } from './a-radio-group'; @@ -101,6 +103,7 @@ export const allComponents = { text, textarea, textinput, + timepicker, overflowMenu, // Tiles tile, diff --git a/sdk/react/src/lib/helpers/tools.ts b/sdk/react/src/lib/helpers/tools.ts index d91d708ee..727f794a3 100644 --- a/sdk/react/src/lib/helpers/tools.ts +++ b/sdk/react/src/lib/helpers/tools.ts @@ -146,7 +146,7 @@ export const getUsedCollectionsStyleUrls = (collections: any[], componentObj: an } return collections - .filter((collection) => usedCollectionsNames.includes(collection.name)) + ?.filter((collection) => usedCollectionsNames.includes(collection.name)) .flatMap((collection) => collection.styleUrls); };