Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • graphpolaris/frontend-v2
  • rijkheere/frontend-v-2-reordering-paoh
2 results
Show changes
Commits on Source (12)
Showing
with 211 additions and 66 deletions
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
useQuerybuilderSettings, useQuerybuilderSettings,
useSessionCache, useSessionCache,
} from '@graphpolaris/shared/lib/data-access'; } from '@graphpolaris/shared/lib/data-access';
import { setCurrentTheme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { resetGraphQueryResults, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; import { resetGraphQueryResults, queryingBackend } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice';
import { Query2BackendQuery, QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder'; import { Query2BackendQuery, QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder';
import { Navbar } from '../components/navbar/navbar'; import { Navbar } from '../components/navbar/navbar';
...@@ -52,6 +53,10 @@ export function App(props: App) { ...@@ -52,6 +53,10 @@ export function App(props: App) {
} }
}, [props]); }, [props]);
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
dispatch(setCurrentTheme(event.matches));
});
const [authCheck, setAuthCheck] = useState(false); const [authCheck, setAuthCheck] = useState(false);
const [tab, setTab] = useState<SideNavTab>('Schema'); const [tab, setTab] = useState<SideNavTab>('Schema');
// const [visFullSize, setVisFullSize] = useState<boolean>(false); // const [visFullSize, setVisFullSize] = useState<boolean>(false);
......
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState, useCallback } from 'react';
import { useAppDispatch, useSchemaGraph, useSessionCache, useAuthorizationCache } from '@graphpolaris/shared/lib/data-access'; import {
useAppDispatch,
useSchemaGraph,
useSessionCache,
useAuthorizationCache,
useCheckPermissionPolicy,
} from '@graphpolaris/shared/lib/data-access';
import { deleteSaveState, selectSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice'; import { deleteSaveState, selectSaveState } from '@graphpolaris/shared/lib/data-access/store/sessionSlice';
import { SettingsForm } from './forms/settings'; import { SettingsForm } from './forms/settings';
import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner'; import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner';
...@@ -21,7 +27,6 @@ export default function DatabaseSelector({}) { ...@@ -21,7 +27,6 @@ export default function DatabaseSelector({}) {
const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false); const [dbSelectionMenuOpen, setDbSelectionMenuOpen] = useState<boolean>(false);
const [settingsMenuOpen, setSettingsMenuOpen] = useState<'add' | 'update' | undefined>(undefined); const [settingsMenuOpen, setSettingsMenuOpen] = useState<'add' | 'update' | undefined>(undefined);
const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null); const [selectedSaveState, setSelectedSaveState] = useState<SaveStateI | null>(null);
// const [addDbConnectionFormOpen, setAddDbConnectionFormOpen] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
if ( if (
...@@ -56,6 +61,29 @@ export default function DatabaseSelector({}) { ...@@ -56,6 +61,29 @@ export default function DatabaseSelector({}) {
}; };
}, [connecting]); }, [connecting]);
const { canRead, canWrite } = useCheckPermissionPolicy();
const [readAllowed, setReadAllowed] = useState(false);
const [writeAllowed, setWriteAllowed] = useState(false);
const resource = 'database';
const checkReadPermission = useCallback(async () => {
const result = await canRead(resource);
setReadAllowed(result);
}, [canRead]);
const checkWritePermission = useCallback(async () => {
const result = await canWrite(resource);
setWriteAllowed(result);
}, [canWrite]);
useEffect(() => {
checkReadPermission();
}, [checkReadPermission]);
useEffect(() => {
checkWritePermission();
}, [checkWritePermission]);
return ( return (
<div className="menu-walkthrough"> <div className="menu-walkthrough">
<TooltipProvider delayDuration={1000}> <TooltipProvider delayDuration={1000}>
...@@ -85,11 +113,14 @@ export default function DatabaseSelector({}) { ...@@ -85,11 +113,14 @@ export default function DatabaseSelector({}) {
> >
<DropdownTrigger <DropdownTrigger
onClick={() => { onClick={() => {
setDbSelectionMenuOpen(!dbSelectionMenuOpen); if (connecting || authCache.authorized === false || !!authCache.roomID || writeAllowed) {
console.debug('User blocked from editing query due to being a viewer');
setDbSelectionMenuOpen(!dbSelectionMenuOpen);
}
}} }}
className="w-[18rem]" className="w-[18rem]"
size="md" size="md"
disabled={connecting || authCache.authorized === false || !!authCache.roomID} disabled={connecting || authCache.authorized === false || !!authCache.roomID || !writeAllowed}
title={ title={
<div className="flex items-center"> <div className="flex items-center">
{connecting && session.currentSaveState && session.currentSaveState in session.saveStates ? ( {connecting && session.currentSaveState && session.currentSaveState in session.saveStates ? (
......
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { import { DatabaseType, SaveStateI, databaseNameMapping, databaseProtocolMapping, nilUUID } from '@graphpolaris/shared/lib/data-access';
DatabaseInfo,
DatabaseType,
SaveStateI,
databaseNameMapping,
databaseProtocolMapping,
nilUUID,
} from '@graphpolaris/shared/lib/data-access';
import { Input } from '@graphpolaris/shared/lib/components/inputs'; import { Input } from '@graphpolaris/shared/lib/components/inputs';
import { useImmer } from 'use-immer'; import { useImmer } from 'use-immer';
import { initialState as qbInitialState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { initialState as qbInitialState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
......
import React from 'react'; import React from 'react';
import { DatabaseInfo } from '@graphpolaris/shared/lib/data-access';
import { DatabaseType, SaveStateI, nilUUID } from '@graphpolaris/shared/lib/data-access/broker'; import { DatabaseType, SaveStateI, nilUUID } from '@graphpolaris/shared/lib/data-access/broker';
import { initialState as qbInitialState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { initialState as qbInitialState } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice';
......
...@@ -8,18 +8,24 @@ ...@@ -8,18 +8,24 @@
/* The comment above was added so the code coverage wouldn't count this file towards code coverage. /* The comment above was added so the code coverage wouldn't count this file towards code coverage.
* We do not test components/renderfunctions/styling files. * We do not test components/renderfunctions/styling files.
* See testing plan for more details.*/ * See testing plan for more details.*/
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect, useCallback } from 'react';
import { useAuthorizationCache, useAuth } from '@graphpolaris/shared/lib/data-access'; import { useAuthorizationCache, useAuth, useCheckPermissionPolicy } from '@graphpolaris/shared/lib/data-access';
import DatabaseSelector from './DatabaseManagement/dbConnectionSelector'; import DatabaseSelector from './DatabaseManagement/dbConnectionSelector';
import { DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns'; import { DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns';
import GpLogo from './gp-logo'; import GpLogo from './gp-logo';
import { Popover, PopoverContent, PopoverTrigger } from '@graphpolaris/shared/lib/components/layout/Popover'; import { Popover, PopoverContent, PopoverTrigger } from '@graphpolaris/shared/lib/components/layout/Popover';
import { useDispatch } from 'react-redux';
import { Dialog, DialogContent, DialogTrigger } from '@graphpolaris/shared/lib/components/layout/Dialog';
import { UserManagementContent } from '@graphpolaris/shared/lib/components/userManagementContent/UserManagementContent';
import { addInfo } from '@graphpolaris/shared/lib/data-access/store/configSlice';
export const Navbar = () => { export const Navbar = () => {
const dropdownRef = useRef<HTMLDivElement>(null); const dropdownRef = useRef<HTMLDivElement>(null);
const auth = useAuth(); const auth = useAuth();
const authCache = useAuthorizationCache(); const authCache = useAuthorizationCache();
const [menuOpen, setMenuOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false);
const dispatch = useDispatch();
const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION;
useEffect(() => { useEffect(() => {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
...@@ -33,8 +39,37 @@ export const Navbar = () => { ...@@ -33,8 +39,37 @@ export const Navbar = () => {
}; };
}, [menuOpen]); }, [menuOpen]);
const buildInfo = import.meta.env.GRAPHPOLARIS_VERSION; const { canRead, canWrite } = useCheckPermissionPolicy();
const [readAllowed, setReadAllowed] = useState(false);
const [writeAllowed, setWriteAllowed] = useState(false);
const resource = 'policy';
const checkReadPermission = useCallback(async () => {
const result = await canRead(resource);
setReadAllowed(result);
}, [canRead]);
const checkWritePermission = useCallback(async () => {
const result = await canWrite(resource);
setWriteAllowed(result);
}, [canWrite]);
useEffect(() => {
checkReadPermission();
}, [checkReadPermission]);
useEffect(() => {
checkWritePermission();
}, [checkWritePermission]);
const handleConfirmUsers = (users: { name: string; email: string; type: string }[]) => {
//TODO !FIXME: when the user clicks on confirm, users state is ready to be sent to backend
};
const handleClickShareLink = () => {
//TODO !FIXME: add copy link to clipoard functionality
dispatch(addInfo('Link copied to clipboard'));
};
return ( return (
<nav className="w-full px-4 h-12 flex flex-row items-center gap-2 md:gap-3 lg:gap-4"> <nav className="w-full px-4 h-12 flex flex-row items-center gap-2 md:gap-3 lg:gap-4">
<a href="https://graphpolaris.com/" target="_blank" className="shrink-0 text-dark"> <a href="https://graphpolaris.com/" target="_blank" className="shrink-0 text-dark">
...@@ -53,12 +88,12 @@ export const Navbar = () => { ...@@ -53,12 +88,12 @@ export const Navbar = () => {
</div> </div>
</PopoverTrigger> </PopoverTrigger>
<PopoverContent className="w-56 z-30 bg-white rounded-sm border-[1px] outline-none"> <PopoverContent className="w-56 z-30 bg-light rounded-sm border-[1px] outline-none">
<div className="p-2 text-sm border-b"> <div className="p-2 text-sm border-b">
<h2 className="font-bold">user: {authCache.username}</h2> <h2 className="font-bold">user: {authCache.username}</h2>
<h3 className="text-xs break-words">session: {authCache.sessionID}</h3> <h3 className="text-xs break-words">session: {authCache.sessionID}</h3>
<h3 className="text-xs break-words">license: Creator</h3>
</div> </div>
{authCache.authorized ? ( {authCache.authorized ? (
<> <>
<DropdownItem <DropdownItem
...@@ -75,7 +110,6 @@ export const Navbar = () => { ...@@ -75,7 +110,6 @@ export const Navbar = () => {
<DropdownItem value="Login" onClick={() => {}} /> <DropdownItem value="Login" onClick={() => {}} />
</> </>
)} )}
{authCache?.roomID && ( {authCache?.roomID && (
<div className="p-2 border-b"> <div className="p-2 border-b">
<h3 className="text-xs break-words">Share ID: {authCache.roomID}</h3> <h3 className="text-xs break-words">Share ID: {authCache.roomID}</h3>
...@@ -84,6 +118,20 @@ export const Navbar = () => { ...@@ -84,6 +118,20 @@ export const Navbar = () => {
<div className="p-2 border-t"> <div className="p-2 border-t">
<h3 className="text-xs">Version: {buildInfo}</h3> <h3 className="text-xs">Version: {buildInfo}</h3>
</div> </div>
{writeAllowed && (
<>
<Dialog>
<DialogTrigger className="ml-2 text-sm hover:bg-secondary-200">Manage Viewers Permission</DialogTrigger>
<DialogContent>
<UserManagementContent
sessionId={authCache.sessionID ?? ''}
onConfirm={handleConfirmUsers}
onClickShareLink={handleClickShareLink}
/>
</DialogContent>
</Dialog>
</>
)}
</PopoverContent> </PopoverContent>
</Popover> </Popover>
</div> </div>
......
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from '@graphpolaris/shared/lib/components/tooltip';
export type VisualizationTooltipProps = { export type VisualizationTooltipProps = {
name: string; name: string;
...@@ -9,7 +8,7 @@ export type VisualizationTooltipProps = { ...@@ -9,7 +8,7 @@ export type VisualizationTooltipProps = {
export const VisualizationTooltip: React.FC<VisualizationTooltipProps> = ({ name, colorHeader, children }) => { export const VisualizationTooltip: React.FC<VisualizationTooltipProps> = ({ name, colorHeader, children }) => {
return ( return (
<div className="border-1 border-sec-200 bg-white w-[12rem] -mx-2 -my-2"> <div className="border-1 border-sec-200 bg-light w-[12rem] -mx-2 -my-2">
<div className="flex m-0 justify-start items-stretch border-b border-sec-200 relative"> <div className="flex m-0 justify-start items-stretch border-b border-sec-200 relative">
<div className="left-0 top-0 h-auto w-1.5" style={{ backgroundColor: colorHeader }}></div> <div className="left-0 top-0 h-auto w-1.5" style={{ backgroundColor: colorHeader }}></div>
<div className="px-2.5 py-1 truncate flex"> <div className="px-2.5 py-1 truncate flex">
......
import React, { ReactElement, ReactPropTypes, useMemo } from 'react'; import React, { useMemo } from 'react';
import styles from './buttons.module.scss'; import styles from './buttons.module.scss';
import { Icon, Sizes } from '../icon'; import { Icon } from '../icon';
import { forwardRef } from 'react';
import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';
type ButtonProps = { type ButtonProps = {
......
// BarPlot.stories.tsx // BarPlot.stories.tsx
import React from 'react'; import React from 'react';
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react';
import AxisComponent, { AxisComponentProps } from '.'; import AxisComponent from '.';
import { scaleLinear } from 'd3'; import { scaleLinear } from 'd3';
export default { export default {
......
import React from 'react'; import React from 'react';
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react';
import ColorLegendCat, { LegendProps } from '.'; import ColorLegendCat from '.';
export default { export default {
title: 'Visual charts/Charts/ColorLegendCat', title: 'Visual charts/Charts/ColorLegendCat',
......
import { axisBottom, scaleLinear, select } from 'd3'; import { axisBottom, scaleLinear, select } from 'd3';
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
//import { tailwindColors, dataColors, divergenceColors, categoricalColors } from './../../../../../config/src/colors.js';
//import { tailwindColors, dataColors, divergenceColors, categoricalColors } from '@graphpolaris/config/colors.js';
export type ColorLegendSeqDivProps = { export type ColorLegendSeqDivProps = {
colors: string[]; colors: string[];
...@@ -15,9 +13,6 @@ export const ColorLegendSeqDiv = ({ colors, data, tickCount = 5, name }: ColorLe ...@@ -15,9 +13,6 @@ export const ColorLegendSeqDiv = ({ colors, data, tickCount = 5, name }: ColorLe
useEffect(() => { useEffect(() => {
if (!svgRef.current) return; if (!svgRef.current) return;
console.log(colors);
//console.log(divergenceColors.blueRed);
const widthSVG: number = +svgRef.current.clientWidth; const widthSVG: number = +svgRef.current.clientWidth;
const heightSVG: number = +svgRef.current.clientHeight; const heightSVG: number = +svgRef.current.clientHeight;
......
export * from './barplot'; export * from './barplot';
export * from './colorLegendCat'; export * from './colorLegendCat';
export * from './colorLegendSeqDiv'; export * from './colorLegendSeqDiv';
import React from 'react'; import React from 'react';
import { Meta } from '@storybook/react'; import { Meta } from '@storybook/react';
import Scatterplot, { ScatterplotProps, VisualRegionConfig, regionData, DataPoint } from '.'; import Scatterplot, { VisualRegionConfig, regionData, DataPoint } from '.';
import { scaleLinear } from 'd3'; import { scaleLinear } from 'd3';
const Component: Meta<typeof Scatterplot> = { const Component: Meta<typeof Scatterplot> = {
......
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import * as d3 from 'd3'; import * as d3 from 'd3';
//import { VisualRegionConfig, regionData, DataPoint, DataPointXY } from './types';
import * as PIXI from 'pixi.js'; import * as PIXI from 'pixi.js';
export interface ScatterplotProps { export interface ScatterplotProps {
...@@ -69,7 +68,6 @@ const Scatterplot: React.FC<ScatterplotProps> = ({ data, visualConfig, xScale, o ...@@ -69,7 +68,6 @@ const Scatterplot: React.FC<ScatterplotProps> = ({ data, visualConfig, xScale, o
const yScale = d3.scaleLinear().domain([-1, 1]).range([visualConfig.heightMargin, 0]); const yScale = d3.scaleLinear().domain([-1, 1]).range([visualConfig.heightMargin, 0]);
console.log(visualConfig);
// PIXI // PIXI
const app = new PIXI.Application({ const app = new PIXI.Application({
width: visualConfig.width, width: visualConfig.width,
...@@ -103,8 +101,6 @@ const Scatterplot: React.FC<ScatterplotProps> = ({ data, visualConfig, xScale, o ...@@ -103,8 +101,6 @@ const Scatterplot: React.FC<ScatterplotProps> = ({ data, visualConfig, xScale, o
d.gfx = graphics; d.gfx = graphics;
}); });
console.log('DONE SIMULATION');
const svg = d3 const svg = d3
.select(svgRef.current) .select(svgRef.current)
.attr('width', visualConfig.width) .attr('width', visualConfig.width)
......
import React, { useState, useEffect } from 'react'; import React, { useEffect } from 'react';
import { Button } from '../buttons'; // Adjust the import path according to your project structure import { Button } from '../buttons';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../components/tooltip';
import { setTheme, Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice'; import { setTheme, Theme } from '@graphpolaris/shared/lib/data-access/store/configSlice';
import { useAppDispatch, useConfig } from '@graphpolaris/shared/lib/data-access/store'; import { useAppDispatch, useConfig } from '@graphpolaris/shared/lib/data-access/store';
...@@ -23,14 +23,15 @@ const ColorMode = () => { ...@@ -23,14 +23,15 @@ const ColorMode = () => {
// Function to toggle the theme // Function to toggle the theme
const toggleTheme = () => { const toggleTheme = () => {
const themes = [ const themes = [
Theme.system,
Theme.light, Theme.light,
Theme.dark, Theme.dark,
] ];
const newTheme = themes[(themes.indexOf(config.theme) + 1) % themes.length]; const newTheme = themes[(themes.indexOf(config.theme) + 1) % themes.length];
dispatch(setTheme(newTheme)); dispatch(setTheme(newTheme));
}; };
const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : 'icon-[ic--baseline-light-mode]'; const iconComponent = config.theme === Theme.dark ? 'icon-[ic--baseline-dark-mode]' : config.theme === Theme.light ? 'icon-[ic--baseline-light-mode]' : 'icon-[ic--baseline-auto-mode]';
return ( return (
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
...@@ -39,7 +40,7 @@ const ColorMode = () => { ...@@ -39,7 +40,7 @@ const ColorMode = () => {
<Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} /> <Button variant="ghost" size="sm" iconComponent={iconComponent} onClick={toggleTheme} />
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>{`Switch to ${config.theme === Theme.dark ? 'light' : 'dark'}-mode`}</p> <p>{`Currently on ${config.theme.split('-')[0]} theme`}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
......
import React, { useState, useEffect, useRef, ReactNode } from 'react'; import React, { useState, useRef, ReactNode } from 'react';
import { Icon } from '../icon'; import { Icon } from '../icon';
import { PopoverContent, PopoverTrigger, Popover, PopoverOptions } from '../layout/Popover'; import { PopoverContent, PopoverTrigger, Popover, PopoverOptions } from '../layout/Popover';
import { space } from 'postcss/lib/list';
export const DropdownContainer = ({ children, ...props }: { children: React.ReactNode } & PopoverOptions) => { export const DropdownContainer = ({ children, ...props }: { children: React.ReactNode } & PopoverOptions) => {
return ( return (
......
...@@ -3,7 +3,6 @@ import styles from './inputs.module.scss'; ...@@ -3,7 +3,6 @@ import styles from './inputs.module.scss';
import { DropdownTrigger, DropdownContainer, DropdownItem, DropdownItemContainer } from '../dropdowns'; import { DropdownTrigger, DropdownContainer, DropdownItem, DropdownItemContainer } from '../dropdowns';
import Info from '../info'; import Info from '../info';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../tooltip'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../tooltip';
import { Popover } from '../layout/Popover';
import { Button } from '../buttons'; import { Button } from '../buttons';
type SliderProps = { type SliderProps = {
...@@ -78,6 +77,11 @@ type BooleanProps = { ...@@ -78,6 +77,11 @@ type BooleanProps = {
value: boolean; value: boolean;
tooltip?: string; tooltip?: string;
onChange?: (value: boolean) => void; onChange?: (value: boolean) => void;
size?: 'xs' | 'sm' | 'md' | 'xl';
info?: string;
required?: boolean;
className?: string;
inline?: boolean;
}; };
type RadioProps = { type RadioProps = {
...@@ -106,7 +110,15 @@ type DropdownProps = { ...@@ -106,7 +110,15 @@ type DropdownProps = {
onChange?: (value: number | string) => void; onChange?: (value: number | string) => void;
}; };
export type InputProps = TextProps | SliderProps | CheckboxProps | DropdownProps | RadioProps | BooleanProps | NumberProps; export type InputProps =
| TextProps
| SliderProps
| CheckboxProps
| DropdownProps
| RadioProps
| BooleanProps
| NumberProps
| ToggleSwitchProps;
export const Input = (props: InputProps) => { export const Input = (props: InputProps) => {
switch (props.type) { switch (props.type) {
...@@ -124,6 +136,8 @@ export const Input = (props: InputProps) => { ...@@ -124,6 +136,8 @@ export const Input = (props: InputProps) => {
return <BooleanInput {...(props as BooleanProps)} />; return <BooleanInput {...(props as BooleanProps)} />;
case 'number': case 'number':
return <NumberInput {...(props as NumberProps)} />; return <NumberInput {...(props as NumberProps)} />;
case 'toggle':
return <ToggleSwitchInput {...(props as ToggleSwitchProps)} />;
default: default:
return null; return null;
} }
...@@ -407,23 +421,31 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec ...@@ -407,23 +421,31 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec
); );
}; };
export const BooleanInput = ({ label, value, onChange, tooltip }: BooleanProps) => { export const BooleanInput = ({ label, value, onChange, tooltip, info, size, required, className }: BooleanProps) => {
return ( return (
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger className={className + 'w-full flex justify-between'}>
<label className={`label cursor-pointer w-fit gap-2 px-0 py-1`}> {label && (
<span className="text-sm">{label}</span> <label className="label p-0">
<input <span
type="checkbox" className={`text-${size} text-left truncate font-medium text-secondary-700 ${required && "after:content-['*'] after:ml-0.5 after:text-danger-500"}`}
checked={value} >
onChange={(event) => { {label}
if (onChange) { </span>
onChange(event.target.checked); {info && <Info tooltip={info} />}
} </label>
}} )}
className="checkbox checkbox-xs" <input
/> type="checkbox"
</label> checked={value}
onChange={(event) => {
if (onChange) {
onChange(event.target.checked);
}
}}
className="checkbox checkbox-xs"
aria-label={`Toggle ${label}`}
/>
</TooltipTrigger> </TooltipTrigger>
{tooltip && <TooltipContent>{tooltip}</TooltipContent>} {tooltip && <TooltipContent>{tooltip}</TooltipContent>}
</Tooltip> </Tooltip>
...@@ -523,3 +545,42 @@ export const DropdownInput = ({ ...@@ -523,3 +545,42 @@ export const DropdownInput = ({
</Tooltip> </Tooltip>
); );
}; };
type ToggleSwitchProps = {
label: string;
type: 'toggle';
value: boolean;
classText?: string;
tooltip?: string;
onChange?: (value: boolean) => void;
};
export const ToggleSwitchInput = ({ label, classText, value, tooltip, onChange }: ToggleSwitchProps) => {
const [isSelected, setIsSelected] = useState(value);
const handleClick = () => {
const newValue = !isSelected;
setIsSelected(newValue);
if (onChange) {
onChange(newValue);
}
};
return (
<Tooltip>
<TooltipTrigger>
<div className="flex items-center space-x-2">
<div
className={`relative flex w-10 h-5 rounded-full cursor-pointer transition-all duration-500 ${isSelected ? 'bg-secondary-800' : 'bg-secondary-300'}`}
onClick={handleClick}
>
<div
className={`absolute top-0 left-0 w-5 h-5 rounded-full items-center bg-white transition-all duration-500 shadow-lg border-1 border-gray ${isSelected ? 'translate-x-full' : 'translate-x-0'} `}
></div>
</div>
<span className={classText}>{label}</span>
</div>
</TooltipTrigger>
{tooltip && <TooltipContent>{tooltip}</TooltipContent>}
</Tooltip>
);
};
import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { ToggleSwitchInput } from '.';
const Component: Meta<typeof ToggleSwitchInput> = {
title: 'Components/Inputs',
component: ToggleSwitchInput,
argTypes: { onChange: {} },
decorators: [(Story) => <div className="w-52 m-5">{Story()}</div>],
};
export default Component;
type Story = StoryObj<typeof Component>;
export const ToggleSwitchInputStory: Story = {
args: {
type: 'toggle',
label: 'Toggle Switch component',
value: false,
onChange: (value) => {},
},
};
import * as React from 'react'; import * as React from 'react';
import { useEffect, useRef } from 'react';
import { import {
useFloating, useFloating,
useClick, useClick,
......
import React, { useEffect, useState } from 'react'; import React from 'react';
import { ControlContainer } from '..'; import { ControlContainer } from '..';
export type Panel = { export type Panel = {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import type { Meta, StoryObj } from '@storybook/react'; import type { Meta, StoryObj } from '@storybook/react';
import { Popover, PopoverTrigger, PopoverContent, PopoverHeading, PopoverDescription, PopoverClose } from './Popover'; // Adjust the path based on your component location import { Popover, PopoverTrigger, PopoverContent, PopoverHeading, PopoverDescription, PopoverClose } from './Popover';
import { Icon } from '../icon'; import { Icon } from '../icon';
const metaPopover: Meta<typeof Popover> = { const metaPopover: Meta<typeof Popover> = {
component: Popover, component: Popover,
......