diff --git a/src/App.css b/src/App.css index edaec624..a3c08c3f 100644 --- a/src/App.css +++ b/src/App.css @@ -1,360 +1,366 @@ *, *:after, *:before { - box-sizing: border-box; + box-sizing: border-box; } :root { - --padding-table: 38px; - --relation-color: #ecad5a; - --border-radius-table: var(--border-radius-s); - --checkbox-size: 22px; - --space-cells: 9px; - --color-error: rgb(247, 44, 44); - --border-toolbar: 1.5px solid var(--primary-700); - - /* spacing */ - --space-unit: 4px; - --space-xxs: calc(var(--space-unit) / 2); - --space-xs: calc(var(--space-unit) * 2); - --space-sm: calc(var(--space-unit) * 3); - --space-md: calc(var(--space-unit) * 4); - --space-lg: calc(var(--space-unit) * 6); - --space-xl: calc(var(--space-unit) * 8); - --space-xxl: calc(var(--space-unit) * 12); - --space-xxxl: calc(var(--space-unit) * 16); - - /* colors */ - --primary-50: #eef9f1; - --primary-100: #dcf2e3; - --primary-200: #b9e5c7; - --primary-300: #96d9ab; - --primary-400: #73cc8f; - --primary-500: #50bf73; - --primary-600: #40995c; - --primary-700: #307345; - --primary-800: #204c2e; - --primary-900: #102617; - - --secondary-50: #fbf5ed; - --secondary-100: #f7eada; - --secondary-200: #f0d5b5; - --secondary-300: #e8c091; - --secondary-400: #e1ab6c; - --secondary-500: #d99647; - --secondary-600: #ae7839; - --secondary-700: #825a2b; - --secondary-800: #573c1c; - --secondary-900: #2b1e0e; - - /* text */ - --text-color: #f3eded; - --text-disabled: #919191; - --text-dark: #202020; - - /* background */ - --background-50: #e2e7eb; - --background-100: #cad3dc; - --background-200: #a2b3c2; - --background-300: #7b8fa1; - --background-400: #455a6c; - --background-500: #2b3c50; - --background-600: #243446; - --background-700: #1c2d3f; - --background-800: #142231; - --background-900: #0f1924; - - /*font-sizes*/ - --fs-xs: 12px; - --fs-s: 14px; - --fs-m: 16px; - --fs-md: 18px; - --fs-l: 20px; - --fs-xl: 24px; - --fs-xxl: 32px; - - /*font-weight*/ - --fw-light: 300; - --fw-regular: 400; - --fw-medium: 500; - --fw-bold: 600; - --fw-extrabold: 700; - - /*border-radius*/ - --border-radius-unit: 4px; - --border-radius-xxs: calc(var(--border-radius-unit) / 2); - --border-radius-xs: calc(var(--border-radius-unit) * 2); - --border-radius-s: calc(var(--border-radius-unit) * 3); - --border-radius-m: calc(var(--border-radius-unit) * 4); - --border-radius-l: calc(var(--border-radius-unit) * 6); - --border-radius-xl: calc(var(--border-radius-unit) * 8); - --border-radius-xxl: calc(var(--border-radius-unit) * 12); - --border-radius-xxxl: calc(var(--border-radius-unit) * 16); - - --input-border-color: var(--background-400); - --edit-table-header: var(--background-900); - --input-border-color-active: var(--primary-300); - --input-radio-border-color: var(--background-400); - - /* Modal */ - --background-dialog: var(--background-700); - --veil-modal: #0d0d1185; - - /* Canvas */ - --bg-canvas: var(--background-800); - --bg-toolbar: var(--background-400); - --bg-table: var(--background-700); - --header-table: var(--primary-300); - - /* Input */ - --bg-input: var(--background-500); - --bg-input-disabled: var(--background-600); - --hover-input: var(--background-500); - - /* buttons */ - --button-secondary: var(--secondary-400); - --hover-button-secondary: var(--secondary-600); - --hover-button: var(--primary-300); - - /* border */ - --primary-border-color: var(--primary-300); - --secondary-border-color: var(--background-300); - --shadow-filter: var(--primary-600); - - /* checkbox */ - --bg-checkbox: var(--background-500); - --border-checkbox: var(--background-400); - --hover-checkbox: var(--background-200); - - /*About*/ - --color-project: var(--primary-300); - --color-team: var(--secondary-300); - - /* Footer*/ - --footer-background: var(--background-700); - --footer-text-color: var(--primary-300); - - /*Main styles*/ - font-family: Inter, system-ui, Helvetica, Arial, sans-serif; - font-size: var(--fs-m); - line-height: 1.5; - font-weight: var(--fw-regular); - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; + --padding-table: 38px; + --relation-color: #ecad5a; + --border-radius-table: var(--border-radius-s); + --checkbox-size: 22px; + --space-cells: 9px; + --color-error: rgb(247, 44, 44); + --border-toolbar: 1.5px solid var(--primary-700); + + /* spacing */ + --space-unit: 4px; + --space-xxs: calc(var(--space-unit) / 2); + --space-xs: calc(var(--space-unit) * 2); + --space-sm: calc(var(--space-unit) * 3); + --space-md: calc(var(--space-unit) * 4); + --space-lg: calc(var(--space-unit) * 6); + --space-xl: calc(var(--space-unit) * 8); + --space-xxl: calc(var(--space-unit) * 12); + --space-xxxl: calc(var(--space-unit) * 16); + + /* colors */ + --primary-50: #eef9f1; + --primary-100: #dcf2e3; + --primary-200: #b9e5c7; + --primary-300: #96d9ab; + --primary-400: #73cc8f; + --primary-500: #50bf73; + --primary-600: #40995c; + --primary-700: #307345; + --primary-800: #204c2e; + --primary-900: #102617; + + --secondary-50: #fbf5ed; + --secondary-100: #f7eada; + --secondary-200: #f0d5b5; + --secondary-300: #e8c091; + --secondary-400: #e1ab6c; + --secondary-500: #d99647; + --secondary-600: #ae7839; + --secondary-700: #825a2b; + --secondary-800: #573c1c; + --secondary-900: #2b1e0e; + + /* text */ + --text-color: #f3eded; + --text-disabled: #919191; + --text-dark: #202020; + + /* background */ + --background-50: #e2e7eb; + --background-100: #cad3dc; + --background-200: #a2b3c2; + --background-300: #7b8fa1; + --background-400: #455a6c; + --background-500: #2b3c50; + --background-600: #243446; + --background-700: #1c2d3f; + --background-800: #142231; + --background-900: #0f1924; + + /*font-sizes*/ + --fs-xs: 12px; + --fs-s: 14px; + --fs-m: 16px; + --fs-md: 18px; + --fs-l: 20px; + --fs-xl: 24px; + --fs-xxl: 32px; + + /*font-weight*/ + --fw-light: 300; + --fw-regular: 400; + --fw-medium: 500; + --fw-bold: 600; + --fw-extrabold: 700; + + /*border-radius*/ + --border-radius-unit: 4px; + --border-radius-xxs: calc(var(--border-radius-unit) / 2); + --border-radius-xs: calc(var(--border-radius-unit) * 2); + --border-radius-s: calc(var(--border-radius-unit) * 3); + --border-radius-m: calc(var(--border-radius-unit) * 4); + --border-radius-l: calc(var(--border-radius-unit) * 6); + --border-radius-xl: calc(var(--border-radius-unit) * 8); + --border-radius-xxl: calc(var(--border-radius-unit) * 12); + --border-radius-xxxl: calc(var(--border-radius-unit) * 16); + + --input-border-color: var(--background-400); + --edit-table-header: var(--background-900); + --input-border-color-active: var(--primary-300); + --input-radio-border-color: var(--background-400); + + /* Modal */ + --background-dialog: var(--background-700); + --veil-modal: #0d0d1185; + + /* Canvas */ + --bg-canvas: var(--background-800); + --bg-toolbar: var(--background-400); + --bg-table: var(--background-700); + --header-table: var(--primary-300); + + /* Input */ + --bg-input: var(--background-500); + --bg-input-disabled: var(--background-600); + --hover-input: var(--background-500); + + /* buttons */ + --button-secondary: var(--secondary-400); + --hover-button-secondary: var(--secondary-600); + --hover-button: var(--primary-300); + + /* border */ + --primary-border-color: var(--primary-300); + --secondary-border-color: var(--background-300); + --shadow-filter: var(--primary-600); + + /* checkbox */ + --bg-checkbox: var(--background-500); + --border-checkbox: var(--background-400); + --hover-checkbox: var(--background-200); + + /*About*/ + --color-project: var(--primary-300); + --color-team: var(--secondary-300); + + /* Footer*/ + --footer-background: var(--background-700); + --footer-text-color: var(--primary-300); + + /*Main styles*/ + font-family: Inter, system-ui, Helvetica, Arial, sans-serif; + font-size: var(--fs-m); + line-height: 1.5; + font-weight: var(--fw-regular); + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; } body { - margin: 0; - text-align: center; + margin: 0; + text-align: center; } .light { - /* text */ - --text-color: #202020; - --text-disabled: #aeaeae; - - /* Modal */ - --background-dialog: #f6f7f9; - --veil-modal: #23232385; - - /* Canvas */ - --bg-canvas: #f9fafb; - --bg-table: #f6f7f9; - --header-table: var(--primary-300); - - /* Toolbar */ - --bg-toolbar: #f0f2f5; - --border-toolbar: 1.5px solid var(--primary-200); - - /* Input */ - --bg-input: #f3f5f7; - --bg-input-disabled: #f0f2f5; - --hover-input: #f3f5f7; - --input-border-color: var(--background-200); - --input-border-color-active: var(--primary-500); - --input-radio-border-color: var(--background-200); - - /*Edit-table*/ - --edit-table-header: #e0e6eb; - - /* buttons */ - --button-secondary: var(--secondary-600); - --hover-button-secondary: var(--secondary-600); - --hover-button: var(--primary-300); - - /* border */ - --primary-border-color: var(--primary-400); - --secondary-border-color: #b5b9bc; - --shadow-filter: var(--primary-600); - - /* checkbox */ - --bg-checkbox: #e2e7eb; - - /* Not working*/ - --hover-checkbox: var(--background-300); - - /*About*/ - --color-project: var(--primary-500); - --color-team: var(--secondary-600); - - /* Footer*/ - --footer-background: #f3f5f7; - --footer-text-color: var(--primary-700); + /* text */ + --text-color: #202020; + --text-disabled: #aeaeae; + + /* Modal */ + --background-dialog: #f6f7f9; + --veil-modal: #23232385; + + /* Canvas */ + --bg-canvas: #f9fafb; + --bg-table: #f6f7f9; + --header-table: var(--primary-300); + + /* Toolbar */ + --bg-toolbar: #f0f2f5; + --border-toolbar: 1.5px solid var(--primary-200); + + /* Input */ + --bg-input: #f3f5f7; + --bg-input-disabled: #f0f2f5; + --hover-input: #f3f5f7; + --input-border-color: var(--background-200); + --input-border-color-active: var(--primary-500); + --input-radio-border-color: var(--background-200); + + /*Edit-table*/ + --edit-table-header: #e0e6eb; + + /* buttons */ + --button-secondary: var(--secondary-600); + --hover-button-secondary: var(--secondary-600); + --hover-button: var(--primary-300); + + /* border */ + --primary-border-color: var(--primary-400); + --secondary-border-color: #b5b9bc; + --shadow-filter: var(--primary-600); + + /* checkbox */ + --bg-checkbox: #e2e7eb; + + /* Not working*/ + --hover-checkbox: var(--background-300); + + /*About*/ + --color-project: var(--primary-500); + --color-team: var(--secondary-600); + + /* Footer*/ + --footer-background: #f3f5f7; + --footer-text-color: var(--primary-700); } /* Buttons */ button { - border-radius: var(--border-radius-s); - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: var(--fs-s); - font-weight: var(--fw-medium); - font-family: inherit; - background-color: inherit; - cursor: pointer; - transition: border-color 0.25s; - color: var(--text-color); - transition: all 0.3s ease-in-out; + border-radius: var(--border-radius-s); + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: var(--fs-s); + font-weight: var(--fw-medium); + font-family: inherit; + background-color: inherit; + cursor: pointer; + transition: border-color 0.25s; + color: var(--text-color); + transition: all 0.3s ease-in-out; } button:focus-visible { - outline: 1px auto var(--primary-300); + outline: 1px auto var(--primary-300); } button:hover { - background-color: var(--hover-button); - color: var(--text-dark); + background-color: var(--hover-button); + color: var(--text-dark); } button:disabled { - color: var(--text-disabled); - cursor: default; + color: var(--text-disabled); + cursor: default; } button:disabled:hover { - background: transparent; + background: transparent; } .button-secondary { - background-color: var(--secondary-300); - color: var(--text-dark); - margin-top: var(--space-md); - border-radius: var(--border-radius-s); + background-color: var(--secondary-300); + color: var(--text-dark); + margin-top: var(--space-md); + border-radius: var(--border-radius-s); } .button-secondary:hover { - background-color: var(--hover-button-secondary); + background-color: var(--hover-button-secondary); } .button-tertiary { - background-color: var(--background-500); + background-color: var(--background-500); } .button-tertiary:hover { - background-color: var(--background-300); - color: var(--text-dark); + background-color: var(--background-300); + color: var(--text-dark); } .button-secondary:disabled, .button-tertiary:disabled { - color: var(--text-disabled); - cursor: default; - background-color: var(--background-500); + color: var(--text-disabled); + cursor: default; + background-color: var(--background-500); } .button-secondary:disabled:hover, .button-tertiary:disabled:hover { - background-color: var(--background-500); + background-color: var(--background-500); } .light .button-tertiary { - background-color: var(--background-200); + background-color: var(--background-200); } .light .button-tertiary:hover { - background-color: var(--background-300); + background-color: var(--background-300); } .light .button-secondary:disabled, .light .button-tertiary:disabled { - background-color: #efefef; - color: #929292; + background-color: #efefef; + color: #929292; } .light .button-secondary:disabled:hover, .light .button-tertiary:disabled:hover { - background-color: #efefef; + background-color: #efefef; } .two-buttons { - display: flex; - align-items: baseline; - justify-content: center; - margin-top: var(--space-md); - gap: var(--space-lg); + display: flex; + align-items: baseline; + justify-content: center; + margin-top: var(--space-md); + gap: var(--space-lg); } /* Input */ input, select, textarea { - background-color: var(--bg-input); - border-radius: var(--border-radius-xs); - color: var(--text-color); - padding: var(--space-xs); - width: 100%; - border: none; - outline: none; - - border: 1px solid var(--input-border-color); - transition: all 0.2s ease; + background-color: var(--bg-input); + border-radius: var(--border-radius-xs); + color: var(--text-color); + padding: var(--space-xs); + width: 100%; + border: none; + outline: none; + + border: 1px solid var(--input-border-color); + transition: all 0.2s ease; } input:focus, select:focus { - border: 1px solid var(--input-border-color-active); - background-color: var(--hover-input); + border: 1px solid var(--input-border-color-active); + background-color: var(--hover-input); } select:hover, input:hover { - background-color: var(--hover-input); - box-shadow: 0 0 4px var(--hover-checkbox); + background-color: var(--hover-input); + box-shadow: 0 0 4px var(--hover-checkbox); } select { - padding: 7px; + padding: 7px; } /* Checkbox */ input[type='checkbox'] { - margin: 0; - width: var(--checkbox-size); - height: var(--checkbox-size); - cursor: pointer; + margin: 0; + width: var(--checkbox-size); + height: var(--checkbox-size); + cursor: pointer; } .light input[type='checkbox'] { - color-scheme: light; + color-scheme: light; } .dark input[type='checkbox'] { - color-scheme: dark; + color-scheme: dark; } .mobile-only { - display: none; + display: none; } @media screen and (max-device-width: 1090px) { - .hide-mobile { - display: none; - } - - .mobile-only { - display: block; - } -} \ No newline at end of file + .hide-mobile { + display: none; + } + + .mobile-only { + display: block; + } +} + +@media screen and (min-device-width: 1091px) { + .hide-desktop { + display: none; + } +} diff --git a/src/common/components/action-button/action-button.component.module.css b/src/common/components/action-button/action-button.component.module.css new file mode 100644 index 00000000..07cbbe14 --- /dev/null +++ b/src/common/components/action-button/action-button.component.module.css @@ -0,0 +1,68 @@ +.button { + background: none; + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + fill: var(--text-color); +} + +@media screen and (max-device-width: 1090px) { + .button { + padding: 6px; + font-size: var(--fs-s); + } +} + +.tooltip { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + background-color: hsla(0, 0%, 20%, 0.7); + border-radius: 3px; + color: var(--text-color); + font-size: 14px; + line-height: 14px; + padding: 7px 14px; + position: absolute; + transform: translate(0%, 90px); + visibility: hidden; + z-index: 2; +} + +.tooltipBottom { + transform: translate(0%, 90px); +} + +.tooltipTop { + transform: translate(0%, -90px); +} + +.tooltipTopLeft { + transform: translate(-50%, -90px); +} + +@media (hover: hover) { + .button:hover .tooltip { + animation: fadeIn 1s linear; + animation-fill-mode: forwards; + } +} + +@media screen and (max-device-width: 1090px) { + .tooltip { + display: none; + } +} + +@keyframes fadeIn { + 0% { + opacity: 0; + } + 75% { + opacity: 0; + } + 100% { + opacity: 1; + visibility: visible; + } +} diff --git a/src/pods/toolbar/components/toolbar-button/toolbarButton.component.spec.tsx b/src/common/components/action-button/action-button.component.spec.tsx similarity index 58% rename from src/pods/toolbar/components/toolbar-button/toolbarButton.component.spec.tsx rename to src/common/components/action-button/action-button.component.spec.tsx index 5e6e10aa..03fd9978 100644 --- a/src/pods/toolbar/components/toolbar-button/toolbarButton.component.spec.tsx +++ b/src/common/components/action-button/action-button.component.spec.tsx @@ -1,9 +1,9 @@ import { fireEvent, render, screen } from '@testing-library/react'; -import { ToolbarButton } from './toolbarButton.component'; import { vi } from 'vitest'; -import { ShortcutOptions } from '../../shortcut/shortcut.model'; +import { ActionButton } from './action-button.component'; +import { ShortcutOptions } from '@/common/shortcut'; -describe('ToolbarButton', () => { +describe('ActionButton', () => { let onClick: () => void; let shortcutOptions: ShortcutOptions; @@ -25,7 +25,7 @@ describe('ToolbarButton', () => { it('should render the button with the provided label and icon', () => { render( - Icon} label="Label" onClick={onClick} @@ -39,7 +39,7 @@ describe('ToolbarButton', () => { it('should call the onClick callback when the button is clicked', () => { const { getByText } = render( - Icon} label="Label" onClick={onClick} @@ -54,7 +54,7 @@ describe('ToolbarButton', () => { it('should render the tooltip with the correct shortcut key', () => { const { getByRole } = render( - Icon} label="Label" onClick={onClick} @@ -69,7 +69,7 @@ describe('ToolbarButton', () => { it('should disable the button if the disabled prop is true', () => { const { getByText } = render( - Icon} label="Label" onClick={onClick} @@ -82,4 +82,47 @@ describe('ToolbarButton', () => { expect(button).toHaveProperty('disabled', true); }); + + it('should hide the label when showLabel is false', () => { + render( + Icon} + label="Label" + onClick={onClick} + showLabel={false} + shortcutOptions={shortcutOptions} + /> + ); + + expect(screen.queryByText('Label')).toBeNull(); + }); + + it('should apply tooltipBottom by default when tooltipPosition is not provided', () => { + const { getByRole } = render( + Icon} + label="Label" + onClick={onClick} + shortcutOptions={shortcutOptions} + /> + ); + + const tooltip = getByRole('tooltip'); + expect(tooltip.className).toContain('tooltipBottom'); + }); + + it('should apply tooltipTop class when tooltipPosition is top', () => { + const { getByRole } = render( + Icon} + label="Label" + onClick={onClick} + tooltipPosition="top" + shortcutOptions={shortcutOptions} + /> + ); + + const tooltip = getByRole('tooltip'); + expect(tooltip.className).toContain('tooltipTop'); + }); }); diff --git a/src/pods/toolbar/components/toolbar-button/toolbarButton.component.tsx b/src/common/components/action-button/action-button.component.tsx similarity index 53% rename from src/pods/toolbar/components/toolbar-button/toolbarButton.component.tsx rename to src/common/components/action-button/action-button.component.tsx index 0991d792..385a37e7 100644 --- a/src/pods/toolbar/components/toolbar-button/toolbarButton.component.tsx +++ b/src/common/components/action-button/action-button.component.tsx @@ -1,7 +1,8 @@ import React from 'react'; -import useShortcut from '../../shortcut/shortcut.hook'; import { isMacOS } from '@/common/helpers/platform.helpers'; -import { ShortcutOptions } from '../../shortcut/shortcut.model'; +import classes from './action-button.component.module.css'; +import { ShortcutOptions } from '@/common/shortcut'; +import useShortcut from '@/common/shortcut/shortcut.hook'; interface Props { icon?: React.ReactNode; @@ -10,20 +11,33 @@ interface Props { className?: string; disabled?: boolean; shortcutOptions?: ShortcutOptions; + showLabel?: boolean; + tooltipPosition?: 'top' | 'bottom'; } -export const ToolbarButton: React.FC = ({ +export const ActionButton: React.FC = ({ disabled, icon, onClick = () => {}, className, label, shortcutOptions, + showLabel = true, + tooltipPosition = 'bottom', }) => { const shortcutCommand = isMacOS() ? 'Ctrl' : 'Alt'; const showTooltip = shortcutOptions && !disabled; const tooltipText = `(${shortcutCommand} + ${shortcutOptions?.targetKeyLabel})`; + const tooltipPositionClass = + tooltipPosition === 'top' ? classes.tooltipTop : classes.tooltipBottom; + + const tooltipClasses = `${classes.tooltip} ${tooltipPositionClass}`; + + const buttonClasses = className + ? `${classes.button} ${className}`.trim() + : classes.button; + useShortcut({ ...shortcutOptions, targetKey: shortcutOptions?.targetKey || [], @@ -32,15 +46,19 @@ export const ToolbarButton: React.FC = ({ return ( diff --git a/src/pods/footer/footer.pod.module.css b/src/pods/footer/footer.pod.module.css index 53571c5b..fb273bf2 100644 --- a/src/pods/footer/footer.pod.module.css +++ b/src/pods/footer/footer.pod.module.css @@ -6,11 +6,15 @@ background-color: var(--footer-background); color: var(--footer-text-color); text-align: left; - padding: var(--space-unit) var(--space-md); - padding-right: var(--space-xs); + padding: var(--space-xs) var(--space-md); font-size: var(--fs-xs); } +.zoomButtons { + display: flex; + gap: var(--space-xs); +} + @media (max-width: 1090px) { .footer-text { font-size: var(--fs-s); diff --git a/src/pods/toolbar/components/about-button/about-button.tsx b/src/pods/toolbar/components/about-button/about-button.tsx index d770eb21..e9c66ca4 100644 --- a/src/pods/toolbar/components/about-button/about-button.tsx +++ b/src/pods/toolbar/components/about-button/about-button.tsx @@ -1,9 +1,8 @@ import { useModalDialogContext } from '@/core/providers/modal-dialog-provider'; import { AboutIcon } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; import { ABOUT_TITLE } from '@/common/components'; import { AboutPod } from '@/pods/about'; +import { ActionButton } from '@/common/components/action-button'; export const AboutButton = () => { const { openModal } = useModalDialogContext(); @@ -13,11 +12,10 @@ export const AboutButton = () => { }; return ( - } label="About us" onClick={handleRelationClick} - className={classes.button} /> ); }; diff --git a/src/pods/toolbar/components/add-collection/add-collection.component.tsx b/src/pods/toolbar/components/add-collection/add-collection.component.tsx index c64c43ad..43e8a002 100644 --- a/src/pods/toolbar/components/add-collection/add-collection.component.tsx +++ b/src/pods/toolbar/components/add-collection/add-collection.component.tsx @@ -1,15 +1,15 @@ import { useModalDialogContext } from '@/core/providers/modal-dialog-provider'; import { EditTablePod } from '@/pods/edit-table'; -import { ToolbarButton } from '../toolbar-button/toolbarButton.component'; import { TableIcon } from '@/common/components/icons'; import { useCanvasViewSettingsContext } from '@/core/providers'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; + import { TableVm, useCanvasSchemaContext, } from '@/core/providers/canvas-schema'; import { ADD_COLLECTION_TITLE } from '@/common/components/modal-dialog'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; const BORDER_MARGIN = 40; @@ -44,11 +44,11 @@ export const AddCollection = () => { closeModal(); }; return ( - } label="Add Collection" onClick={handleEditTableClick} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.addCollection} /> ); diff --git a/src/pods/toolbar/components/canvas-setting-button/canvas-setting-button.component.tsx b/src/pods/toolbar/components/canvas-setting-button/canvas-setting-button.component.tsx index c4a6e052..a56f1558 100644 --- a/src/pods/toolbar/components/canvas-setting-button/canvas-setting-button.component.tsx +++ b/src/pods/toolbar/components/canvas-setting-button/canvas-setting-button.component.tsx @@ -1,10 +1,9 @@ import { useModalDialogContext } from '@/core/providers/modal-dialog-provider'; import { CanvasSettingsComponent } from '@/pods/canvas-settings'; -import { ToolbarButton } from '../toolbar-button'; import { CanvasSetting } from '@/common/components/icons'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; import { CANVAS_SETTINGS_TITLE } from '@/common/components/modal-dialog'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const CanvasSettingButton = () => { const { openModal, closeModal } = useModalDialogContext(); @@ -21,11 +20,11 @@ export const CanvasSettingButton = () => { }; return ( - } label="Settings" onClick={handleCanvasSettings} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.settings} /> ); diff --git a/src/pods/toolbar/components/copy-button/copy-button.component.tsx b/src/pods/toolbar/components/copy-button/copy-button.component.tsx index bd39519f..deb73084 100644 --- a/src/pods/toolbar/components/copy-button/copy-button.component.tsx +++ b/src/pods/toolbar/components/copy-button/copy-button.component.tsx @@ -1,18 +1,17 @@ import { useCanvasSchemaContext } from '@/core/providers/canvas-schema'; import { CopyIcon } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const CopyButton = () => { const { canvasSchema, copySelectedTable } = useCanvasSchemaContext(); return ( - } label="Copy" onClick={copySelectedTable} - className={`${classes.button} hide-mobile`} + className="hide-mobile" disabled={!canvasSchema.selectedElementId} shortcutOptions={SHORTCUTS.copy} /> diff --git a/src/pods/toolbar/components/delete-button/delete-button.component.tsx b/src/pods/toolbar/components/delete-button/delete-button.component.tsx index 77500b90..0dd4d66c 100644 --- a/src/pods/toolbar/components/delete-button/delete-button.component.tsx +++ b/src/pods/toolbar/components/delete-button/delete-button.component.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; import { TrashIcon } from '@/common/components/icons'; -import { ToolbarButton } from '../toolbar-button'; import { useCanvasSchemaContext } from '@/core/providers'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const DeleteButton: React.FC = () => { const { canvasSchema, deleteSelectedItem } = useCanvasSchemaContext(); @@ -13,11 +12,11 @@ export const DeleteButton: React.FC = () => { } }; return ( - } label="Delete" onClick={handleDeleteSelectedItemClick} - className={`${classes.button} hide-mobile`} + className="hide-mobile" disabled={canvasSchema.selectedElementId ? false : true} shortcutOptions={SHORTCUTS.delete} /> diff --git a/src/pods/toolbar/components/duplicate-button/duplicate-button.component.tsx b/src/pods/toolbar/components/duplicate-button/duplicate-button.component.tsx index 0e98fc27..bb979874 100644 --- a/src/pods/toolbar/components/duplicate-button/duplicate-button.component.tsx +++ b/src/pods/toolbar/components/duplicate-button/duplicate-button.component.tsx @@ -1,18 +1,17 @@ import { useCanvasSchemaContext } from '@/core/providers/canvas-schema'; import { CopyIcon } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const DuplicateButton = () => { const { canvasSchema, duplicateSelectedTable } = useCanvasSchemaContext(); return ( - } label="Duplicate Table" onClick={duplicateSelectedTable} - className={`${classes.button} hide-mobile`} + className="hide-mobile" disabled={!canvasSchema.selectedElementId} shortcutOptions={SHORTCUTS.duplicate} /> diff --git a/src/pods/toolbar/components/export-button/export-button.component.tsx b/src/pods/toolbar/components/export-button/export-button.component.tsx index be0bd41b..d4c11474 100644 --- a/src/pods/toolbar/components/export-button/export-button.component.tsx +++ b/src/pods/toolbar/components/export-button/export-button.component.tsx @@ -22,9 +22,8 @@ import { getSchemaScriptFromTableVmArray, placeAllTablesWithoutOverlap, } from './export-button.business'; -import { ToolbarButton } from '../toolbar-button/toolbarButton.component'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const ExportButton = () => { const { openModal } = useModalDialogContext(); @@ -125,11 +124,11 @@ export const ExportButton = () => { }; return ( - } label="Export" onClick={handleExportClick} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.export} disabled={canvasSchema.tables.length < 1} /> diff --git a/src/pods/toolbar/components/import-button/import-button.component.tsx b/src/pods/toolbar/components/import-button/import-button.component.tsx index fd347db8..957682cc 100644 --- a/src/pods/toolbar/components/import-button/import-button.component.tsx +++ b/src/pods/toolbar/components/import-button/import-button.component.tsx @@ -1,15 +1,14 @@ import { useModalDialogContext } from '@/core/providers/modal-dialog-provider'; -import { ToolbarButton } from '../toolbar-button/toolbarButton.component'; import { useCanvasViewSettingsContext } from '@/core/providers'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; import { TableVm, useCanvasSchemaContext, } from '@/core/providers/canvas-schema'; import { IMPORT_COLLECTION_TITLE } from '@/common/components/modal-dialog'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; import { ImportIcon } from '@/common/components/icons/import-icon.component'; import { ImportPanel } from '@/pods/import-collection/import-panel.pod'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; const BORDER_MARGIN = 40; @@ -45,11 +44,11 @@ export const ImportButton = () => { }; return ( - } label={'Import JSON'} onClick={handleImportClick} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.import} /> ); diff --git a/src/pods/toolbar/components/index.ts b/src/pods/toolbar/components/index.ts index d4c20713..69373094 100644 --- a/src/pods/toolbar/components/index.ts +++ b/src/pods/toolbar/components/index.ts @@ -1,4 +1,3 @@ -export * from './toolbar-button'; export * from './theme-toggle-button'; export * from './zoom-button'; export * from './relation-button'; diff --git a/src/pods/toolbar/components/new-button/new-button.component.tsx b/src/pods/toolbar/components/new-button/new-button.component.tsx index 1f9d54a2..2f1e40a7 100644 --- a/src/pods/toolbar/components/new-button/new-button.component.tsx +++ b/src/pods/toolbar/components/new-button/new-button.component.tsx @@ -3,9 +3,8 @@ import { useCanvasSchemaContext, useCanvasViewSettingsContext, } from '@/core/providers'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const NewButton = () => { const { createEmptySchema } = useCanvasSchemaContext(); @@ -18,11 +17,11 @@ export const NewButton = () => { }; return ( - } label="New" onClick={handleNewButtonClick} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.new} /> ); diff --git a/src/pods/toolbar/components/open-button/open-button.component.tsx b/src/pods/toolbar/components/open-button/open-button.component.tsx index 2019b9ee..70aba364 100644 --- a/src/pods/toolbar/components/open-button/open-button.component.tsx +++ b/src/pods/toolbar/components/open-button/open-button.component.tsx @@ -1,13 +1,12 @@ import React from 'react'; import { OpenIcon } from '@/common/components/icons/open-icon.component'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; import { useCanvasSchemaContext, useCanvasViewSettingsContext, } from '@/core/providers'; import { FileInput, OnFileSelectedCallback } from '@/common/file-input'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const OpenButton: React.FC = () => { const { loadSchema } = useCanvasSchemaContext(); @@ -34,11 +33,10 @@ export const OpenButton: React.FC = () => { }; return ( - } label={'Open'} onClick={() => FileInput(handleOpenButtonClick)} - className={classes.button} shortcutOptions={SHORTCUTS.open} /> ); diff --git a/src/pods/toolbar/components/paste-button/paste-button.component.tsx b/src/pods/toolbar/components/paste-button/paste-button.component.tsx index fb518aa1..58396731 100644 --- a/src/pods/toolbar/components/paste-button/paste-button.component.tsx +++ b/src/pods/toolbar/components/paste-button/paste-button.component.tsx @@ -1,18 +1,17 @@ import { useCanvasSchemaContext } from '@/core/providers/canvas-schema'; import { PasteIcon } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const PasteButton = () => { const { pasteTable, hasClipboardContent } = useCanvasSchemaContext(); return ( - } label="Paste" onClick={pasteTable} - className={`${classes.button} hide-mobile`} + className="hide-mobile" disabled={!hasClipboardContent} shortcutOptions={SHORTCUTS.Paste} /> diff --git a/src/pods/toolbar/components/redo-button/redo-button.component.tsx b/src/pods/toolbar/components/redo-button/redo-button.component.tsx index 8ed7173a..cf630f1b 100644 --- a/src/pods/toolbar/components/redo-button/redo-button.component.tsx +++ b/src/pods/toolbar/components/redo-button/redo-button.component.tsx @@ -1,19 +1,18 @@ import React from 'react'; import { RedoIcon } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; import { useCanvasSchemaContext } from '@/core/providers'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const RedoButton: React.FC = () => { const { canRedo, doRedo } = useCanvasSchemaContext(); return ( - } label={'Redo'} onClick={() => doRedo()} - className={`${classes.button} hide-mobile`} + className="hide-mobile" disabled={!canRedo()} shortcutOptions={SHORTCUTS.redo} /> diff --git a/src/pods/toolbar/components/relation-button/relation-button.component.tsx b/src/pods/toolbar/components/relation-button/relation-button.component.tsx index b848a7e9..6010ed05 100644 --- a/src/pods/toolbar/components/relation-button/relation-button.component.tsx +++ b/src/pods/toolbar/components/relation-button/relation-button.component.tsx @@ -1,14 +1,13 @@ import { useModalDialogContext } from '@/core/providers/modal-dialog-provider'; import { EditRelationPod } from '@/pods/edit-relation'; import { Relation } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; import { ADD_RELATION_TITLE } from '@/common/components'; import { RelationVm, useCanvasSchemaContext, } from '@/core/providers/canvas-schema'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const RelationButton = () => { const { openModal, closeModal } = useModalDialogContext(); @@ -34,11 +33,11 @@ export const RelationButton = () => { }; return ( - } label="Add Relation" onClick={handleRelationClick} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.addRelation} disabled={canvasSchema.tables.length < 1} /> diff --git a/src/pods/toolbar/components/save-button/save-button.component.tsx b/src/pods/toolbar/components/save-button/save-button.component.tsx index 2f197735..3bddc998 100644 --- a/src/pods/toolbar/components/save-button/save-button.component.tsx +++ b/src/pods/toolbar/components/save-button/save-button.component.tsx @@ -1,12 +1,11 @@ +import { ActionButton } from '@/common/components/action-button'; import { SaveIcon } from '@/common/components/icons/save-icon.component'; import { downloadFile, saveFileModern } from '@/common/export'; +import { SHORTCUTS } from '@/common/shortcut'; import { useCanvasSchemaContext, useCanvasViewSettingsContext, } from '@/core/providers'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; const DEFAULT_FILE_NAME = 'diagram'; const DEFAULT_EXTENSION_DESCRIPTION = 'Mongo Modeler'; @@ -45,11 +44,11 @@ export const SaveButton = () => { }; return ( - } label={'Save'} onClick={() => saveFile(canvasViewSettings.filename)} - className={`${classes.button} hide-mobile`} + className="hide-mobile" shortcutOptions={SHORTCUTS.save} disabled={canvasSchema.tables.length < 1} /> diff --git a/src/pods/toolbar/components/theme-toggle-button/themeToggleButton.component.tsx b/src/pods/toolbar/components/theme-toggle-button/themeToggleButton.component.tsx index aff26c62..32bf369d 100644 --- a/src/pods/toolbar/components/theme-toggle-button/themeToggleButton.component.tsx +++ b/src/pods/toolbar/components/theme-toggle-button/themeToggleButton.component.tsx @@ -1,7 +1,6 @@ import { useThemeContext } from '@/core/providers'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; import { DarkIcon, LightIcon } from '@/common/components/icons'; -import { ToolbarButton } from '../toolbar-button'; +import { ActionButton } from '@/common/components/action-button'; interface Props { darkLabel: string; @@ -14,8 +13,8 @@ export const ThemeToggleButton: React.FC = ({ const { theme, toggleTheme } = useThemeContext(); const label = theme.themeMode === 'dark' ? lightLabel : darkLabel; return ( - : } label={label} onClick={toggleTheme} diff --git a/src/pods/toolbar/components/toolbar-button/index.ts b/src/pods/toolbar/components/toolbar-button/index.ts deleted file mode 100644 index 80612c74..00000000 --- a/src/pods/toolbar/components/toolbar-button/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './toolbarButton.component'; diff --git a/src/pods/toolbar/components/undo-button/undo-button.component.tsx b/src/pods/toolbar/components/undo-button/undo-button.component.tsx index a04bb83a..8a643ff1 100644 --- a/src/pods/toolbar/components/undo-button/undo-button.component.tsx +++ b/src/pods/toolbar/components/undo-button/undo-button.component.tsx @@ -1,19 +1,18 @@ import React from 'react'; import { UndoIcon } from '@/common/components/icons'; -import { ToolbarButton } from '@/pods/toolbar/components/toolbar-button'; import { useCanvasSchemaContext } from '@/core/providers'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; export const UndoButton: React.FC = () => { const { doUndo } = useCanvasSchemaContext(); return ( - } label={'Undo'} onClick={() => doUndo()} - className={`${classes.button} hide-mobile`} + className="hide-mobile" disabled={false} shortcutOptions={SHORTCUTS.undo} /> diff --git a/src/pods/toolbar/components/zoom-button/zoom-in-button.component.tsx b/src/pods/toolbar/components/zoom-button/zoom-in-button.component.tsx index 541fb664..89fb6ff1 100644 --- a/src/pods/toolbar/components/zoom-button/zoom-in-button.component.tsx +++ b/src/pods/toolbar/components/zoom-button/zoom-in-button.component.tsx @@ -1,8 +1,7 @@ import { useCanvasViewSettingsContext } from '@/core/providers'; -import { ToolbarButton } from '../toolbar-button'; import { ZoomIn } from '@/common/components/icons'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; const MINIMUM_ZOOM_FACTOR_ALLOWED = 2.5; @@ -10,11 +9,11 @@ export const ZoomInButton = () => { const { zoomIn, canvasViewSettings } = useCanvasViewSettingsContext(); return ( - } label="Zoom In" onClick={zoomIn} - className={classes.button} + className="hide-desktop" disabled={canvasViewSettings.zoomFactor < MINIMUM_ZOOM_FACTOR_ALLOWED} shortcutOptions={SHORTCUTS.zoomIn} /> diff --git a/src/pods/toolbar/components/zoom-button/zoom-out-button.component.tsx b/src/pods/toolbar/components/zoom-button/zoom-out-button.component.tsx index 057f4818..35304600 100644 --- a/src/pods/toolbar/components/zoom-button/zoom-out-button.component.tsx +++ b/src/pods/toolbar/components/zoom-button/zoom-out-button.component.tsx @@ -1,8 +1,7 @@ import { useCanvasViewSettingsContext } from '@/core/providers'; -import { ToolbarButton } from '../toolbar-button'; import { ZoomOut } from '@/common/components/icons/zoom-out-icon.component'; -import classes from '@/pods/toolbar/toolbar.pod.module.css'; -import { SHORTCUTS } from '../../shortcut/shortcut.const'; +import { ActionButton } from '@/common/components/action-button'; +import { SHORTCUTS } from '@/common/shortcut'; const MAXIMUM_ZOOM_FACTOR_ALLOWED = 7.8; @@ -10,11 +9,11 @@ export const ZoomOutButton = () => { const { zoomOut, canvasViewSettings } = useCanvasViewSettingsContext(); return ( - } label="Zoom Out" onClick={zoomOut} - className={classes.button} + className="hide-desktop" disabled={canvasViewSettings.zoomFactor > MAXIMUM_ZOOM_FACTOR_ALLOWED} shortcutOptions={SHORTCUTS.zoomOut} /> diff --git a/src/pods/toolbar/toolbar.pod.module.css b/src/pods/toolbar/toolbar.pod.module.css index 96869890..02041686 100644 --- a/src/pods/toolbar/toolbar.pod.module.css +++ b/src/pods/toolbar/toolbar.pod.module.css @@ -35,50 +35,6 @@ } } -.button { - background: none; - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - fill: var(--text-color); -} - -@media screen and (max-device-width: 1090px) { - .button { - padding: 6px; - font-size: var(--fs-s); - } -} - -@media (hover: hover) { - .button:hover [role='tooltip'] { - animation: fade-in 1s linear; - animation-fill-mode: forwards; - } -} - -[role='tooltip'] { - -moz-border-radius: 3px; - -webkit-border-radius: 3px; - background-color: hsla(0, 0%, 20%, 0.7); - border-radius: 3px; - color: var(--text-color); - font-size: 14px; - line-height: 14px; - padding: 7px 14px; - position: absolute; - transform: translate(0%, 90px); - visibility: hidden; - z-index: 2; -} - -@media screen and (max-device-width: 1090px) { - [role='tooltip'] { - display: none; - } -} - @keyframes fade-in { 0% { opacity: 0;