diff --git a/app/client/src/assets/icons/control/drag.svg b/app/client/src/assets/icons/control/drag.svg new file mode 100644 index 0000000000..a432baa4a2 --- /dev/null +++ b/app/client/src/assets/icons/control/drag.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/client/src/components/designSystems/appsmith/Table.tsx b/app/client/src/components/designSystems/appsmith/Table.tsx index 68da25e7e4..95dcef8f5c 100644 --- a/app/client/src/components/designSystems/appsmith/Table.tsx +++ b/app/client/src/components/designSystems/appsmith/Table.tsx @@ -69,7 +69,7 @@ export const Table = (props: TableProps) => { const data = React.useMemo(() => props.data, [JSON.stringify(props.data)]); const columns = React.useMemo(() => props.columns, [ JSON.stringify(props.columns), - props.columnActions, + JSON.stringify(props.columnActions), ]); const { getTableProps, diff --git a/app/client/src/components/designSystems/appsmith/TabsComponent.tsx b/app/client/src/components/designSystems/appsmith/TabsComponent.tsx index 069febc174..5ba211dec0 100644 --- a/app/client/src/components/designSystems/appsmith/TabsComponent.tsx +++ b/app/client/src/components/designSystems/appsmith/TabsComponent.tsx @@ -3,6 +3,7 @@ import styled, { css } from "styled-components"; import { ComponentProps } from "./BaseComponent"; import { TabsWidgetProps, TabContainerWidgetProps } from "widgets/TabsWidget"; import { generateClassName, getCanvasClassName } from "utils/generators"; +import { scrollbarLight } from "constants/DefaultTheme"; interface TabsComponentProps extends ComponentProps { children?: ReactNode; @@ -53,8 +54,13 @@ const ScrollableCanvasWrapper = styled.div< `; const TabsContainer = styled.div` + width: 100%; + overflow-x: auto; + overflow-y: hidden; + ${scrollbarLight}; + background: ${props => props.theme.colors.builderBodyBG}; && { - height: 32px; + height: 38px; width: 100%; display: flex; justify-content: flex-start; diff --git a/app/client/src/components/propertyControls/TabControl.tsx b/app/client/src/components/propertyControls/TabControl.tsx index 97197ef6ff..1b180b8fdd 100644 --- a/app/client/src/components/propertyControls/TabControl.tsx +++ b/app/client/src/components/propertyControls/TabControl.tsx @@ -16,12 +16,10 @@ const StyledDeleteIcon = styled(FormIcons.DELETE_ICON as AnyStyledComponent)` cursor: pointer; `; -const StyledDragIcon = styled( - ControlIcons.DRAGGABLE_CONTROL as AnyStyledComponent, -)` +const StyledDragIcon = styled(ControlIcons.DRAG_CONTROL as AnyStyledComponent)` padding: 0; position: relative; - margin-left: 15px; + margin-right: 15px; cursor: move; svg { path { @@ -40,6 +38,7 @@ const StyledPropertyPaneButtonWrapper = styled.div` const ItemWrapper = styled.div` display: flex; justify-content: flex-start; + align-items: center; `; const TabsWrapper = styled.div` @@ -77,6 +76,7 @@ function TabControlComponent(props: RenderComponentProps) { const { deleteOption, updateOption, item, index } = props; return ( + - ); } diff --git a/app/client/src/constants/DefaultTheme.tsx b/app/client/src/constants/DefaultTheme.tsx index 8805865b53..a3c316b48b 100644 --- a/app/client/src/constants/DefaultTheme.tsx +++ b/app/client/src/constants/DefaultTheme.tsx @@ -564,5 +564,23 @@ export const theme: Theme = { }, }; +export const scrollbarLight = css` + scrollbar-color: ${props => props.theme.colors.paneText} + + scrollbar-width: thin; + &::-webkit-scrollbar { + width: 6px; + height: 6px; + } + &::-webkit-scrollbar-track { + box-shadow: inset 0 0 6px + ${props => getColorWithOpacity(props.theme.colors.paneText, 0.3)}; + } + &::-webkit-scrollbar-thumb { + background-color: ${props => props.theme.colors.paneText}; + border-radius: ${props => props.theme.radii[1]}px; + } +`; + export { css, createGlobalStyle, keyframes, ThemeProvider }; export default styled; diff --git a/app/client/src/constants/WidgetValidation.ts b/app/client/src/constants/WidgetValidation.ts index 8dbc8b9ef0..9741656714 100644 --- a/app/client/src/constants/WidgetValidation.ts +++ b/app/client/src/constants/WidgetValidation.ts @@ -17,6 +17,7 @@ export const VALIDATION_TYPES = { MARKERS: "MARKERS", ACTION_SELECTOR: "ACTION_SELECTOR", ARRAY_ACTION_SELECTOR: "ARRAY_ACTION_SELECTOR", + SELECTED_TAB: "SELECTED_TAB", }; export type ValidationResponse = { diff --git a/app/client/src/icons/ControlIcons.tsx b/app/client/src/icons/ControlIcons.tsx index ddcd8199eb..455025cb07 100644 --- a/app/client/src/icons/ControlIcons.tsx +++ b/app/client/src/icons/ControlIcons.tsx @@ -15,6 +15,7 @@ import { ReactComponent as HelpIcon } from "assets/icons/control/help.svg"; import { ReactComponent as PickMyLocationSelectedIcon } from "assets/icons/control/pick-location-selected.svg"; import { ReactComponent as SettingsIcon } from "assets/icons/control/settings.svg"; +import { ReactComponent as DragIcon } from "assets/icons/control/drag.svg"; import PlayIcon from "assets/icons/control/play-icon.png"; /* eslint-disable react/display-name */ @@ -101,6 +102,11 @@ export const ControlIcons: { /> ), + DRAG_CONTROL: (props: IconProps) => ( + + + + ), }; export type ControlIconName = keyof typeof ControlIcons; diff --git a/app/client/src/utils/Validators.ts b/app/client/src/utils/Validators.ts index 3a1568c709..6bfbc6d4d3 100644 --- a/app/client/src/utils/Validators.ts +++ b/app/client/src/utils/Validators.ts @@ -499,4 +499,25 @@ export const VALIDATORS: Record = { message: message, }; }, + [VALIDATION_TYPES.SELECTED_TAB]: ( + value: any, + props: WidgetProps, + dataTree?: DataTree, + ): ValidationResponse => { + const tabs = + props.tabs && _.isString(props.tabs) + ? JSON.parse(props.tabs) + : props.tabs && Array.isArray(props.tabs) + ? props.tabs + : []; + const tabNames = tabs.map((i: { label: string; id: string }) => i.label); + const isValidTabName = tabNames.includes(value); + return { + isValid: isValidTabName, + parsed: value, + message: isValidTabName + ? "" + : `${WIDGET_TYPE_VALIDATION_ERROR}: Invalid tab name.`, + }; + }, }; diff --git a/app/client/src/widgets/TabsWidget.tsx b/app/client/src/widgets/TabsWidget.tsx index f52729f49e..1f10f6587a 100644 --- a/app/client/src/widgets/TabsWidget.tsx +++ b/app/client/src/widgets/TabsWidget.tsx @@ -14,6 +14,7 @@ class TabsWidget extends BaseWidget< static getPropertyValidationMap(): WidgetPropertyValidationType { return { tabs: VALIDATION_TYPES.TABS_DATA, + selectedTab: VALIDATION_TYPES.SELECTED_TAB, }; }