Revamp Link widget

This commit is contained in:
Nakul Nagargade 2025-03-18 16:16:04 +05:30
parent 37f76e386b
commit f7f156f424
8 changed files with 478 additions and 56 deletions

View file

@ -25,6 +25,7 @@ const shouldAddBoxShadowAndVisibility = [
'DaterangePicker',
'DatePickerV2',
'TimePicker',
'Link',
];
const RenderWidget = ({

View file

@ -24,6 +24,7 @@ const SHOW_ADDITIONAL_ACTIONS = [
'RichTextEditor',
'Image',
'ModalV2',
'Link',
];
const PROPERTIES_VS_ACCORDION_TITLE = {
Text: 'Data',
@ -36,6 +37,7 @@ const PROPERTIES_VS_ACCORDION_TITLE = {
Image: 'Data',
Container: 'Data',
ModalV2: 'Data',
Link: 'Data',
};
export const DefaultComponent = ({ componentMeta, darkMode, ...restProps }) => {
@ -129,6 +131,7 @@ export const baseComponentProperties = (
'DropdownV2',
'MultiselectV2',
'Image',
'Link',
],
Layout: [],
};

View file

@ -80,6 +80,7 @@ const NEW_REVAMPED_COMPONENTS = [
'Image',
'Container',
'ModalV2',
'Link',
];
export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selectedComponentId }) => {

View file

@ -12,14 +12,6 @@ export const linkConfig = {
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
linkText: {
type: 'code',
displayName: 'Link text',
@ -28,6 +20,14 @@ export const linkConfig = {
defaultValue: 'Click here',
},
},
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
targetType: {
type: 'select',
displayName: 'Target type',
@ -39,6 +39,34 @@ export const linkConfig = {
schema: { type: 'string' },
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onClick: { displayName: 'On click' },
@ -52,6 +80,7 @@ export const linkConfig = {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
accordian: 'Link text',
},
textSize: {
type: 'number',
@ -60,7 +89,39 @@ export const linkConfig = {
schema: { type: 'number' },
defaultValue: 14,
},
accordian: 'Link text',
},
horizontalAlignment: {
type: 'alignButtons',
displayName: 'Alignment',
validation: {
schema: { type: 'string' },
defaultValue: 'left',
},
accordian: 'Link text',
},
verticalAlignment: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' }, defaultValue: 'center' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignverticallytop', value: 'top', iconName: 'alignverticallytop' },
{ displayName: 'alignverticallycenter', value: 'center', iconName: 'alignverticallycenter' },
{ displayName: 'alignverticallybottom', value: 'bottom', iconName: 'alignverticallybottom' },
],
accordian: 'Link text',
isFxNotRequired: true,
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
accordian: 'Link text',
visibility: false,
},
underline: {
type: 'select',
displayName: 'Underline',
@ -73,14 +134,30 @@ export const linkConfig = {
schema: { type: 'string' },
defaultValue: 'on-hover',
},
accordian: 'Link text',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
accordian: 'Link text',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
isFxNotRequired: true,
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
exposedVariables: {},
@ -89,6 +166,31 @@ export const linkConfig = {
handle: 'click',
displayName: 'Click',
},
{
handle: 'setLinkTarget',
displayName: 'Set link target',
params: [{ handle: 'setLinkTargetState', displayName: 'Value', defaultValue: '', type: 'code' }],
},
{
handle: 'setLinkText',
displayName: 'Set link text',
params: [{ handle: 'setLinkTextState', displayName: 'Value', defaultValue: '', type: 'code' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'isVisible', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'isDisabled', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'isLoading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
],
definition: {
others: {
@ -99,13 +201,22 @@ export const linkConfig = {
linkTarget: { value: 'https://dev.to/' },
linkText: { value: 'Click here' },
targetType: { value: 'new' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
tooltip: { value: '' },
loadingState: { value: '{{false}}' },
},
events: [],
styles: {
textColor: { value: '#375FCF' },
textSize: { value: '{{14}}' },
underline: { value: 'on-hover' },
visibility: { value: '{{true}}' },
verticalAlignment: { value: 'center' },
horizontalAlignment: { value: 'left' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
icon: { value: 'IconHome2' },
iconVisibility: { value: false },
},
},
};

View file

@ -1,32 +1,106 @@
import React, { useRef, useEffect } from 'react';
import React, { useRef, useEffect, useState } from 'react';
import * as Icons from '@tabler/icons-react';
import cx from 'classnames';
import Loader from '@/ToolJetUI/Loader/Loader';
export const Link = ({ height, properties, styles, fireEvent, setExposedVariable, dataCy }) => {
const { linkTarget, linkText, targetType } = properties;
const { textColor, textSize, underline, visibility, boxShadow } = styles;
export const Link = ({ height, properties, styles, fireEvent, setExposedVariables, dataCy }) => {
const { linkTarget, linkText, targetType, visibility, disabledState, loadingState } = properties;
const { textColor, textSize, underline, boxShadow, verticalAlignment, horizontalAlignment, icon, iconVisibility } =
styles;
const clickRef = useRef();
const [linkTargetState, setLinkTargetState] = useState(linkTarget);
const [linkTextState, setLinkTextState] = useState(linkText);
const [isVisible, setIsVisible] = useState(visibility);
const [isDisabled, setIsDisabled] = useState(disabledState);
const [isLoading, setIsLoading] = useState(false);
const computedStyles = {
fontSize: textSize,
display: 'flex',
alignItems: verticalAlignment === 'top' ? 'flex-start' : verticalAlignment === 'center' ? 'center' : 'flex-end',
justifyContent:
horizontalAlignment === 'left' ? 'flex-start' : horizontalAlignment === 'center' ? 'center' : 'flex-end',
height,
boxShadow,
cursor: isDisabled ? 'not-allowed' : 'pointer',
opacity: isDisabled ? 0.5 : 1,
pointerEvents: isDisabled ? 'none' : 'auto',
};
// eslint-disable-next-line import/namespace
const IconElement = Icons?.[icon] == undefined ? Icons['IconHome2'] : Icons[icon];
// Update the state when the linkTarget or linkText changes
useEffect(() => {
setLinkTargetState(linkTarget);
setLinkTextState(linkText);
}, [linkTarget, linkText]);
// Update the exposed variables when the linkTarget or linkText changes
useEffect(() => {
const exposedVariables = {
linkTarget: linkTargetState,
linkText: linkTextState,
};
setExposedVariables(exposedVariables);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [linkTargetState, linkTextState]);
useEffect(() => {
setExposedVariable('click', async function () {
clickRef.current.click();
});
setIsVisible(visibility);
setIsDisabled(disabledState);
setIsLoading(loadingState);
const exposedVariables = {
isLoading: loadingState,
isVisible: visibility,
isDisabled: disabledState,
};
setExposedVariables(exposedVariables);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [visibility, disabledState, loadingState]);
// Update the exposed functions on mount
useEffect(() => {
const exposedVariables = {
click: async function () {
clickRef.current.click();
},
setVisibility: async function (value) {
setIsVisible(value);
},
setDisable: async function (value) {
setIsDisabled(value);
},
setLoading: async function (value) {
setIsLoading(value);
},
setLinkTarget: async function (value) {
setLinkTargetState(value);
},
setLinkText: async function (value) {
setLinkTextState(value);
},
};
setExposedVariables(exposedVariables);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (isLoading) {
return (
<div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<center>
<Loader width="16" absolute={false} />
</center>
</div>
);
}
return (
<div
className={cx('link-widget', { 'd-none': !visibility }, `${underline}`)}
className={cx('link-widget', { 'd-none': !isVisible }, `${underline}`)}
style={computedStyles}
data-cy={dataCy}
>
<a
{...(linkTarget != '' ? { href: linkTarget } : {})}
{...(linkTargetState != '' ? { href: linkTargetState } : {})}
target={targetType === 'new' && '_blank'}
onClick={(event) => {
event.stopPropagation();
@ -35,10 +109,22 @@ export const Link = ({ height, properties, styles, fireEvent, setExposedVariable
onMouseOver={() => {
fireEvent('onHover');
}}
style={{ color: textColor }}
style={{ color: textColor, fontSize: textSize }}
ref={clickRef}
>
{linkText}
<span className="d-flex align-items-center justify-content-center">
{iconVisibility && (
<IconElement
style={{
width: textSize,
height: textSize,
marginRight: '4px',
}}
stroke={1.5}
/>
)}
{linkTextState}
</span>
</a>
</div>
);

View file

@ -12,14 +12,6 @@ export const linkConfig = {
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
linkText: {
type: 'code',
displayName: 'Link text',
@ -28,6 +20,14 @@ export const linkConfig = {
defaultValue: 'Click here',
},
},
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
targetType: {
type: 'select',
displayName: 'Target type',
@ -39,6 +39,34 @@ export const linkConfig = {
schema: { type: 'string' },
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onClick: { displayName: 'On click' },
@ -52,6 +80,7 @@ export const linkConfig = {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
accordian: 'Link text',
},
textSize: {
type: 'number',
@ -60,7 +89,39 @@ export const linkConfig = {
schema: { type: 'number' },
defaultValue: 14,
},
accordian: 'Link text',
},
horizontalAlignment: {
type: 'alignButtons',
displayName: 'Alignment',
validation: {
schema: { type: 'string' },
defaultValue: 'left',
},
accordian: 'Link text',
},
verticalAlignment: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' }, defaultValue: 'center' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignverticallytop', value: 'top', iconName: 'alignverticallytop' },
{ displayName: 'alignverticallycenter', value: 'center', iconName: 'alignverticallycenter' },
{ displayName: 'alignverticallybottom', value: 'bottom', iconName: 'alignverticallybottom' },
],
accordian: 'Link text',
isFxNotRequired: true,
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
accordian: 'Link text',
visibility: false,
},
underline: {
type: 'select',
displayName: 'Underline',
@ -73,14 +134,30 @@ export const linkConfig = {
schema: { type: 'string' },
defaultValue: 'on-hover',
},
accordian: 'Link text',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
accordian: 'Link text',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
isFxNotRequired: true,
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
exposedVariables: {},
@ -89,6 +166,31 @@ export const linkConfig = {
handle: 'click',
displayName: 'Click',
},
{
handle: 'setLinkTarget',
displayName: 'Set link target',
params: [{ handle: 'setLinkTargetState', displayName: 'Value', defaultValue: '', type: 'code' }],
},
{
handle: 'setLinkText',
displayName: 'Set link text',
params: [{ handle: 'setLinkTextState', displayName: 'Value', defaultValue: '', type: 'code' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'isVisible', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'isDisabled', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'isLoading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
],
definition: {
others: {
@ -99,13 +201,22 @@ export const linkConfig = {
linkTarget: { value: 'https://dev.to/' },
linkText: { value: 'Click here' },
targetType: { value: 'new' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
tooltip: { value: '' },
loadingState: { value: '{{false}}' },
},
events: [],
styles: {
textColor: { value: '#375FCF' },
textSize: { value: '{{14}}' },
underline: { value: 'on-hover' },
visibility: { value: '{{true}}' },
verticalAlignment: { value: 'center' },
horizontalAlignment: { value: 'left' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
icon: { value: 'IconHome2' },
iconVisibility: { value: false },
},
},
};

View file

@ -7142,8 +7142,6 @@ tbody {
}
.link-widget {
display: flex;
align-items: center;
overflow: auto;
&.hover {

View file

@ -12,14 +12,6 @@ export const linkConfig = {
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
},
properties: {
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
linkText: {
type: 'code',
displayName: 'Link text',
@ -28,6 +20,14 @@ export const linkConfig = {
defaultValue: 'Click here',
},
},
linkTarget: {
type: 'code',
displayName: 'Link target',
validation: {
schema: { type: 'string' },
defaultValue: 'https://dev.to/',
},
},
targetType: {
type: 'select',
displayName: 'Target type',
@ -39,6 +39,34 @@ export const linkConfig = {
schema: { type: 'string' },
},
},
loadingState: {
type: 'toggle',
displayName: 'Loading state',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
},
section: 'additionalActions',
},
disabledState: {
type: 'toggle',
displayName: 'Disable',
validation: { schema: { type: 'boolean' }, defaultValue: false },
section: 'additionalActions',
},
tooltip: {
type: 'code',
displayName: 'Tooltip',
validation: { schema: { type: 'string' }, defaultValue: 'Tooltip text' },
section: 'additionalActions',
placeholder: 'Enter tooltip text',
},
},
events: {
onClick: { displayName: 'On click' },
@ -52,6 +80,7 @@ export const linkConfig = {
schema: { type: 'string' },
defaultValue: '#375FCF',
},
accordian: 'Link text',
},
textSize: {
type: 'number',
@ -60,7 +89,39 @@ export const linkConfig = {
schema: { type: 'number' },
defaultValue: 14,
},
accordian: 'Link text',
},
horizontalAlignment: {
type: 'alignButtons',
displayName: 'Alignment',
validation: {
schema: { type: 'string' },
defaultValue: 'left',
},
accordian: 'Link text',
},
verticalAlignment: {
type: 'switch',
displayName: '',
validation: { schema: { type: 'string' }, defaultValue: 'center' },
showLabel: false,
isIcon: true,
options: [
{ displayName: 'alignverticallytop', value: 'top', iconName: 'alignverticallytop' },
{ displayName: 'alignverticallycenter', value: 'center', iconName: 'alignverticallycenter' },
{ displayName: 'alignverticallybottom', value: 'bottom', iconName: 'alignverticallybottom' },
],
accordian: 'Link text',
isFxNotRequired: true,
},
icon: {
type: 'icon',
displayName: 'Icon',
validation: { schema: { type: 'string' }, defaultValue: 'IconHome2' },
accordian: 'Link text',
visibility: false,
},
underline: {
type: 'select',
displayName: 'Underline',
@ -73,14 +134,30 @@ export const linkConfig = {
schema: { type: 'string' },
defaultValue: 'on-hover',
},
accordian: 'Link text',
},
visibility: {
type: 'toggle',
displayName: 'Visibility',
boxShadow: {
type: 'boxShadow',
displayName: 'Box Shadow',
validation: {
schema: { type: 'boolean' },
defaultValue: true,
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: '0px 0px 0px 0px #00000040',
},
accordian: 'Link text',
},
padding: {
type: 'switch',
displayName: 'Padding',
validation: {
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
defaultValue: 'default',
},
isFxNotRequired: true,
options: [
{ displayName: 'Default', value: 'default' },
{ displayName: 'None', value: 'none' },
],
accordian: 'container',
},
},
exposedVariables: {},
@ -89,6 +166,31 @@ export const linkConfig = {
handle: 'click',
displayName: 'Click',
},
{
handle: 'setLinkTarget',
displayName: 'Set link target',
params: [{ handle: 'setLinkTargetState', displayName: 'Value', defaultValue: '', type: 'code' }],
},
{
handle: 'setLinkText',
displayName: 'Set link text',
params: [{ handle: 'setLinkTextState', displayName: 'Value', defaultValue: '', type: 'code' }],
},
{
handle: 'setVisibility',
displayName: 'Set visibility',
params: [{ handle: 'isVisible', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setDisable',
displayName: 'Set disable',
params: [{ handle: 'isDisabled', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
{
handle: 'setLoading',
displayName: 'Set loading',
params: [{ handle: 'isLoading', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
},
],
definition: {
others: {
@ -99,13 +201,22 @@ export const linkConfig = {
linkTarget: { value: 'https://dev.to/' },
linkText: { value: 'Click here' },
targetType: { value: 'new' },
visibility: { value: '{{true}}' },
disabledState: { value: '{{false}}' },
tooltip: { value: '' },
loadingState: { value: '{{false}}' },
},
events: [],
styles: {
textColor: { value: '#375FCF' },
textSize: { value: '{{14}}' },
underline: { value: 'on-hover' },
visibility: { value: '{{true}}' },
verticalAlignment: { value: 'center' },
horizontalAlignment: { value: 'left' },
padding: { value: 'default' },
boxShadow: { value: '0px 0px 0px 0px #00000040' },
icon: { value: 'IconHome2' },
iconVisibility: { value: false },
},
},
};