diff --git a/__tests__/integration/mirador/thumbnail-navigation.test.js b/__tests__/integration/mirador/thumbnail-navigation.test.js index 35de953ccaa3b13e04ec8ab4fb17bc7a9faf381e..82502cbd53e644f1417557d7fa56bb5f80f704f5 100644 --- a/__tests__/integration/mirador/thumbnail-navigation.test.js +++ b/__tests__/integration/mirador/thumbnail-navigation.test.js @@ -6,7 +6,8 @@ describe('Thumbnail navigation', () => { await expect(page).toMatchElement('.mirador-window', { polling: 'mutation', timeout: 5000 }); }); - it('navigates a manifest using thumbnail navigation', async () => { + // TODO: pick a new url; this Harvard one is 404 + xit('navigates a manifest using thumbnail navigation', async () => { await expect(page).toMatchElement('.mirador-thumb-navigation'); let windows = await page.evaluate(() => ( miradorInstance.store.getState().windows @@ -20,7 +21,7 @@ describe('Thumbnail navigation', () => { )); expect(Object.values(windows)[0].canvasId).toBe('https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-18737483'); // canvas @ index 1 }); - it('displays on right side', async () => { + xit('displays on right side', async () => { await expect(page).toMatchElement('.mirador-thumb-navigation'); await expect(page).toMatchElement('.mirador-companion-area-far-bottom .mirador-thumb-navigation'); const windowId = await page.evaluate(() => { diff --git a/__tests__/src/components/AppProviders.test.js b/__tests__/src/components/AppProviders.test.js index 82e2f981e309ba39b5afbfc60e4b4304904bfa3d..ae97556638812588c2481172ad0e7bc3943d9a23 100644 --- a/__tests__/src/components/AppProviders.test.js +++ b/__tests__/src/components/AppProviders.test.js @@ -1,4 +1,4 @@ -import Button from '@material-ui/core/Button'; +import Button from '@mui/material/Button'; import { render, screen } from 'test-utils'; import { useTranslation } from 'react-i18next'; import { useDrop } from 'react-dnd'; diff --git a/__tests__/src/components/CollapsibleSection.test.js b/__tests__/src/components/CollapsibleSection.test.js index 9e3ee8757ca6837b22d788e51ecc0ac879fb03e6..aad2dd0f4c16bbce9bfa1a7e826d4cf525baae70 100644 --- a/__tests__/src/components/CollapsibleSection.test.js +++ b/__tests__/src/components/CollapsibleSection.test.js @@ -34,9 +34,9 @@ describe('CollapsibleSection', () => { expect(screen.getByRole('button')).toHaveAttribute('aria-label', 'expandSection'); }); - it('renders children based on the open state', async () => { - expect(screen.getByTestId('child')).toBeInTheDocument(); + it('displays children based on the open state', async () => { + expect(screen.getByTestId('child')).toBeVisible(); await userEvent.click(screen.getByRole('button')); - expect(screen.queryByTestId('child')).not.toBeInTheDocument(); + expect(screen.queryByTestId('child')).not.toBeVisible(); }); }); diff --git a/__tests__/src/components/CompanionArea.test.js b/__tests__/src/components/CompanionArea.test.js index 404359b6af87d87a52ae7bd54e2f992a4a8adb9b..53e7bcdb0a5d1e347e664b42f4fe945c78371c2d 100644 --- a/__tests__/src/components/CompanionArea.test.js +++ b/__tests__/src/components/CompanionArea.test.js @@ -30,7 +30,7 @@ describe('CompanionArea', () => { it('should add the appropriate classes when the companion area fills the full width', () => { const { container } = createWrapper({ position: 'bottom' }); - expect(container.querySelector('.mirador-companion-area-bottom')).toHaveClass('horizontal'); // eslint-disable-line testing-library/no-node-access, testing-library/no-container + expect(container.querySelector('.mirador-companion-area-bottom')).toBeInTheDocument(); // eslint-disable-line testing-library/no-node-access, testing-library/no-container }); it('renders the appropriate <CompanionWindow> components', () => { diff --git a/__tests__/src/components/CompanionWindow.test.js b/__tests__/src/components/CompanionWindow.test.js index 60343426fe4b741bc80ad827b67adaf8cc742849..a7fe7b71ef7f4c22b3a3838545c029845a11e84d 100644 --- a/__tests__/src/components/CompanionWindow.test.js +++ b/__tests__/src/components/CompanionWindow.test.js @@ -11,7 +11,6 @@ function createWrapper(props) { isDisplayed direction="ltr" windowId="x" - classes={{ horizontal: 'horizontal', small: 'small', vertical: 'vertical' }} companionWindow={{}} position="right" {...props} @@ -96,9 +95,10 @@ describe('CompanionWindow', () => { updateCompanionWindow, }); - expect(screen.getByRole('complementary')).toHaveClass('vertical'); + expect(screen.getByRole('complementary')).toHaveClass('mirador-companion-window-right'); await user.click(screen.getByRole('button', { name: 'moveCompanionWindowToBottom' })); + expect(updateCompanionWindow).toHaveBeenCalledWith({ position: 'bottom' }); }); }); @@ -113,7 +113,7 @@ describe('CompanionWindow', () => { updateCompanionWindow, }); - expect(screen.getByRole('complementary')).toHaveClass('horizontal'); + expect(screen.getByRole('complementary')).toHaveClass('mirador-companion-window-bottom '); await user.click(screen.getByRole('button', { name: 'moveCompanionWindowToRight' })); @@ -127,12 +127,6 @@ describe('CompanionWindow', () => { expect(screen.getByTestId('xyz')).toBeInTheDocument(); }); - it('adds a small class when the component width is small', () => { - const { container } = createWrapper({ size: { width: 369 } }); - - expect(container.querySelector('.MuiToolbar-root')).toHaveClass('small'); // eslint-disable-line testing-library/no-node-access, testing-library/no-container - }); - it('has a resize handler', () => { const { container } = createWrapper(); diff --git a/__tests__/src/components/GalleryViewThumbnail.test.js b/__tests__/src/components/GalleryViewThumbnail.test.js index 3ff910b0865188bef23cb3062272b4fcde964a73..bad9e3098edd648ca859b814766c6ddfff075a09 100644 --- a/__tests__/src/components/GalleryViewThumbnail.test.js +++ b/__tests__/src/components/GalleryViewThumbnail.test.js @@ -11,7 +11,6 @@ function createWrapper(props) { return render( <GalleryViewThumbnail canvas={Utils.parseManifest(manifestJson).getSequences()[0].getCanvases()[0]} - classes={{ selected: 'selected' }} focusOnCanvas={() => {}} setCanvas={() => {}} {...props} @@ -23,26 +22,6 @@ describe('GalleryView', () => { beforeEach(() => { window.HTMLElement.prototype.scrollIntoView = jest.fn(); }); - it('sets a mirador-current-canvas-grouping class when the canvas is selected', () => { - createWrapper({ selected: true }); - expect(screen.getByRole('button')).toBeInTheDocument(); - expect(screen.getByRole('button')).toHaveClass('selected'); - }); - it('does not set a mirador-current-canvas-grouping class when the canvas is not selected', () => { - createWrapper({ selected: false }); - expect(screen.getByRole('button')).toBeInTheDocument(); - expect(screen.getByRole('button')).not.toHaveClass('selected'); - }); - it('sets a mirador-current-canvas-grouping class when the canvas is selected', () => { - createWrapper({ selected: true }); - expect(screen.getByRole('button')).toBeInTheDocument(); - expect(screen.getByRole('button')).toHaveClass('selected'); - }); - it('does not set a mirador-current-canvas-grouping class when the canvas is not selected', () => { - createWrapper({ selected: false }); - expect(screen.getByRole('button')).toBeInTheDocument(); - expect(screen.getByRole('button')).not.toHaveClass('selected'); - }); it('renders the thumbnail', () => { createWrapper({ config: { height: 55 } }); expect(screen.getByRole('presentation')).toBeInTheDocument(); diff --git a/__tests__/src/components/IIIFAuthentication.test.js b/__tests__/src/components/IIIFAuthentication.test.js index d9be920ac9d4ae9b4eeb14415b3b03720c271382..d49d5e712d86dfd835d1353a816ed54910da433b 100644 --- a/__tests__/src/components/IIIFAuthentication.test.js +++ b/__tests__/src/components/IIIFAuthentication.test.js @@ -33,7 +33,7 @@ describe('IIIFAuthentication', () => { describe('without an auth service', () => { it('renders nothing', () => { createWrapper({ authServiceId: null }); - expect(screen.queryByText('login', { selector: 'span' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'login' })).not.toBeInTheDocument(); expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); }); @@ -41,9 +41,7 @@ describe('IIIFAuthentication', () => { it('renders a login bar', async () => { const handleAuthInteraction = jest.fn(); createWrapper({ handleAuthInteraction }); - const confirmBtn = screen.getByText('login', { selector: 'span' }); - expect(confirmBtn).toBeInTheDocument(); - await user.click(confirmBtn); + await user.click(screen.getByRole('button', { name: 'login' })); expect(handleAuthInteraction).toHaveBeenCalledWith('w', 'http://example.com/auth'); }); it('renders nothing for a non-interactive login', () => { @@ -55,7 +53,8 @@ describe('IIIFAuthentication', () => { it('renders with an error message', async () => { const handleAuthInteraction = jest.fn(); createWrapper({ handleAuthInteraction, status: 'failed' }); - const confirmBtn = await screen.findByText('retry', { selector: 'span' }); + await user.click(screen.getByRole('button', { name: 'continue' })); + const confirmBtn = screen.getByRole('button', { name: /retry/ }); expect(screen.getByText('Login failed')).toBeInTheDocument(); expect(screen.getByText('cancel')).toBeInTheDocument(); expect(screen.getByText('... and this is why.')).toBeInTheDocument(); @@ -71,7 +70,7 @@ describe('IIIFAuthentication', () => { window.open = mockWindowOpen; const resolveCookieMock = jest.fn(); createWrapper({ resolveAuthenticationRequest: resolveCookieMock, status: 'cookie' }); - expect(screen.getByText('login', { selector: 'span' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'login' })).toBeInTheDocument(); expect(mockWindowOpen).toHaveBeenCalledWith(`http://example.com/auth?origin=${window.origin}`, 'IiifLoginSender', 'centerscreen'); mockWindow.closed = true; jest.runOnlyPendingTimers(); @@ -81,7 +80,7 @@ describe('IIIFAuthentication', () => { it('does the IIIF access token behavior', async () => { const resolveTokenMock = jest.fn(); createWrapper({ resolveAccessTokenRequest: resolveTokenMock, status: 'token' }); - expect(screen.getByText('login', { selector: 'span' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'login' })).toBeInTheDocument(); window.dispatchEvent(new MessageEvent('message', { data: { messageId: 'http://example.com/token' }, })); @@ -100,7 +99,7 @@ describe('IIIFAuthentication', () => { resetAuthenticationState, status: 'ok', }); - const confirmBtn = await screen.findByText('exit', { selector: 'span' }); + const confirmBtn = await screen.findByRole('button', { name: 'exit' }); await user.click(confirmBtn); await waitFor(() => expect(resetAuthenticationState).toHaveBeenCalledWith({ authServiceId: 'http://example.com/auth', tokenServiceId: 'http://example.com/token', diff --git a/__tests__/src/components/LanguageSettings.test.js b/__tests__/src/components/LanguageSettings.test.js index 0cddf8b5db29fe186da2a48ddf186f6e5f614ef2..98b0a82f6d7bce81ff7767993531b8377eef9b50 100644 --- a/__tests__/src/components/LanguageSettings.test.js +++ b/__tests__/src/components/LanguageSettings.test.js @@ -35,13 +35,6 @@ describe('LanguageSettings', () => { expect(screen.getAllByRole('menuitem')).toHaveLength(2); }); - it('non-active list items are buttons (and active are not)', () => { - createWrapper({ languages }); - - expect(screen.getByRole('menuitem', { name: 'Deutsch' })).not.toHaveClass('MuiButtonBase-root'); - expect(screen.getByRole('menuitem', { name: 'English' })).toHaveClass('MuiButtonBase-root'); - }); - it('renders the check icon when the active prop returns true', () => { createWrapper({ languages }); diff --git a/__tests__/src/components/LocalePicker.test.js b/__tests__/src/components/LocalePicker.test.js index a37bdb4bd927a0eb503c6e0b1136d08f99af7e9f..2ff1b2ceefa4c4e1f96a2a06ee1ead0476de8e85 100644 --- a/__tests__/src/components/LocalePicker.test.js +++ b/__tests__/src/components/LocalePicker.test.js @@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event'; import { LocalePicker } from '../../../src/components/LocalePicker'; /** - * Helper function to create a shallow wrapper around LanguageSettings + * Helper function to create a shallow wrapper around LocalePicker */ function createWrapper(props) { return render( @@ -24,36 +24,42 @@ describe('LocalePicker', () => { }); it('renders a select with the current value', () => { - createWrapper({ availableLocales: ['en', 'de'], locale: 'en' }); - - expect(screen.getByRole('button')).toHaveTextContent('en'); + createWrapper({ availableLocales: ['en', 'de'], locale: 'de' }); + // The option to expand the dropdown menu is rendered by a CompanionWindow titleControls prop in WindowSideBarInfoPanel, which is a combobox + const dropdownTitle = screen.getByRole('combobox'); + expect(dropdownTitle).toHaveTextContent('de'); }); - it('renders a select with a list item for each language passed in props', async () => { + it('renders a select with both options and sets the current value', async () => { const user = userEvent.setup(); - - createWrapper({ availableLocales: ['en', 'de'], locale: 'en' }); - - await user.click(screen.getByRole('button')); - - expect(screen.getAllByRole('option')).toHaveLength(2); + createWrapper({ availableLocales: ['en', 'de'], locale: 'de' }); + const dropdownTitle = screen.getByRole('combobox'); + // Open the menu + await user.click(dropdownTitle); + // The dropddown menu is not nested within the combobox, it is a sibling in the DOM, an MuiMenu + const menu = screen.getByRole('listbox'); + // Assert that the menu element has 2 children (2 options) + expect(menu.children).toHaveLength(2); // eslint-disable-line testing-library/no-node-access + // Verify that the select element has the correct value ('de') + const deOption = screen.getByRole('option', { name: 'de' }); + expect(deOption).toHaveAttribute('aria-selected', 'true'); + // Verify en is also an option expect(screen.getByRole('option', { name: 'en' })).toBeInTheDocument(); - expect(screen.getByRole('option', { name: 'de' })).toBeInTheDocument(); }); it('triggers setLocale prop when clicking a list item', async () => { const user = userEvent.setup(); const setLocale = jest.fn(); - createWrapper({ availableLocales: ['en', 'de'], locale: 'en', setLocale, }); - - await user.click(screen.getByRole('button')); + const dropdownTitle = screen.getByRole('combobox'); + // Open the Select component + await user.click(dropdownTitle); + // Change the locale to 'de' await user.click(screen.getByRole('option', { name: 'de' })); - expect(setLocale).toHaveBeenCalledTimes(1); expect(setLocale).toHaveBeenCalledWith('de'); }); diff --git a/__tests__/src/components/ManifestListItem.test.js b/__tests__/src/components/ManifestListItem.test.js index ac40ea45865438b9d687ffb8c8d428db64eeb2f6..bd444de9156ad4b77d10e828de451631c19bb105 100644 --- a/__tests__/src/components/ManifestListItem.test.js +++ b/__tests__/src/components/ManifestListItem.test.js @@ -23,18 +23,23 @@ describe('ManifestListItem', () => { createWrapper({ buttonRef: 'ref' }); expect(screen.getByRole('listitem')).toHaveAttribute('data-manifestid', 'http://example.com'); + expect(screen.getByRole('listitem')).toHaveClass('MuiListItem-root'); expect(screen.getByRole('button')).toHaveTextContent('xyz'); }); it('adds a class when the item is active', () => { createWrapper({ active: true, classes: { active: 'active' } }); + // If this is true, we can assume the proper styling classes are being applied + expect(screen.getByRole('listitem')).toHaveAttribute('data-active', 'true'); + expect(screen.getByRole('listitem')).toHaveClass('active'); + expect(screen.getByRole('listitem')).toHaveClass('Mui-selected'); }); it('renders a placeholder element until real data is available', () => { const { container } = createWrapper({ ready: false }); expect(screen.queryByRole('button')).not.toBeInTheDocument(); - expect(container.querySelectorAll('.MuiSkeleton-rect').length).toBeGreaterThan(0); // eslint-disable-line testing-library/no-node-access, testing-library/no-container + expect(container.querySelectorAll('.MuiSkeleton-rectangular').length).toBeGreaterThan(0); // eslint-disable-line testing-library/no-node-access, testing-library/no-container }); it('renders an error message if fetching the manifest failed', () => { createWrapper({ error: 'This is an error message' }); diff --git a/__tests__/src/components/PrimaryWindow.test.js b/__tests__/src/components/PrimaryWindow.test.js index 52ca6c86b82a9c87bcf08e37c7a09b33c5bc3db8..ad78ebf4b5cf39c5f7433721471429ded5b96237 100644 --- a/__tests__/src/components/PrimaryWindow.test.js +++ b/__tests__/src/components/PrimaryWindow.test.js @@ -16,7 +16,7 @@ function createWrapper(props) { describe('PrimaryWindow', () => { it('should render expected elements', async () => { createWrapper({ isFetching: false }); - await screen.findByRole('region', { accessibleName: 'item' }); + await screen.findByTestId('test-window'); expect(document.querySelector('.mirador-primary-window')).toBeInTheDocument(); // eslint-disable-line testing-library/no-node-access expect(document.querySelector('.mirador-companion-area-left')).toBeInTheDocument(); // eslint-disable-line testing-library/no-node-access }); @@ -30,7 +30,7 @@ describe('PrimaryWindow', () => { }); it('should render <GalleryView> if fetching is complete and view is gallery', async () => { createWrapper({ isFetching: false, view: 'gallery' }); - await screen.findByRole('region', { accessibleName: 'gallery section' }); + await screen.findByTestId('test-window'); expect(document.querySelector('#xyz-gallery')).toBeInTheDocument(); // eslint-disable-line testing-library/no-node-access }); it('should render <CollectionDialog> and <SelectCollection> if manifest is collection and isCollectionDialogVisible', async () => { diff --git a/__tests__/src/components/SanitizedHtml.test.js b/__tests__/src/components/SanitizedHtml.test.js index 37c09f96c12440c7ab051fe23ca45626754cb3ee..382b90570e5ca8748fab1db2d7da25746e9c1f90 100644 --- a/__tests__/src/components/SanitizedHtml.test.js +++ b/__tests__/src/components/SanitizedHtml.test.js @@ -6,7 +6,6 @@ describe('SanitizedHtml', () => { render( <SanitizedHtml data-testid="subject" - classes={{ root: 'root' }} htmlString="<script>doBadThings()</script><b>Don't worry!</b><a>Some link</a>" ruleSet="iiif" />, @@ -18,7 +17,6 @@ describe('SanitizedHtml', () => { }); it('should pass correct class name to root element', () => { - expect(screen.getByTestId('subject')).toHaveClass('root'); expect(screen.getByTestId('subject')).toHaveClass('mirador-third-party-html'); }); diff --git a/__tests__/src/components/ScrollIndicatedDialogContent.test.js b/__tests__/src/components/ScrollIndicatedDialogContent.test.js index 4c76018dca7d1c4614071af837c6e6ec493566ee..ae84979485a464c2d6b98928a42082833702c223 100644 --- a/__tests__/src/components/ScrollIndicatedDialogContent.test.js +++ b/__tests__/src/components/ScrollIndicatedDialogContent.test.js @@ -6,7 +6,6 @@ function createWrapper(props) { return render( <ScrollIndicatedDialogContent data-testid="subject" - classes={{ shadowScrollDialog: 'shadowScrollDialog' }} {...props} />, ); @@ -19,16 +18,8 @@ describe('ScrollIndicatedDialogContent', () => { expect(screen.getByTestId('subject')).toHaveAttribute('randomprop', 'randomPropValue'); }); - it('provides a className to the DialogContent prop to style it', () => { - createWrapper(); - - expect(screen.getByTestId('subject')).toHaveClass('shadowScrollDialog'); - }); - it('joins an incoming className prop with our className', () => { createWrapper({ className: 'upstreamClassName' }); - - expect(screen.getByTestId('subject')).toHaveClass('shadowScrollDialog'); expect(screen.getByTestId('subject')).toHaveClass('upstreamClassName'); }); }); diff --git a/__tests__/src/components/ScrollTo.test.js b/__tests__/src/components/ScrollTo.test.js index eb2f38c157394c6a7c6c1dd773ecc747fbf56cd4..fd4f34f087645e865eb56725a97705c40b80f7ca 100644 --- a/__tests__/src/components/ScrollTo.test.js +++ b/__tests__/src/components/ScrollTo.test.js @@ -1,4 +1,4 @@ -import { render, screen } from 'test-utils'; +import { render } from 'test-utils'; import { createRef } from 'react'; import { ScrollTo } from '../../../src/components/ScrollTo'; @@ -12,7 +12,7 @@ describe('ScrollTo', () => { containerRef = createRef(); render(<div data-testid="container" ref={containerRef} />); - containerRef.current.domEl = { + containerRef.current = { getBoundingClientRect: () => containerBoundingRect, getElementsByClassName: () => [{ scrollTo }], }; @@ -22,12 +22,6 @@ describe('ScrollTo', () => { const scrollToElBelowBoundingRect = { bottom: 601, top: 501 }; const visibleScrollToElBoundingRect = { bottom: 300, top: 200 }; - it('wraps the given children in a div element', () => { - render(<ScrollTo data-testid="subject" scrollTo>Child Prop</ScrollTo>); - - expect(screen.getByTestId('subject')).toHaveTextContent('Child Prop'); - }); - describe('when updating the scrollTo prop', () => { beforeEach(() => { jest.spyOn(ScrollTo.prototype, 'elementToScrollTo').mockImplementation(() => ({ offsetTop: 450 })); @@ -38,13 +32,13 @@ describe('ScrollTo', () => { ...scrollToElAboveBoundingRect, })); - const { rerender } = render(<ScrollTo scrollTo containerRef={containerRef}>Child</ScrollTo>); + const { rerender } = render(<ScrollTo scrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); // It is called once when initially rendered w/ true expect(scrollTo).toHaveBeenCalled(); scrollTo.mockReset(); - rerender(<ScrollTo containerRef={containerRef}>Child</ScrollTo>); + rerender(<ScrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); // But it is not called on the re-render w/ false expect(scrollTo).not.toHaveBeenCalled(); @@ -56,9 +50,9 @@ describe('ScrollTo', () => { jest.spyOn(ScrollTo.prototype, 'scrollToBoundingRect').mockImplementation(() => ({ ...scrollToElAboveBoundingRect, })); - const { rerender } = render(<ScrollTo containerRef={containerRef}>Child</ScrollTo>); + const { rerender } = render(<ScrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); - rerender(<ScrollTo scrollTo containerRef={containerRef}>Child</ScrollTo>); + rerender(<ScrollTo scrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); expect(scrollTo).toHaveBeenCalledWith(0, 230); }); @@ -68,9 +62,9 @@ describe('ScrollTo', () => { ...scrollToElBelowBoundingRect, })); - const { rerender } = render(<ScrollTo containerRef={containerRef}>Child</ScrollTo>); + const { rerender } = render(<ScrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); - rerender(<ScrollTo scrollTo containerRef={containerRef}>Child</ScrollTo>); + rerender(<ScrollTo scrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); expect(scrollTo).toHaveBeenCalledWith(0, 230); }); @@ -80,9 +74,9 @@ describe('ScrollTo', () => { ...visibleScrollToElBoundingRect, })); - const { rerender } = render(<ScrollTo containerRef={containerRef}>Child</ScrollTo>); + const { rerender } = render(<ScrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); - rerender(<ScrollTo scrollTo containerRef={containerRef}>Child</ScrollTo>); + rerender(<ScrollTo scrollTo containerRef={containerRef}><div>Child</div></ScrollTo>); expect(scrollTo).not.toHaveBeenCalled(); }); diff --git a/__tests__/src/components/SearchHit.test.js b/__tests__/src/components/SearchHit.test.js index 3db42a226f4f1be2ba8646ee75ab9804a274f318..e999a8b1e8efe2fa83a0294167d76ce2d21d49ae 100644 --- a/__tests__/src/components/SearchHit.test.js +++ b/__tests__/src/components/SearchHit.test.js @@ -24,7 +24,6 @@ const Subject = (props) => ( announcer={() => {}} annotation={{ targetId: 'x' }} annotationId="foo" - classes={{ windowSelected: 'windowSelected' }} hit={{ after: ', and start the chainsaw', annotations: ['foo'], @@ -46,7 +45,6 @@ describe('SearchHit', () => { render(<Subject selectAnnotation={selectAnnotation} />); expect(screen.getByRole('listitem')).toHaveClass('Mui-selected'); - expect(screen.getByRole('listitem')).toHaveClass('windowSelected'); expect(screen.getByRole('listitem')).toHaveTextContent('1Light up the moose , and start the chai more'); await user.click(screen.getByRole('button')); @@ -69,14 +67,13 @@ describe('SearchHit', () => { it('renders the annotationLabel if present', () => { render(<Subject annotationLabel="The Anno Label" />); - expect(screen.getAllByRole('heading', { level: 6 })).toHaveLength(2); - expect(screen.getByRole('heading', { level: 6, name: 'The Anno Label' })).toHaveClass('MuiTypography-subtitle2'); + expect(screen.getByRole('heading', { level: 4, name: 'The Anno Label' })).toBeInTheDocument(); }); it('does not render the typography if no annotation label is present', () => { render(<Subject />); - expect(screen.getByRole('heading', { level: 6 })).toBeInTheDocument(); + expect(screen.getByRole('heading', { level: 4 })).toBeInTheDocument(); }); }); diff --git a/__tests__/src/components/SearchPanel.test.js b/__tests__/src/components/SearchPanel.test.js index 8b4fb3a304ea83af9f521338171e3a1bdea74759..cd7b50ee01958a1ab2aff5109d3edd4e9b7ad274 100644 --- a/__tests__/src/components/SearchPanel.test.js +++ b/__tests__/src/components/SearchPanel.test.js @@ -54,7 +54,7 @@ describe('SearchPanel', () => { it('has the SearchPanelControls component', () => { createWrapper(); - expect(screen.getByRole('textbox', { name: 'searchInputLabel' })).toBeInTheDocument(); + expect(screen.getByRole('combobox', { name: 'searchInputLabel' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'searchSubmitAria' })).toBeInTheDocument(); }); diff --git a/__tests__/src/components/SearchPanelControls.test.js b/__tests__/src/components/SearchPanelControls.test.js index e2ab065c2730aafab356b1af114d26ca6e28513d..0b018912404980e4266fbf88a25092ac8f518e09 100644 --- a/__tests__/src/components/SearchPanelControls.test.js +++ b/__tests__/src/components/SearchPanelControls.test.js @@ -33,7 +33,7 @@ describe('SearchPanelControls', () => { searchService: { id: 'http://example.com/search', options: { resource: { id: 'abc' } } }, }); - await user.click(screen.getByRole('textbox')); + await user.click(screen.getByRole('combobox')); await user.keyboard('somestring'); await user.click(await screen.findByText('somestring 12345')); expect(fetchSearch).toHaveBeenCalledWith('window', 'cw', 'http://example.com/search?q=somestring+12345', 'somestring 12345'); @@ -44,7 +44,7 @@ describe('SearchPanelControls', () => { it('renders a text input through the renderInput prop', () => { createWrapper(); - expect(screen.getByRole('textbox')).toHaveAttribute('id', 'search-cw'); + expect(screen.getByRole('combobox')).toHaveAttribute('id', 'search-cw'); }); it('endAdornment is a SearchIcon (with no CircularProgress indicator)', () => { createWrapper(); @@ -67,7 +67,7 @@ describe('SearchPanelControls', () => { }; createWrapper({ fetchSearch, query: 'asdf', searchService }); - await user.clear(screen.getByRole('textbox')); + await user.clear(screen.getByRole('combobox')); await user.keyboard('yolo'); await user.click(screen.getByRole('button')); @@ -84,7 +84,7 @@ describe('SearchPanelControls', () => { createWrapper({ fetchSearch, query: '', searchService }); - await user.clear(screen.getByRole('textbox')); + await user.clear(screen.getByRole('combobox')); await user.click(screen.getByRole('button', { name: 'searchSubmitAria' })); expect(fetchSearch).not.toHaveBeenCalled(); }); @@ -92,12 +92,12 @@ describe('SearchPanelControls', () => { describe('input', () => { it('has the query prop has the input value on intial render', () => { createWrapper({ query: 'Wolpertinger' }); - expect(screen.getByRole('textbox')).toHaveValue('Wolpertinger'); + expect(screen.getByRole('combobox')).toHaveValue('Wolpertinger'); }); it('clears the local search state/input when the incoming query prop has been cleared', () => { const wrapper = createWrapper({ query: 'Wolpertinger' }); - expect(screen.getByRole('textbox')).toHaveValue('Wolpertinger'); + expect(screen.getByRole('combobox')).toHaveValue('Wolpertinger'); wrapper.rerender(( <SearchPanelControls @@ -107,7 +107,7 @@ describe('SearchPanelControls', () => { /> )); - expect(screen.getByRole('textbox')).toHaveValue(''); + expect(screen.getByRole('combobox')).toHaveValue(''); }); }); }); diff --git a/__tests__/src/components/SearchResults.test.js b/__tests__/src/components/SearchResults.test.js index 14a906cb6bf052c50fafea29a677a7f372039e35..3579a9789afeea5b5de2df963e75b981cfb92fb4 100644 --- a/__tests__/src/components/SearchResults.test.js +++ b/__tests__/src/components/SearchResults.test.js @@ -95,8 +95,8 @@ describe('SearchResults', () => { searchHits: [], }); - expect(screen.getByRole('heading', { level: 6, name: 'The Anno Label' })).toBeInTheDocument(); - expect(screen.getByRole('heading', { level: 6, name: 'Annother Anno Label' })).toBeInTheDocument(); + expect(screen.getByRole('heading', { level: 4, name: 'The Anno Label' })).toBeInTheDocument(); + expect(screen.getByRole('heading', { level: 4, name: 'Annother Anno Label' })).toBeInTheDocument(); }); }); diff --git a/__tests__/src/components/ThumbnailCanvasGrouping.test.js b/__tests__/src/components/ThumbnailCanvasGrouping.test.js index 96895ba47f9f91d210fbb08f3a061669089405f9..8bfff1d6d06dc63f5b0ff8a348b4eb52c744e72d 100644 --- a/__tests__/src/components/ThumbnailCanvasGrouping.test.js +++ b/__tests__/src/components/ThumbnailCanvasGrouping.test.js @@ -36,12 +36,13 @@ describe('ThumbnailCanvasGrouping', () => { setCanvas = jest.fn(); wrapper = createWrapper({ data, setCanvas }); }); + const spyCurrentCanvasClass = jest.spyOn(ThumbnailCanvasGrouping.prototype, 'currentCanvasClass'); + afterEach(() => { + spyCurrentCanvasClass.mockClear(); + }); it('renders', () => { expect(screen.getByRole('gridcell')).toBeInTheDocument(); }); - it('sets a mirador-current-canvas-grouping class on current canvas', () => { - expect(screen.getByRole('button')).toHaveClass('mirador-current-canvas-grouping'); - }); it('renders a CaptionedIIIFThumbnail', () => { expect(screen.getByText('Image 1')).toBeInTheDocument(); }); @@ -49,9 +50,9 @@ describe('ThumbnailCanvasGrouping', () => { wrapper.unmount(); const user = userEvent.setup(); wrapper = createWrapper({ data, index: 0, setCanvas }); - await user.click(wrapper.container.querySelector('.mirador-thumbnail-nav-canvas-0')); // eslint-disable-line testing-library/no-node-access - + expect(spyCurrentCanvasClass).toHaveBeenCalledWith([0]); + expect(spyCurrentCanvasClass).toHaveReturnedWith('current-canvas-grouping'); expect(setCanvas).toHaveBeenCalledWith('http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json'); }); describe('attributes based off far-bottom position', () => { diff --git a/__tests__/src/components/WindowAuthenticationBar.test.js b/__tests__/src/components/WindowAuthenticationBar.test.js index b47771b1d06e105ceb32e335cae3ce96d8038a57..c4368b81132bfd1c1202473d4fc296444eb73404 100644 --- a/__tests__/src/components/WindowAuthenticationBar.test.js +++ b/__tests__/src/components/WindowAuthenticationBar.test.js @@ -37,38 +37,36 @@ describe('AuthenticationControl', () => { it('renders a non-collapsing version if there is no description', () => { createWrapper({ description: undefined, header: undefined }); expect(screen.getByText('Log in to see more', { selector: 'span' })).toBeInTheDocument(); - expect(screen.getByText('Login', { selector: 'span' })).toBeInTheDocument(); + expect(screen.getByRole('button')).toHaveTextContent('Login'); }); it('renders a collapsable version if there is a description', async () => { createWrapper({ description: 'long description', header: 'Login to Example Institution' }); const continueBtn = document.querySelectorAll('.MuiButtonBase-root')[0]; - const cancelBtn = screen.getByText('cancel', { selector: 'span' }).closest('button'); - const loginBtn = screen.getByText('Login', { selector: 'span' }); const collapseEl = document.querySelector('.MuiCollapse-hidden'); // disable transition animations for easier testing of the Mui Collapse open/close state config.disabled = true; // initial collapsed state: Presence of continue button text. Hidden cancelBtn, loginBtn, and description expect(screen.getByText('continue')).toBeInTheDocument(); - expect(cancelBtn).not.toBeVisible(); - expect(loginBtn).not.toBeVisible(); + expect(screen.queryByRole('button', { name: 'cancel' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Login' })).not.toBeInTheDocument(); expect(within(collapseEl).getByText('long description')).not.toBeVisible(); // click to expand await user.click(continueBtn); // expanded state: Removal of continue button text from DOM. Visible cancelBtn, loginBtn, and description expect(screen.queryByText('continue')).not.toBeInTheDocument(); - expect(cancelBtn).toBeVisible(); - expect(loginBtn).toBeVisible(); + expect(screen.getByRole('button', { name: 'cancel' })).toBeVisible(); + expect(screen.getByRole('button', { name: 'Login' })).toBeVisible(); expect(within(collapseEl).getByText('long description')).toBeVisible(); expect(collapseEl).toHaveClass('MuiCollapse-entered'); // click the cancel button to collapse - await user.click(cancelBtn); + await user.click(screen.getByRole('button', { name: 'cancel' })); // collapsed state: Presence of continue button text. Hidden cancelBtn, loginBtn, and description expect(screen.getByText('continue')).toBeInTheDocument(); - expect(cancelBtn).not.toBeVisible(); - expect(loginBtn).not.toBeVisible(); + expect(screen.queryByRole('button', { name: 'cancel' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Login' })).not.toBeInTheDocument(); expect(within(collapseEl).getByText('long description')).not.toBeVisible(); // re-enable transition animation config.disabled = false; @@ -77,7 +75,7 @@ describe('AuthenticationControl', () => { it('triggers an action when the confirm button is clicked', async () => { const onConfirm = jest.fn(); createWrapper({ onConfirm }); - await user.click(screen.getByText('Login', { selector: 'span' }).closest('button')); + await user.click(screen.getByRole('button', { name: 'Login' })); expect(onConfirm).toHaveBeenCalled(); }); diff --git a/__tests__/src/components/WindowCanvasNavigationControls.test.js b/__tests__/src/components/WindowCanvasNavigationControls.test.js index d089de29157852f4bd77cb0d56e582cac1145b7f..de08147cc359a800fcaae3b5f26e54b996c67af1 100644 --- a/__tests__/src/components/WindowCanvasNavigationControls.test.js +++ b/__tests__/src/components/WindowCanvasNavigationControls.test.js @@ -28,7 +28,7 @@ describe('WindowCanvasNavigationControls', () => { it('renders only a screen-reader accessibile version when visible=false', () => { const { container } = render(<Subject visible={false} />); - expect(container.firstChild.classList[1]).toMatch(/srOnly/); // eslint-disable-line testing-library/no-node-access + expect(container.firstChild).toHaveStyle({ height: '1px', margin: '-1px', width: '1px' }); // eslint-disable-line testing-library/no-node-access }); it('stacks the nav controls on small width screens', () => { @@ -37,10 +37,7 @@ describe('WindowCanvasNavigationControls', () => { }); it('shows the zoom control component when specified', () => { - render( - <Subject />, - { preloadedState: { workspace: { showZoomControls: true } } }, - ); + render(<Subject showZoomControls />); expect(screen.getByRole('button', { name: 'zoomIn' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'zoomOut' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'zoomReset' })).toBeInTheDocument(); diff --git a/__tests__/src/components/WindowSideBar.test.js b/__tests__/src/components/WindowSideBar.test.js index 895e20fb3516246cd1e681630d9733af96dc8133..0fa75243cf9bb7be907346cdfc7bdd06db3ffd20 100644 --- a/__tests__/src/components/WindowSideBar.test.js +++ b/__tests__/src/components/WindowSideBar.test.js @@ -5,7 +5,6 @@ import { WindowSideBar } from '../../../src/components/WindowSideBar'; function createWrapper({ ...props }) { return render( <WindowSideBar - classes={{ drawer: 'test-drawer' }} t={k => k} windowId="xyz" {...props} @@ -26,7 +25,6 @@ function createWrapper({ ...props }) { describe('WindowSideBar when closed', () => { it('renders without an error', () => { createWrapper({}); - expect(document.querySelector('.test-drawer')).toBeInTheDocument(); // eslint-disable-line testing-library/no-node-access expect(screen.queryByRole('navigation', { accessibleName: 'sidebarPanelsNavigation' })).not.toBeInTheDocument(); }); }); diff --git a/__tests__/src/components/WindowSideBarCanvasPanel.test.js b/__tests__/src/components/WindowSideBarCanvasPanel.test.js index 9ad3ce6cca69634ab1cc5015e595680a894fb1bf..9c3c48ca0ac8ebef6279fba2c6ba317018967054 100644 --- a/__tests__/src/components/WindowSideBarCanvasPanel.test.js +++ b/__tests__/src/components/WindowSideBarCanvasPanel.test.js @@ -65,7 +65,7 @@ describe('WindowSideBarCanvasPanel', () => { createWrapper({ multipleSequences: true, updateSequence }); expect(screen.getByTestId('sequence-select')).toHaveTextContent('a'); - await user.click(within(screen.getByTestId('sequence-select')).getByRole('button')); + await user.click(within(screen.getByTestId('sequence-select')).getByRole('combobox')); const listbox = within(screen.getByRole('listbox')); expect(listbox.getAllByRole('option')).toHaveLength(2); diff --git a/__tests__/src/components/WindowTopMenuButton.test.js b/__tests__/src/components/WindowTopMenuButton.test.js index 86ef9584d47c462e9db7ba0f522c36f3e8ea7013..15d4b65f42d325a951427a68d261e999aba4f1dd 100644 --- a/__tests__/src/components/WindowTopMenuButton.test.js +++ b/__tests__/src/components/WindowTopMenuButton.test.js @@ -38,9 +38,16 @@ describe('WindowTopMenuButton', () => { expect(screen.queryByRole('menu')).not.toBeInTheDocument(); }); - it('the button has a class indicating that it is "selected" once it is clicked', async () => { + it('the open attribute of the button is null without being clicked', async () => { + render(<Subject />); + // without a click, the button is not open and therefore doesn't have aria-owns attr + expect(screen.getByLabelText('windowMenu')).not.toHaveAttribute('aria-owns'); // eslint-disable-line testing-library/no-node-access + }); + + it('the open attribute of the button is applied once it is clicked', async () => { render(<Subject />); await user.click(screen.getByLabelText('windowMenu')); - expect(screen.getByLabelText('windowMenu')).toHaveClass('ctrlBtnSelected'); // eslint-disable-line testing-library/no-node-access + // when 'open' is true, aria-owns is set to the id of the window + expect(screen.getByLabelText('windowMenu')).toHaveAttribute('aria-owns', 'window-menu_xyz'); // eslint-disable-line testing-library/no-node-access }); }); diff --git a/__tests__/src/components/Workspace.test.js b/__tests__/src/components/Workspace.test.js index 3f1fd853e79f7aec16a9fad1ab6b05b789675b1b..493598f751109c49936e69dd95271179c79dbbdb 100644 --- a/__tests__/src/components/Workspace.test.js +++ b/__tests__/src/components/Workspace.test.js @@ -83,22 +83,6 @@ describe('Workspace', () => { }); }); - describe('when the workspace control panel is displayed', () => { - it('has the *-with-control-panel class applied', () => { - const { container } = createWrapper(); - - expect(container.querySelector('.mirador-workspace-with-control-panel')).toBeInTheDocument(); - }); - }); - - describe('when the workspace control panel is not displayed', () => { - it('does not have the *-with-control-panel class applied', () => { - const { container } = createWrapper({ isWorkspaceControlPanelVisible: false }); - - expect(container.querySelector('.mirador-workspace-with-control-panel')).not.toBeInTheDocument(); - }); - }); - describe('drag and drop', () => { it('adds a new catalog entry from a manifest', async () => { const manifestJson = '{ "data": "123" }'; @@ -106,7 +90,7 @@ describe('Workspace', () => { const addWindow = jest.fn(); const { container } = createWrapper({ addWindow }); - const dropTarget = container.querySelector('.mirador-workspace-with-control-panel'); + const dropTarget = container.querySelector('.mirador-workspace-viewport'); const file = new File([manifestJson], 'manifest.json', { type: 'application/json' }); const dataTransfer = { @@ -129,7 +113,7 @@ describe('Workspace', () => { const { container } = createWrapper({ addWindow, allowNewWindows: false }); - const dropTarget = container.querySelector('.mirador-workspace-with-control-panel'); + const dropTarget = container.querySelector('.mirador-workspace-viewport'); const file = new File([manifestJson], 'manifest.json', { type: 'application/json' }); const dataTransfer = { diff --git a/__tests__/src/components/WorkspaceElasticWindow.test.js b/__tests__/src/components/WorkspaceElasticWindow.test.js index 5d8982ea6b553f3bafc7fa8ae0fb103ff307c8d5..03f565afd16baebc97bd1774e00dc388eb0273df 100644 --- a/__tests__/src/components/WorkspaceElasticWindow.test.js +++ b/__tests__/src/components/WorkspaceElasticWindow.test.js @@ -46,18 +46,13 @@ describe('WorkspaceElasticWindow', () => { expect(el).toHaveStyle({ height: '200px', transform: 'translate(5040px,5040px)', width: '200px' }); }); describe('focuses the window', () => { - it('adds class to focused window', () => { - const { container } = createWrapper({ classes: { focused: 'focused-window' }, focused: true, layout }); - const el = container.firstChild; - - expect(el).toHaveClass('focused-window'); - }); it('calls focusWindow when clicked', async () => { const user = userEvent.setup(); const mockFocusWindow = jest.fn(); const { container } = createWrapper({ focusWindow: mockFocusWindow, layout }); - const el = container.querySelector('.mirador-window-top-bar'); - await user.click(el); + const topBar = container.querySelector('.mirador-window-top-bar'); + await user.click(topBar); + expect(mockFocusWindow).toHaveBeenCalled(); }); }); diff --git a/__tests__/src/components/ZoomControls.test.js b/__tests__/src/components/ZoomControls.test.js index 6f7be5e3ad62133eec3c26a4b1ddadb9eda7dfbe..41a562513eb8b5d87d6765b003ebbc9771046786 100644 --- a/__tests__/src/components/ZoomControls.test.js +++ b/__tests__/src/components/ZoomControls.test.js @@ -6,74 +6,47 @@ import { ZoomControls } from '../../../src/components/ZoomControls'; function createWrapper(props) { return render( <ZoomControls - classes={{ divider: 'divider', zoom_controls: 'zoom_controls' }} windowId="xyz" zoomToWorld={() => {}} {...props} - />, ); } describe('ZoomControls', () => { const viewer = { x: 100, y: 100, zoom: 1 }; - const showZoomControls = false; let updateViewport; - describe('with showZoomControls=false', () => { - it('renders nothing unless asked', () => { - const { container } = createWrapper({ showZoomControls, updateViewport, viewer }); - expect(container).toBeEmptyDOMElement(); + const zoomToWorld = jest.fn(); + let user; + beforeEach(() => { + user = userEvent.setup(); + updateViewport = jest.fn(); + createWrapper({ + updateViewport, viewer, zoomToWorld, }); }); - describe('with showZoomControls=true', () => { - const zoomToWorld = jest.fn(); - let user; - beforeEach(() => { - user = userEvent.setup(); - updateViewport = jest.fn(); - createWrapper({ - showZoomControls: true, updateViewport, viewer, zoomToWorld, - }); - }); - - it('renders a couple buttons', () => { - expect(screen.getByRole('button', { name: 'zoomIn' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'zoomOut' })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: 'zoomReset' })).toBeInTheDocument(); - }); - - it('has a zoom-in button', async () => { - await user.click(screen.getByRole('button', { name: 'zoomIn' })); - - expect(updateViewport).toHaveBeenCalledWith('xyz', { zoom: 2 }); - }); - - it('has a zoom-out button', async () => { - await user.click(screen.getByRole('button', { name: 'zoomOut' })); - expect(updateViewport).toHaveBeenCalledWith('xyz', { zoom: 0.5 }); - }); + it('renders a couple buttons', () => { + expect(screen.getByRole('button', { name: 'zoomIn' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'zoomOut' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: 'zoomReset' })).toBeInTheDocument(); + }); - it('has a zoom reset button', async () => { - await user.click(screen.getByRole('button', { name: 'zoomReset' })); + it('has a zoom-in button', async () => { + await user.click(screen.getByRole('button', { name: 'zoomIn' })); - expect(zoomToWorld).toHaveBeenCalledWith(false); - }); + expect(updateViewport).toHaveBeenCalledWith('xyz', { zoom: 2 }); }); - /* eslint-disable testing-library/no-container, testing-library/no-node-access */ - describe('responsive divider', () => { - it('is present when the displayDivider prop is true (default)', () => { - const { container } = createWrapper({ showZoomControls: true, viewer }); - - expect(container.querySelector('.divider')).toBeInTheDocument(); - }); + it('has a zoom-out button', async () => { + await user.click(screen.getByRole('button', { name: 'zoomOut' })); + expect(updateViewport).toHaveBeenCalledWith('xyz', { zoom: 0.5 }); + }); - it('is not present when the displayDivider prop is false', () => { - const { container } = createWrapper({ displayDivider: false, showZoomControls: true, viewer }); + it('has a zoom reset button', async () => { + await user.click(screen.getByRole('button', { name: 'zoomReset' })); - expect(container.querySelector('.divider')).not.toBeInTheDocument(); - }); + expect(zoomToWorld).toHaveBeenCalledWith(false); }); }); diff --git a/__tests__/utils/test-utils.js b/__tests__/utils/test-utils.js index 7bc1adf23faae99a0d9146a63145f7947dea6088..7104dec6b038df0a91a8744b5378e22aeab848e9 100644 --- a/__tests__/utils/test-utils.js +++ b/__tests__/utils/test-utils.js @@ -3,9 +3,12 @@ import { render } from '@testing-library/react'; import PropTypes from 'prop-types'; import { createStore, applyMiddleware } from 'redux'; import thunkMiddleware from 'redux-thunk'; +import { createTheme, ThemeProvider } from '@mui/material/styles'; import createRootReducer from '../../src/state/reducers/rootReducer'; +import settings from '../../src/config/settings'; const rootReducer = createRootReducer(); +const theme = createTheme(settings.theme); /** * Hook up our rendered object to redux @@ -21,7 +24,7 @@ function renderWithProviders( ) { /** :nodoc: */ function Wrapper({ children }) { - return <Provider store={store}>{children}</Provider>; + return <ThemeProvider theme={theme}><Provider store={store}>{children}</Provider></ThemeProvider>; } Wrapper.propTypes = { diff --git a/jest-puppeteer.config.js b/jest-puppeteer.config.js index b7f3d29270c58ad2de562ff6eceb7c3ffa280179..31bf265ad41b8b662bbac4fa8cb0062e7652777a 100644 --- a/jest-puppeteer.config.js +++ b/jest-puppeteer.config.js @@ -1,6 +1,6 @@ module.exports = { launch: { - headless: process.env.HEADLESS !== 'false', + headless: process.env.HEADLESS !== 'false' ? 'new' : false, }, server: [{ command: 'npm run server -- -p 4488', diff --git a/jest.json b/jest.json index b5713a9dc77afc15459a2b531716ba0d63c852f3..947d95cd44f04a7c4501be996ca01b82001e7832 100644 --- a/jest.json +++ b/jest.json @@ -33,7 +33,7 @@ "<rootDir>/__tests__/utils" ], "preset": "jest-puppeteer", - "setupFilesAfterEnv": ["@testing-library/jest-dom/extend-expect"], + "setupFilesAfterEnv": ["@testing-library/jest-dom/jest-globals"], "transformIgnorePatterns": [ "<rootDir>/node_modules/(?!@react-dnd|react-dnd|dnd-core|react-dnd-html5-backend|dnd-multi-backend|rdndmb-html5-to-touch)" ], @@ -58,7 +58,7 @@ "preset": "jest-puppeteer", "setupFilesAfterEnv": [ "<rootDir>/setupJestIntegration.js", - "@testing-library/jest-dom/extend-expect" + "@testing-library/jest-dom/jest-globals" ], "transformIgnorePatterns": [ "<rootDir>/node_modules/(?!@react-dnd|react-dnd|dnd-core|react-dnd-html5-backend|dnd-multi-backend|rdndmb-html5-to-touch)" diff --git a/package.json b/package.json index 567470b7f75eb603e8af7c4f6618dd03b1a7608e..d77b1f238acfc073e66039b66cadfc3bbf861a69 100644 --- a/package.json +++ b/package.json @@ -33,18 +33,21 @@ ], "repository": "https://github.com/ProjectMirador/mirador", "dependencies": { + "@emotion/cache": "^11.11.0", + "@emotion/react": "^11.10.6", + "@emotion/styled": "^11.10.6", "@hello-pangea/dnd": "^16.0.1", - "@material-ui/core": "^4.12.3", - "@material-ui/icons": "^4.9.1", - "@material-ui/lab": "^4.0.0-alpha.53", + "@mui/icons-material": "^5.11.16", + "@mui/lab": "^5.0.0-alpha.134", + "@mui/material": "^5.13.5", + "@mui/utils": "^5.13.1", + "@mui/x-tree-view": "^6.17.0", "@react-aria/live-announcer": "^3.1.2", + "@redux-devtools/extension": "^3.3.0", "classnames": "^2.2.6", - "clsx": "^1.0.4", "deepmerge": "^4.2.2", "dompurify": "^3.0.0", - "i18next": "^21.0.0 || ^22.0.0", - "jss": "^10.3.0", - "jss-rtl": "^0.3.0", + "i18next": "^21.0.0 || ^22.0.0 || ^23.0.0", "lodash": "^4.17.11", "manifesto.js": "^4.2.0", "normalize-url": "^4.5.0", @@ -58,7 +61,7 @@ "react-dnd-multi-backend": "^8.0.0", "react-dnd-touch-backend": "^16.0.0", "react-full-screen": "^1.1.1", - "react-i18next": "^11.7.0 || ^12.0.0", + "react-i18next": "^11.7.0 || ^12.0.0 || ^13.0.0", "react-image": "^4.0.1", "react-intersection-observer": "^9.0.0", "react-mosaic-component": "^6.0.0", @@ -73,6 +76,8 @@ "redux-saga": "^1.1.3", "redux-thunk": "^2.3.0", "reselect": "^4.0.0", + "stylis": "^4.3.0", + "stylis-plugin-rtl": "^2.1.1", "url": "^0.11.0", "uuid": "^8.1.0 || ^9.0.0" }, @@ -88,7 +93,7 @@ "@babel/preset-react": "^7.16.7", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.4", "@testing-library/dom": "^9.2.0", - "@testing-library/jest-dom": "^5.16.5", + "@testing-library/jest-dom": "^6.1.5", "@testing-library/react": "^12.1.5", "@testing-library/user-event": "^14.4.3", "@typescript-eslint/eslint-plugin": "^5.15.0", @@ -108,19 +113,19 @@ "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jest": "^27.1.5", - "eslint-plugin-jest-dom": "^4.0.3", + "eslint-plugin-jest-dom": "^5.1.0", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-react": "^7.29.4", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-testing-library": "^5.10.2", - "glob": "^9.3.0", + "glob": "^10.3.0", "http-server": "^14.1.0", "jest": "^29.3.1", "jest-environment-jsdom": "^29.4.3", "jest-fetch-mock": "^3.0.0", - "jest-puppeteer": "^8.0.0", - "jsdom": "^21.0.0", - "puppeteer": "^19.0.0", + "jest-puppeteer": "^9.0.2", + "jsdom": "^23.0.0", + "puppeteer": "^21.0.0", "react": "^17.0.0", "react-dnd-test-backend": "^16.0.1", "react-dom": "^17.0.0", diff --git a/src/components/AnnotationSettings.js b/src/components/AnnotationSettings.js index dd91bdc754f4ef9f4494d62a1f43ce64593af6dd..f6fec27ad711fa018e2ce39177c5d302c31c2e5a 100644 --- a/src/components/AnnotationSettings.js +++ b/src/components/AnnotationSettings.js @@ -1,7 +1,7 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import VisibilityIcon from '@material-ui/icons/VisibilitySharp'; -import VisibilityOffIcon from '@material-ui/icons/VisibilityOffSharp'; +import VisibilityIcon from '@mui/icons-material/VisibilitySharp'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOffSharp'; import MiradorMenuButton from '../containers/MiradorMenuButton'; /** diff --git a/src/components/AppProviders.js b/src/components/AppProviders.js index e4bcddeac7af38bf14d8c537da32fcbc9717119f..f39aae60984e174a60d8a569b591bc78b6275753 100644 --- a/src/components/AppProviders.js +++ b/src/components/AppProviders.js @@ -3,13 +3,15 @@ import PropTypes from 'prop-types'; import { FullScreen, useFullScreenHandle } from 'react-full-screen'; import { I18nextProvider } from 'react-i18next'; import { - ThemeProvider, StylesProvider, createTheme, jssPreset, createGenerateClassName, -} from '@material-ui/core/styles'; + ThemeProvider, StyledEngineProvider, createTheme, +} from '@mui/material/styles'; import { DndContext, DndProvider } from 'react-dnd'; import { MultiBackend } from 'react-dnd-multi-backend'; import { HTML5toTouch } from 'rdndmb-html5-to-touch'; -import { create } from 'jss'; -import rtl from 'jss-rtl'; +import rtlPlugin from 'stylis-plugin-rtl'; +import { prefixer } from 'stylis'; +import { CacheProvider } from '@emotion/react'; +import createCache from '@emotion/cache'; import createI18nInstance from '../i18n'; import FullScreenContext from '../contexts/FullScreenContext'; @@ -92,12 +94,25 @@ export class AppProviders extends Component { /** */ render() { const { - children, createGenerateClassNameOptions, + children, theme, translations, dndManager, } = this.props; - const generateClassName = createGenerateClassName(createGenerateClassNameOptions); + /** + * Create rtl emotion cache + */ + const cacheRtl = createCache({ + key: 'muirtl', + stylisPlugins: [prefixer, rtlPlugin], + }); + + /** + * Create default emotion cache + */ + const cacheDefault = createCache({ + key: 'mui', + }); Object.keys(translations).forEach((lng) => { this.i18n.addResourceBundle(lng, 'translation', translations[lng], true, true); @@ -106,18 +121,15 @@ export class AppProviders extends Component { return ( <FullScreenShim> <I18nextProvider i18n={this.i18n}> - <ThemeProvider - theme={createTheme(theme)} - > - <StylesProvider - jss={create({ plugins: [...jssPreset().plugins, rtl()] })} - generateClassName={generateClassName} - > - <MaybeDndProvider dndManager={dndManager}> - {children} - </MaybeDndProvider> - </StylesProvider> - </ThemeProvider> + <StyledEngineProvider injectFirst> + <CacheProvider value={theme.direction === 'rtl' ? cacheRtl : cacheDefault}> + <ThemeProvider theme={createTheme((theme))}> + <MaybeDndProvider dndManager={dndManager}> + {children} + </MaybeDndProvider> + </ThemeProvider> + </CacheProvider> + </StyledEngineProvider> </I18nextProvider> </FullScreenShim> ); @@ -126,7 +138,6 @@ export class AppProviders extends Component { AppProviders.propTypes = { children: PropTypes.node, - createGenerateClassNameOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types dndManager: PropTypes.object, // eslint-disable-line react/forbid-prop-types language: PropTypes.string.isRequired, theme: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types @@ -135,6 +146,5 @@ AppProviders.propTypes = { AppProviders.defaultProps = { children: null, - createGenerateClassNameOptions: {}, dndManager: undefined, }; diff --git a/src/components/AttributionPanel.js b/src/components/AttributionPanel.js index c2d8cc6979e1332c0b9ef46a4c0369209d645342..b1d9c6ebec7acf6af795a26c83c8e205d0df8e1d 100644 --- a/src/components/AttributionPanel.js +++ b/src/components/AttributionPanel.js @@ -1,14 +1,24 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; -import Link from '@material-ui/core/Link'; -import Skeleton from '@material-ui/lab/Skeleton'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Link from '@mui/material/Link'; +import Skeleton from '@mui/material/Skeleton'; import { Img } from 'react-image'; import CompanionWindow from '../containers/CompanionWindow'; +import { CompanionWindowSection } from './CompanionWindowSection'; import { LabelValueMetadata } from './LabelValueMetadata'; import ns from '../config/css-ns'; import { PluginHook } from './PluginHook'; +const StyledLogo = styled(Img)(() => ({ + maxWidth: '100%', +})); + +const StyledPlaceholder = styled(Skeleton)(({ theme }) => ({ + backgroundColor: theme.palette.grey[300], +})); + /** * WindowSideBarInfoPanel */ @@ -24,7 +34,6 @@ export class AttributionPanel extends Component { rights, windowId, id, - classes, t, } = this.props; @@ -35,7 +44,7 @@ export class AttributionPanel extends Component { windowId={windowId} id={id} > - <div className={classes.section}> + <CompanionWindowSection> { requiredStatement && ( <LabelValueMetadata labelValuePairs={requiredStatement} defaultLabel={t('attribution')} /> )} @@ -53,20 +62,19 @@ export class AttributionPanel extends Component { </dl> ) } - </div> + </CompanionWindowSection> { manifestLogo && ( - <div className={classes.section}> - <Img + <CompanionWindowSection> + <StyledLogo src={[manifestLogo]} alt="" role="presentation" - className={classes.logo} unloader={ - <Skeleton className={classes.placeholder} variant="rect" height={60} width={60} /> + <StyledPlaceholder variant="rectangular" height={60} width={60} /> } /> - </div> + </CompanionWindowSection> )} <PluginHook {...this.props} /> @@ -76,7 +84,6 @@ export class AttributionPanel extends Component { } AttributionPanel.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), id: PropTypes.string.isRequired, manifestLogo: PropTypes.string, requiredStatement: PropTypes.arrayOf(PropTypes.shape({ @@ -89,7 +96,6 @@ AttributionPanel.propTypes = { }; AttributionPanel.defaultProps = { - classes: {}, manifestLogo: null, requiredStatement: null, rights: null, diff --git a/src/components/AudioViewer.js b/src/components/AudioViewer.js index ab7b9d6883b7186b6b990744707d5be5923eab32..9ceb07d78826542af7e9133df4c66f4ea42232a4 100644 --- a/src/components/AudioViewer.js +++ b/src/components/AudioViewer.js @@ -1,40 +1,48 @@ -import { Component, Fragment } from 'react'; +import { Fragment } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; + +const StyledContainer = styled('div')({ + alignItems: 'center', + display: 'flex', + width: '100%', +}); + +const StyledAudio = styled('audio')({ + width: '100%', +}); /** */ -export class AudioViewer extends Component { +export function AudioViewer(props) { /* eslint-disable jsx-a11y/media-has-caption */ /** */ - render() { - const { - captions, classes, audioOptions, audioResources, - } = this.props; + const { + captions, audioOptions, audioResources, + } = props; - return ( - <div className={classes.container}> - <audio className={classes.audio} {...audioOptions}> - {audioResources.map(audio => ( - <Fragment key={audio.id}> - <source src={audio.id} type={audio.getFormat()} /> - </Fragment> - ))} - {captions.map(caption => ( - <Fragment key={caption.id}> - <track src={caption.id} label={caption.getDefaultLabel()} srcLang={caption.getProperty('language')} /> - </Fragment> - ))} - </audio> - </div> - ); - } - /* eslint-enable jsx-a11y/media-has-caption */ + return ( + <StyledContainer> + <StyledAudio {...audioOptions}> + {audioResources.map((audio) => ( + <Fragment key={audio.id}> + <source src={audio.id} type={audio.getFormat()} /> + </Fragment> + ))} + {captions.map((caption) => ( + <Fragment key={caption.id}> + <track src={caption.id} label={caption.getDefaultLabel()} srcLang={caption.getProperty('language')} /> + </Fragment> + ))} + </StyledAudio> + </StyledContainer> + ); } +/* eslint-enable jsx-a11y/media-has-caption */ AudioViewer.propTypes = { audioOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types audioResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types captions: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types - classes: PropTypes.objectOf(PropTypes.string).isRequired, }; AudioViewer.defaultProps = { diff --git a/src/components/Branding.js b/src/components/Branding.js index d2f4d4f9e1a42cd605136baa9da81cba0526586e..9b3af649ccbb0520a5842e95778b45091a97205b 100644 --- a/src/components/Branding.js +++ b/src/components/Branding.js @@ -1,7 +1,8 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import IconButton from '@material-ui/core/IconButton'; -import Typography from '@material-ui/core/Typography'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; import MiradorIcon from './icons/MiradorIcon'; /** @@ -13,7 +14,7 @@ export class Branding extends Component { const { t, variant, ...ContainerProps } = this.props; return ( - <div {...ContainerProps}> + <Stack alignItems="center" {...ContainerProps}> { variant === 'wide' && ( <div> <Typography align="center" component="p" variant="h3">{t('mirador')}</Typography> @@ -25,11 +26,12 @@ export class Branding extends Component { href="https://projectmirador.org" target="_blank" rel="noopener" + size="large" > <MiradorIcon aria-label={t('aboutMirador')} titleAccess={t('aboutMirador')} fontSize="large" /> </IconButton> </Typography> - </div> + </Stack> ); } } diff --git a/src/components/CanvasAnnotations.js b/src/components/CanvasAnnotations.js index 544e135d78469b3d2364dec08008cfdeeea14846..8b4993c3e28145bf4ed63fcd53e918cdc71b64b3 100644 --- a/src/components/CanvasAnnotations.js +++ b/src/components/CanvasAnnotations.js @@ -1,11 +1,10 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import Chip from '@material-ui/core/Chip'; -import MenuList from '@material-ui/core/MenuList'; -import MenuItem from '@material-ui/core/MenuItem'; -import ListItemText from '@material-ui/core/ListItemText'; -import Typography from '@material-ui/core/Typography'; +import Chip from '@mui/material/Chip'; +import MenuList from '@mui/material/MenuList'; +import MenuItem from '@mui/material/MenuItem'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; import SanitizedHtml from '../containers/SanitizedHtml'; import { ScrollTo } from './ScrollTo'; @@ -58,7 +57,7 @@ export class CanvasAnnotations extends Component { */ render() { const { - annotations, classes, index, label, selectedAnnotationId, t, totalSize, + annotations, index, label, selectedAnnotationId, t, totalSize, listContainerComponent, htmlSanitizationRuleSet, hoveredAnnotationIds, containerRef, } = this.props; @@ -66,53 +65,50 @@ export class CanvasAnnotations extends Component { return ( <> - <Typography className={classes.sectionHeading} variant="overline"> + <Typography sx={{ paddingLeft: 2, paddingRight: 1, paddingTop: 2 }} variant="overline"> {t('annotationCanvasLabel', { context: `${index + 1}/${totalSize}`, label })} </Typography> <MenuList autoFocusItem variant="selectedMenu"> - { - annotations.map(annotation => ( + {annotations.map((annotation) => ( + <ScrollTo + containerRef={containerRef} + key={`${annotation.id}-scroll`} + offsetTop={96} // offset for the height of the form above + scrollTo={selectedAnnotationId === annotation.id} + selected={selectedAnnotationId === annotation.id} + > <MenuItem - button component={listContainerComponent} - className={clsx( - classes.annotationListItem, - { - [classes.hovered]: hoveredAnnotationIds.includes(annotation.id), + variant="multiline" + divider + sx={{ + '&:hover,&:focus': { + backgroundColor: 'action.hover', }, - )} + backgroundColor: hoveredAnnotationIds.includes(annotation.id) ? 'action.hover' : '', + }} key={annotation.id} annotationid={annotation.id} - selected={selectedAnnotationId === annotation.id} - onClick={e => this.handleClick(e, annotation)} + onClick={(e) => this.handleClick(e, annotation)} onFocus={() => this.handleAnnotationHover(annotation)} onBlur={this.handleAnnotationBlur} onMouseEnter={() => this.handleAnnotationHover(annotation)} onMouseLeave={this.handleAnnotationBlur} > - <ScrollTo - containerRef={containerRef} - key={`${annotation.id}-scroll`} - offsetTop={96} // offset for the height of the form above - scrollTo={selectedAnnotationId === annotation.id} - > - <ListItemText primaryTypographyProps={{ variant: 'body2' }}> - <SanitizedHtml - ruleSet={htmlSanitizationRuleSet} - htmlString={annotation.content} - /> - <div> - { - annotation.tags.map(tag => ( - <Chip size="small" variant="outlined" label={tag} id={tag} className={classes.chip} key={tag.toString()} /> - )) - } - </div> - </ListItemText> - </ScrollTo> + <ListItemText + primaryTypographyProps={{ variant: 'body2' }} + primary={ + <SanitizedHtml ruleSet={htmlSanitizationRuleSet} htmlString={annotation.content} /> + } + secondary={ + annotation.tags.map((tag) => ( + <Chip component="span" size="small" variant="outlined" label={tag} id={tag} key={tag.toString()} /> + )) + } + /> </MenuItem> - )) - } + </ScrollTo> + ))} </MenuList> </> ); @@ -126,7 +122,6 @@ CanvasAnnotations.propTypes = { id: PropTypes.string.isRequired, }), ), - classes: PropTypes.objectOf(PropTypes.string), containerRef: PropTypes.oneOfType([ PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) }), @@ -146,7 +141,6 @@ CanvasAnnotations.propTypes = { }; CanvasAnnotations.defaultProps = { annotations: [], - classes: {}, containerRef: undefined, hoveredAnnotationIds: [], htmlSanitizationRuleSet: 'iiif', diff --git a/src/components/CanvasInfo.js b/src/components/CanvasInfo.js index 93f4b31243d6b9e5b33632085d6b53c4b3e3207c..39ec1a03fd29aeb92d23f60e2be2d672427bac5b 100644 --- a/src/components/CanvasInfo.js +++ b/src/components/CanvasInfo.js @@ -1,6 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; import CollapsibleSection from '../containers/CollapsibleSection'; import SanitizedHtml from '../containers/SanitizedHtml'; import { LabelValueMetadata } from './LabelValueMetadata'; diff --git a/src/components/CanvasLayers.js b/src/components/CanvasLayers.js index 99e400ca2573b8237ef3137fa27712764c9b6d81..87f78490043e3700c25a0572f61f996372b18e7e 100644 --- a/src/components/CanvasLayers.js +++ b/src/components/CanvasLayers.js @@ -1,23 +1,36 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import clsx from 'clsx'; +import { styled } from '@mui/material/styles'; import { v4 as uuid } from 'uuid'; -import Input from '@material-ui/core/Input'; -import InputAdornment from '@material-ui/core/InputAdornment'; -import List from '@material-ui/core/List'; -import ListItem from '@material-ui/core/ListItem'; -import Slider from '@material-ui/core/Slider'; -import Tooltip from '@material-ui/core/Tooltip'; -import DragHandleIcon from '@material-ui/icons/DragHandleSharp'; -import MoveToTopIcon from '@material-ui/icons/VerticalAlignTopSharp'; -import VisibilityIcon from '@material-ui/icons/VisibilitySharp'; -import VisibilityOffIcon from '@material-ui/icons/VisibilityOffSharp'; -import OpacityIcon from '@material-ui/icons/OpacitySharp'; -import Typography from '@material-ui/core/Typography'; +import Input from '@mui/material/Input'; +import InputAdornment from '@mui/material/InputAdornment'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import Slider from '@mui/material/Slider'; +import Tooltip from '@mui/material/Tooltip'; +import DragHandleIcon from '@mui/icons-material/DragHandleSharp'; +import MoveToTopIcon from '@mui/icons-material/VerticalAlignTopSharp'; +import VisibilityIcon from '@mui/icons-material/VisibilitySharp'; +import VisibilityOffIcon from '@mui/icons-material/VisibilityOffSharp'; +import OpacityIcon from '@mui/icons-material/OpacitySharp'; +import Typography from '@mui/material/Typography'; import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import IIIFThumbnail from '../containers/IIIFThumbnail'; +const StyledDragHandle = styled('div')(({ theme }) => ({ + alignItems: 'center', + borderRight: `0.5px solid ${theme.palette.divider}`, + display: 'flex', + flex: 1, + flexDirection: 'row', + marginBottom: theme.spacing(-2), + marginRight: theme.spacing(1), + marginTop: theme.spacing(-2), + maxWidth: theme.spacing(3), + width: theme.spacing(3), +})); + /** */ const reorder = (list, startIndex, endIndex) => { const result = Array.from(list); @@ -116,7 +129,6 @@ export class CanvasLayers extends Component { /** @private */ renderLayer(resource, index) { const { - classes, layerMetadata, t, } = this.props; @@ -136,10 +148,12 @@ export class CanvasLayers extends Component { maxHeight={height} maxWidth={width} resource={resource} - classes={{ image: classes.image, root: classes.thumbnail }} + border /> <Typography - className={classes.label} + sx={{ + paddingLeft: 1, + }} component="div" variant="body1" > @@ -158,10 +172,21 @@ export class CanvasLayers extends Component { </div> <div style={{ alignItems: 'center', display: 'flex' }}> <Tooltip title={t('layer_opacity')}> - <OpacityIcon className={classes.opacityIcon} color={layer.visibility ? 'inherit' : 'disabled'} fontSize="small" /> + <OpacityIcon sx={{ marginRight: 0.5 }} color={layer.visibility ? 'inherit' : 'disabled'} fontSize="small" /> </Tooltip> <Input - classes={{ input: classes.opacityInput }} + sx={{ + 'MuiInput-input': { + '&::-webkit-outer-spin-button,&::-webkit-inner-spin-button': { + margin: 0, + WebkitAppearance: 'none', + }, + MozAppearance: 'textfield', + textAlign: 'right', + typography: 'caption', + width: '3ch', + }, + }} disabled={!layer.visibility} value={Math.round(layer.opacity * 100)} type="number" @@ -174,7 +199,11 @@ export class CanvasLayers extends Component { }} /> <Slider - className={classes.slider} + sx={{ + marginLeft: 2, + marginRight: 2, + maxWidth: 150, + }} disabled={!layer.visibility} value={layer.opacity * 100} onChange={(e, value) => this.handleOpacityChange(resource.id, value)} @@ -187,7 +216,6 @@ export class CanvasLayers extends Component { /** @private */ renderDraggableLayer(resource, index) { const { - classes, t, } = this.props; @@ -198,20 +226,33 @@ export class CanvasLayers extends Component { ref={provided.innerRef} {...provided.draggableProps} component="li" - className={clsx( - classes.listItem, - { - [classes.dragging]: snapshot.isDragging, - }, - )} + divider + sx={{ + alignItems: 'stretch', + cursor: 'pointer', + paddingBottom: 2, + paddingRight: 2, + paddingTop: 2, + ...(snapshot.isDragging && { + backgroundColor: 'action.hover', + }), + }} disableGutters key={resource.id} > - <div {...provided.dragHandleProps} className={classes.dragHandle}> + <StyledDragHandle + {...provided.dragHandleProps} + sx={{ + '&:hover': { + backgroundColor: snapshot.isDragging ? 'action.selected' : 'action.hover', + }, + backgroundColor: snapshot.isDragging ? 'action.selected' : 'shades.light', + }} + > <Tooltip title={t('layer_move')}> <DragHandleIcon /> </Tooltip> - </div> + </StyledDragHandle> {this.renderLayer(resource, index)} </ListItem> )} @@ -222,7 +263,6 @@ export class CanvasLayers extends Component { /** */ render() { const { - classes, index, label, layers, @@ -233,7 +273,14 @@ export class CanvasLayers extends Component { return ( <> { totalSize > 1 && ( - <Typography className={classes.sectionHeading} variant="overline"> + <Typography + sx={{ + paddingLeft: 1, + paddingRight: 1, + paddingTop: 2, + }} + variant="overline" + > {t('annotationCanvasLabel', { context: `${index + 1}/${totalSize}`, label })} </Typography> )} @@ -241,7 +288,9 @@ export class CanvasLayers extends Component { <Droppable droppableId={this.droppableId}> {(provided, snapshot) => ( <List - className={classes.list} + sx={{ + paddingTop: 0, + }} {...provided.droppableProps} ref={provided.innerRef} > @@ -262,7 +311,6 @@ export class CanvasLayers extends Component { CanvasLayers.propTypes = { canvasId: PropTypes.string.isRequired, - classes: PropTypes.objectOf(PropTypes.string), index: PropTypes.number.isRequired, label: PropTypes.string.isRequired, layerMetadata: PropTypes.objectOf(PropTypes.shape({ @@ -277,6 +325,5 @@ CanvasLayers.propTypes = { }; CanvasLayers.defaultProps = { - classes: {}, layerMetadata: undefined, }; diff --git a/src/components/ChangeThemeDialog.js b/src/components/ChangeThemeDialog.js index 146071b855d6dc492722e5ebec2c9d8dc9da2d7a..17bd83171aab087aa3eafd88173a167674f1a3fa 100644 --- a/src/components/ChangeThemeDialog.js +++ b/src/components/ChangeThemeDialog.js @@ -1,16 +1,20 @@ import { Component } from 'react'; import { - Dialog, DialogTitle, ListItemIcon, ListItemText, MenuList, MenuItem, - Typography, DialogContent, -} from '@material-ui/core'; -import PaletteIcon from '@material-ui/icons/PaletteSharp'; +} from '@mui/material'; +import PaletteIcon from '@mui/icons-material/PaletteSharp'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import { WorkspaceDialog } from './WorkspaceDialog'; + +const ThemeIcon = styled(PaletteIcon, { name: 'ThemeIcon', slot: 'icon' })(({ theme }) => ({ + color: '#BDBDBD', +})); /** * a simple dialog providing the possibility to switch the theme @@ -36,7 +40,6 @@ export class ChangeThemeDialog extends Component { /** */ render() { const { - classes, handleClose, open, selectedTheme, @@ -44,42 +47,34 @@ export class ChangeThemeDialog extends Component { themeIds, } = this.props; return ( - <Dialog - onClose={handleClose} - open={open} - > - <DialogTitle disableTypography> - <Typography variant="h2"> - {t('changeTheme')} - </Typography> + <WorkspaceDialog onClose={handleClose} open={open} variant="menu"> + <DialogTitle> + {t('changeTheme')} </DialogTitle> - <DialogContent className={classes.dialogContent}> + <DialogContent> <MenuList autoFocusItem> - { - themeIds.map(value => ( - <MenuItem - key={value} - className={classes.listitem} - onClick={() => { this.handleThemeChange(value); }} - selected={value === selectedTheme} - value={value} - > - <ListItemIcon> - <PaletteIcon className={classes[value]} /> - </ListItemIcon> - <ListItemText>{t(value)}</ListItemText> - </MenuItem> - )) - } + {themeIds.map((value) => ( + <MenuItem + key={value} + className="listitem" + onClick={() => this.handleThemeChange(value)} + selected={value === selectedTheme} + value={value} + > + <ListItemIcon> + <ThemeIcon ownerState={{ value }} /> + </ListItemIcon> + <ListItemText>{t(value)}</ListItemText> + </MenuItem> + ))} </MenuList> </DialogContent> - </Dialog> + </WorkspaceDialog> ); } } ChangeThemeDialog.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, handleClose: PropTypes.func.isRequired, open: PropTypes.bool, selectedTheme: PropTypes.string.isRequired, diff --git a/src/components/CollapsibleSection.js b/src/components/CollapsibleSection.js index 8773c9b6d951d9c14ccbb6ec3000db74bd032811..04c5f7d5137bd28367c52808d6663c1f98b60d05 100644 --- a/src/components/CollapsibleSection.js +++ b/src/components/CollapsibleSection.js @@ -1,9 +1,10 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; -import KeyboardArrowDown from '@material-ui/icons/KeyboardArrowDownSharp'; -import KeyboardArrowUp from '@material-ui/icons/KeyboardArrowUpSharp'; -import MiradorMenuButton from '../containers/MiradorMenuButton'; +import Typography from '@mui/material/Typography'; +import Accordion from '@mui/material/Accordion'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; /** * CollapsableSection ~ @@ -14,14 +15,12 @@ export class CollapsibleSection extends Component { super(props); this.state = { open: true }; - this.toggleSection = this.toggleSection.bind(this); + this.handleChange = this.handleChange.bind(this); } - /** */ - toggleSection() { - const { open } = this.state; - - this.setState({ open: !open }); + /** Control the accordion state so we can provide aria labeling */ + handleChange(event, isExpanded) { + this.setState({ open: isExpanded }); } /** @@ -29,45 +28,28 @@ export class CollapsibleSection extends Component { */ render() { const { - children, classes, id, label, t, + children, id, label, t, } = this.props; const { open } = this.state; return ( - <> - <div className={classes.container}> - <Typography - className={classes.heading} - id={id} - onClick={this.toggleSection} - variant="overline" - component="h4" - > + <Accordion id={id} elevation={0} expanded={open} onChange={this.handleChange} disableGutters square variant="compact"> + <AccordionSummary id={`${id}-header`} aria-controls={`${id}-content`} aria-label={t(open ? 'collapseSection' : 'expandSection', { section: label })} expandIcon={<ExpandMoreIcon />}> + <Typography variant="overline" component="h4"> {label} </Typography> - <MiradorMenuButton - aria-label={ - t( - open ? 'collapseSection' : 'expandSection', - { section: label }, - ) - } - aria-expanded={open} - className={classes.button} - onClick={this.toggleSection} - > - {open ? <KeyboardArrowUp /> : <KeyboardArrowDown />} - </MiradorMenuButton> - </div> - {open && children} - </> + </AccordionSummary> + <AccordionDetails> + {children} + </AccordionDetails> + </Accordion> ); } } CollapsibleSection.propTypes = { children: PropTypes.node.isRequired, - classes: PropTypes.objectOf(PropTypes.string).isRequired, + id: PropTypes.string.isRequired, label: PropTypes.string.isRequired, t: PropTypes.func.isRequired, diff --git a/src/components/CollectionDialog.js b/src/components/CollectionDialog.js index 3dc335ba9b3bfb13ef74ff4a66fc7348b59c129d..75fa641f14cc78bced024ae8cb14736effba434c 100644 --- a/src/components/CollectionDialog.js +++ b/src/components/CollectionDialog.js @@ -10,15 +10,32 @@ import { MenuList, MenuItem, Typography, -} from '@material-ui/core'; -import ArrowBackIcon from '@material-ui/icons/ArrowBackSharp'; -import Skeleton from '@material-ui/lab/Skeleton'; +} from '@mui/material'; +import ArrowBackIcon from '@mui/icons-material/ArrowBackSharp'; +import Skeleton from '@mui/material/Skeleton'; +import { styled } from '@mui/material/styles'; import asArray from '../lib/asArray'; import { LabelValueMetadata } from './LabelValueMetadata'; import CollapsibleSection from '../containers/CollapsibleSection'; import ScrollIndicatedDialogContent from '../containers/ScrollIndicatedDialogContent'; import ManifestInfo from '../containers/ManifestInfo'; +const StyledScrollIndicatedDialogContent = styled(ScrollIndicatedDialogContent)(() => ({ + padding: (theme) => theme.spacing(1), +})); + +const StyledCollectionMetadata = styled('div')(() => ({ + '& .MuiPaper-root': { + background: 'transparent', + }, + padding: (theme) => theme.spacing(2), +})); + +const StyledCollectionFilter = styled('div')(() => ({ + padding: (theme) => theme.spacing(2), + paddingTop: 0, +})); + /** * a dialog providing the possibility to select the collection */ @@ -108,37 +125,27 @@ export class CollectionDialog extends Component { /** */ placeholder() { - const { classes } = this.props; - return ( <Dialog - className={classes.dialog} + variant="contained" onClose={this.hideDialog} open container={this.dialogContainer()} - BackdropProps={this.backdropProps()} > - <DialogTitle id="select-collection" disableTypography> - <Skeleton className={classes.placeholder} variant="text" /> + <DialogTitle id="select-collection"> + <Skeleton variant="text" /> </DialogTitle> <ScrollIndicatedDialogContent> - <Skeleton className={classes.placeholder} variant="text" /> - <Skeleton className={classes.placeholder} variant="text" /> + <Skeleton variant="text" /> + <Skeleton variant="text" /> </ScrollIndicatedDialogContent> </Dialog> ); } - /** */ - backdropProps() { - const { classes } = this.props; - return { classes: { root: classes.dialog } }; - } - /** */ render() { const { - classes, collection, error, isMultipart, @@ -173,21 +180,20 @@ export class CollectionDialog extends Component { return ( <Dialog - className={classes.dialog} + variant="contained" onClose={this.hideDialog} container={this.dialogContainer()} - BackdropProps={this.backdropProps()} open > - <DialogTitle id="select-collection" disableTypography> + <DialogTitle id="select-collection"> <Typography component="div" variant="overline"> { t(isMultipart ? 'multipartCollection' : 'collection') } </Typography> - <Typography variant="h3"> + <Typography component="div" variant="h3"> {CollectionDialog.getUseableLabel(manifest)} </Typography> </DialogTitle> - <ScrollIndicatedDialogContent className={classes.dialogContent}> + <StyledScrollIndicatedDialogContent> { collection && ( <Button startIcon={<ArrowBackIcon />} @@ -197,7 +203,7 @@ export class CollectionDialog extends Component { </Button> )} - <div className={classes.collectionMetadata}> + <StyledCollectionMetadata> <ManifestInfo manifestId={manifest.id} /> <CollapsibleSection id="select-collection-rights" @@ -221,15 +227,15 @@ export class CollectionDialog extends Component { ) } </CollapsibleSection> - </div> - <div className={classes.collectionFilter}> + </StyledCollectionMetadata> + <StyledCollectionFilter> {manifest.getTotalCollections() > 0 && ( <Chip clickable color={currentFilter === 'collections' ? 'primary' : 'default'} onClick={() => this.setFilter('collections')} label={t('totalCollections', { count: manifest.getTotalCollections() })} /> )} {manifest.getTotalManifests() > 0 && ( <Chip clickable color={currentFilter === 'manifests' ? 'primary' : 'default'} onClick={() => this.setFilter('manifests')} label={t('totalManifests', { count: manifest.getTotalManifests() })} /> )} - </div> + </StyledCollectionFilter> { currentFilter === 'collections' && ( <MenuList> { @@ -237,7 +243,7 @@ export class CollectionDialog extends Component { <MenuItem key={c.id} onClick={() => { this.selectCollection(c); }} - className={classes.collectionItem} + variant="multiline" > {CollectionDialog.getUseableLabel(c)} </MenuItem> @@ -252,7 +258,7 @@ export class CollectionDialog extends Component { <MenuItem key={m.id} onClick={() => { this.selectManifest(m); }} - className={classes.collectionItem} + variant="multiline" > {CollectionDialog.getUseableLabel(m)} </MenuItem> @@ -260,7 +266,7 @@ export class CollectionDialog extends Component { } </MenuList> )} - </ScrollIndicatedDialogContent> + </StyledScrollIndicatedDialogContent> <DialogActions> <Button onClick={this.hideDialog}> {t('close')} @@ -273,7 +279,6 @@ export class CollectionDialog extends Component { CollectionDialog.propTypes = { addWindow: PropTypes.func.isRequired, - classes: PropTypes.objectOf(PropTypes.string).isRequired, collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types collectionPath: PropTypes.arrayOf(PropTypes.string), container: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), diff --git a/src/components/CollectionInfo.js b/src/components/CollectionInfo.js index 4cd3d3961e5bf804d24687e703199d8b0e6b4d61..ead13a799bbd0b346fa4f1e5b0a4124c395e2700 100644 --- a/src/components/CollectionInfo.js +++ b/src/components/CollectionInfo.js @@ -1,8 +1,8 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import Typography from '@material-ui/core/Typography'; -import ViewListIcon from '@material-ui/icons/ViewListSharp'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import ViewListIcon from '@mui/icons-material/ViewListSharp'; import CollapsibleSection from '../containers/CollapsibleSection'; /** diff --git a/src/components/CompanionArea.js b/src/components/CompanionArea.js index bdf7610b6e5e06f26505d86cce8b4622818ed98b..b73350d644204d6e36febe1491f0be5ff69431a9 100644 --- a/src/components/CompanionArea.js +++ b/src/components/CompanionArea.js @@ -1,19 +1,58 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Slide from '@material-ui/core/Slide'; -import ArrowLeftIcon from '@material-ui/icons/ArrowLeftSharp'; -import ArrowRightIcon from '@material-ui/icons/ArrowRightSharp'; +import { styled } from '@mui/material/styles'; +import Slide from '@mui/material/Slide'; +import ArrowLeftIcon from '@mui/icons-material/ArrowLeftSharp'; +import ArrowRightIcon from '@mui/icons-material/ArrowRightSharp'; +import classNames from 'classnames'; import CompanionWindowFactory from '../containers/CompanionWindowFactory'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import ns from '../config/css-ns'; +const Root = styled('div', { name: 'CompanionArea', slot: 'root' })(({ ownerState, theme }) => ({ + display: 'flex', + minHeight: 0, + position: 'relative', + zIndex: theme.zIndex.appBar - 2, + ...((ownerState.position === 'bottom' || ownerState.position === 'far-bottom') && { + flexDirection: 'column', + width: '100%', + }), +})); + +const Container = styled('div', { name: 'CompanionArea', slot: 'container' })(({ ownerState }) => ({ + display: ownerState?.companionAreaOpen ? 'flex' : 'none', + ...((ownerState?.position === 'bottom' || ownerState?.position === 'far-bottom') && { + flexDirection: 'column', + width: '100%', + }), + ...((ownerState?.position === 'left' && (ownerState?.companionWindowIds && ownerState.companionWindowIds.length > 0)) && { + minWidth: '235px', + }), +})); + +const StyledToggle = styled('div', { name: 'CompanionArea', slot: 'toggle' })(({ theme }) => ({ + alignItems: 'center', + backgroundColor: theme.palette.background.paper, + border: `1px solid ${theme.palette.mode === 'dark' ? theme.palette.divider : theme.palette.shades?.dark}`, + borderInlineStart: 0, + borderRadius: 0, + display: 'inline-flex', + height: '48px', + left: '100%', + marginTop: '1rem', + overflow: 'hidden', + padding: 2, + position: 'absolute', + width: '23px', + zIndex: theme.zIndex.drawer, +})); + /** */ export class CompanionArea extends Component { /** */ areaLayoutClass() { - const { - classes, position, - } = this.props; + const { classes, position } = this.props; return (position === 'bottom' || position === 'far-bottom') ? classes.horizontal : null; } @@ -51,45 +90,44 @@ export class CompanionArea extends Component { /** */ render() { const { - classes, companionWindowIds, companionAreaOpen, setCompanionAreaOpen, + className, + companionWindowIds, companionAreaOpen, setCompanionAreaOpen, position, sideBarOpen, t, windowId, } = this.props; - + const classes = classNames(this.areaLayoutClass(), ns(`companion-area-${position}`), className); return ( - <div className={[classes.root, this.areaLayoutClass(), ns(`companion-area-${position}`)].join(' ')}> + <Root ownerState={this.props} className={classes}> <Slide in={companionAreaOpen} direction={this.slideDirection()}> - <div className={[ns('companion-windows'), companionWindowIds.length > 0 && classes[position], this.areaLayoutClass()].join(' ')} style={{ display: companionAreaOpen ? 'flex' : 'none' }}> - { - companionWindowIds.map(id => ( - <CompanionWindowFactory id={id} key={id} windowId={windowId} /> - )) - } - </div> + <Container + ownerState={this.props} + className={`${ns('companion-windows')}`} + > + {companionWindowIds.map((id) => ( + <CompanionWindowFactory id={id} key={id} windowId={windowId} /> + ))} + </Container> </Slide> - { - setCompanionAreaOpen && position === 'left' && sideBarOpen && companionWindowIds.length > 0 - && ( - <div className={classes.toggle}> - <MiradorMenuButton - aria-expanded={companionAreaOpen} - aria-label={companionAreaOpen ? t('collapseSidePanel') : t('expandSidePanel')} - className={classes.toggleButton} - key={companionAreaOpen ? 'collapse' : 'expand'} - onClick={() => { setCompanionAreaOpen(windowId, !companionAreaOpen); }} - TooltipProps={{ placement: 'right' }} - > - {this.collapseIcon()} - </MiradorMenuButton> - </div> - ) - } - </div> + {setCompanionAreaOpen && position === 'left' && sideBarOpen && companionWindowIds.length > 0 && ( + <StyledToggle> + <MiradorMenuButton + aria-expanded={companionAreaOpen} + aria-label={companionAreaOpen ? t('collapseSidePanel') : t('expandSidePanel')} + edge="start" + onClick={() => { setCompanionAreaOpen(windowId, !companionAreaOpen); }} + TooltipProps={{ placement: 'right' }} + > + {this.collapseIcon()} + </MiradorMenuButton> + </StyledToggle> + )} + </Root> ); } } CompanionArea.propTypes = { classes: PropTypes.objectOf(PropTypes.string), + className: PropTypes.string, companionAreaOpen: PropTypes.bool.isRequired, companionWindowIds: PropTypes.arrayOf(PropTypes.string).isRequired, direction: PropTypes.string.isRequired, @@ -102,6 +140,7 @@ CompanionArea.propTypes = { CompanionArea.defaultProps = { classes: {}, + className: undefined, setCompanionAreaOpen: () => {}, sideBarOpen: false, }; diff --git a/src/components/CompanionWindow.js b/src/components/CompanionWindow.js index 9cf4fe29a4e55f7576b7b3a6af724b0a87a4297a..8a31ce2fb1b8a4b1127bcdec3e1d9566b7911241 100644 --- a/src/components/CompanionWindow.js +++ b/src/components/CompanionWindow.js @@ -1,15 +1,25 @@ import { Children, cloneElement, Component } from 'react'; import PropTypes from 'prop-types'; -import CloseIcon from '@material-ui/icons/CloseSharp'; -import OpenInNewIcon from '@material-ui/icons/OpenInNewSharp'; -import MoveIcon from '@material-ui/icons/DragIndicatorSharp'; -import Paper from '@material-ui/core/Paper'; -import Typography from '@material-ui/core/Typography'; -import Toolbar from '@material-ui/core/Toolbar'; +import { styled } from '@mui/material/styles'; +import CloseIcon from '@mui/icons-material/CloseSharp'; +import OpenInNewIcon from '@mui/icons-material/OpenInNewSharp'; +import MoveIcon from '@mui/icons-material/DragIndicatorSharp'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import Toolbar from '@mui/material/Toolbar'; import { Rnd } from 'react-rnd'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import ns from '../config/css-ns'; +const Root = styled(Paper, { name: 'CompanionWindow', slot: 'root' })({}); +const StyledToolbar = styled(Toolbar, { name: 'CompanionWindow', slot: 'toolbar' })({}); +const StyledTitle = styled(Typography, { name: 'CompanionWindow', slot: 'title' })({}); +const StyledTitleControls = styled('div', { name: 'CompanionWindow', slot: 'controls' })({}); +const Contents = styled(Paper, { name: 'CompanionWindow', slot: 'contents' })({}); +const StyledRnd = styled(Rnd, { name: 'CompanionWindow', slot: 'resize' })({}); +const StyledPositionButton = styled(MiradorMenuButton, { name: 'CompanionWindow', slot: 'positionButton' })({}); +const StyledCloseButton = styled(MiradorMenuButton, { name: 'CompanionWindow', slot: 'closeButton' })({}); + /** * CompanionWindow */ @@ -69,7 +79,7 @@ export class CompanionWindow extends Component { const { ariaLabel, classes, paperClassName, onCloseClick, updateCompanionWindow, isDisplayed, position, t, title, children, titleControls, size, - defaultSidebarPanelWidth, defaultSidebarPanelHeight, + defaultSidebarPanelWidth, defaultSidebarPanelHeight, innerRef, } = this.props; const isBottom = (position === 'bottom' || position === 'far-bottom'); @@ -87,19 +97,21 @@ export class CompanionWindow extends Component { }); return ( - <Paper - className={[classes.root, position === 'bottom' ? classes.horizontal : classes.vertical, classes[`companionWindow-${position}`], ns(`companion-window-${position}`), paperClassName].join(' ')} + <Root + ownerState={this.props} + ref={innerRef} style={{ display: isDisplayed ? null : 'none', order: position === 'left' ? -1 : null, }} + className={[ns(`companion-window-${position}`), paperClassName, position === 'bottom' ? classes.horizontal : classes.vertical].join(' ')} square component="aside" aria-label={ariaLabel || title} > - <Rnd - className={[classes.rnd]} - style={{ display: 'flex', position: 'relative' }} + <StyledRnd + style={{ display: 'inherit', position: 'inherit' }} + ownerState={this.props} default={{ height: isBottom ? defaultSidebarPanelHeight : '100%', width: isBottom ? 'auto' : defaultSidebarPanelWidth, @@ -110,18 +122,12 @@ export class CompanionWindow extends Component { minWidth={position === 'left' ? 235 : 100} > - <Toolbar - className={[ - classes.toolbar, - classes.companionWindowHeader, - size.width < 370 ? classes.small : null, - ns('companion-window-header'), - ].join(' ')} + <StyledToolbar + variant="dense" + className={[ns('companion-window-header'), size.width < 370 ? classes.small : null].join(' ')} disableGutters > - <Typography variant="h3" className={classes.windowSideBarTitle}> - {title} - </Typography> + <StyledTitle variant="h3">{title}</StyledTitle> { position === 'left' ? updateCompanionWindow @@ -137,49 +143,50 @@ export class CompanionWindow extends Component { <> { updateCompanionWindow && ( - <MiradorMenuButton + <StyledPositionButton aria-label={position === 'bottom' ? t('moveCompanionWindowToRight') : t('moveCompanionWindowToBottom')} - className={classes.positionButton} onClick={() => { updateCompanionWindow({ position: position === 'bottom' ? 'right' : 'bottom' }); }} > <MoveIcon /> - </MiradorMenuButton> + </StyledPositionButton> ) } - <MiradorMenuButton + <StyledCloseButton + sx={{ + ...(size.width < 370 && { + order: 'unset', + }), + }} aria-label={t('closeCompanionWindow')} - className={classes.closeButton} onClick={onCloseClick} > <CloseIcon /> - </MiradorMenuButton> + </StyledCloseButton> </> ) } { titleControls && ( - <div - className={[ - classes.titleControls, - isBottom - ? classes.companionWindowTitleControlsBottom - : classes.companionWindowTitleControls, - ns('companion-window-title-controls'), - ].join(' ')} + <StyledTitleControls + ownerState={{ position }} + sx={{ + order: isBottom || size.width < 370 ? 'unset' : 1000, + }} + className={ns('companion-window-title-controls')} > {titleControls} - </div> + </StyledTitleControls> ) } - </Toolbar> - <Paper - className={[classes.content, ns('scrollto-scrollable')].join(' ')} + </StyledToolbar> + <Contents + className={ns('scrollto-scrollable')} elevation={0} > {childrenWithAdditionalProps} - </Paper> - </Rnd> - </Paper> + </Contents> + </StyledRnd> + </Root> ); } } @@ -187,10 +194,14 @@ export class CompanionWindow extends Component { CompanionWindow.propTypes = { ariaLabel: PropTypes.string, children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string).isRequired, + classes: PropTypes.objectOf(PropTypes.string), defaultSidebarPanelHeight: PropTypes.number, defaultSidebarPanelWidth: PropTypes.number, direction: PropTypes.string.isRequired, + innerRef: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), isDisplayed: PropTypes.bool, onCloseClick: PropTypes.func, paperClassName: PropTypes.string, @@ -208,8 +219,10 @@ CompanionWindow.propTypes = { CompanionWindow.defaultProps = { ariaLabel: undefined, children: undefined, + classes: {}, defaultSidebarPanelHeight: 201, defaultSidebarPanelWidth: 235, + innerRef: undefined, isDisplayed: false, onCloseClick: () => {}, paperClassName: '', diff --git a/src/components/CompanionWindowSection.js b/src/components/CompanionWindowSection.js new file mode 100644 index 0000000000000000000000000000000000000000..312164e85b190960ee6e0adab8b030d3146104fd --- /dev/null +++ b/src/components/CompanionWindowSection.js @@ -0,0 +1,8 @@ +import { styled } from '@mui/material/styles'; + +export const CompanionWindowSection = styled('div', { name: 'CompanionWindowSection', slot: 'root' })(({ theme }) => ({ + paddingBlockEnd: theme.spacing(1), + paddingBlockStart: theme.spacing(2), + paddingInlineEnd: theme.spacing(1), + paddingInlineStart: theme.spacing(2), +})); diff --git a/src/components/ErrorContent.js b/src/components/ErrorContent.js index d6e996ece45f003bfe8bb20b09483454a870409d..b179f769530332094f51158ba1dd664ee310f3c1 100644 --- a/src/components/ErrorContent.js +++ b/src/components/ErrorContent.js @@ -1,19 +1,34 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Accordion from '@material-ui/core/Accordion'; -import AccordionSummary from '@material-ui/core/AccordionSummary'; -import AccordionDetails from '@material-ui/core/AccordionDetails'; -import Typography from '@material-ui/core/Typography'; -import Alert from '@material-ui/lab/Alert'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { styled } from '@mui/material/styles'; +import Accordion from '@mui/material/Accordion'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import Stack from '@mui/material/Stack'; +import Alert from '@mui/material/Alert'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { PluginHook } from './PluginHook'; +const ErrorStackTrace = styled('pre', { name: 'ErrorContent', slot: 'stacktrace' })({ + overflowY: 'scroll', +}); + +const ErrorMetadata = styled('pre', { name: 'ErrorContent', slot: 'metadata' })({ + height: '100px', + overflowY: 'scroll', +}); + +const InlineAccordion = styled(Accordion, { name: 'ErrorContent', slot: 'accordion' })({ + backgroundColor: 'inherit', + color: 'inherit', + margin: 0, +}); + /** */ export class ErrorContent extends Component { /** */ render() { const { - classes, error, metadata, showJsError, @@ -23,34 +38,28 @@ export class ErrorContent extends Component { if (!showJsError) return null; return ( - <> - <Alert elevation={6} variant="filled" severity="error"> - {t('errorDialogTitle')} - </Alert> - + <Alert elevation={6} variant="filled" severity="error"> + {t('errorDialogTitle')} {showJsError && ( - <Accordion square className={classes.alert}> - <AccordionSummary - expandIcon={<ExpandMoreIcon />} - > - <Typography>{t('jsError', { message: error.message, name: error.name })}</Typography> + <InlineAccordion elevation={2} square> + <AccordionSummary sx={{ marginInlineStart: '-1rem' }} expandIcon={<ExpandMoreIcon sx={{ color: '#fff' }} />}> + {t('jsError', { message: error.message, name: error.name })} </AccordionSummary> - <AccordionDetails className={classes.details}> - <pre>{ t('jsStack', { stack: error.stack }) }</pre> - { metadata && ( - <pre>{JSON.stringify(metadata, null, 2)}</pre> - )} + <AccordionDetails> + <Stack> + <ErrorStackTrace>{t('jsStack', { stack: error.stack })}</ErrorStackTrace> + {metadata && <ErrorMetadata>{JSON.stringify(metadata, null, 2)}</ErrorMetadata>} + </Stack> </AccordionDetails> - </Accordion> + </InlineAccordion> )} <PluginHook {...this.props} /> - </> + </Alert> ); } } ErrorContent.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, error: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types metadata: PropTypes.object, // eslint-disable-line react/forbid-prop-types showJsError: PropTypes.bool, diff --git a/src/components/ErrorDialog.js b/src/components/ErrorDialog.js index e9f5b342ae0a07d856a68df8d7a9927eced27d70..5828a6b98b4f21249f9a0e808945c418f82ce055 100644 --- a/src/components/ErrorDialog.js +++ b/src/components/ErrorDialog.js @@ -1,10 +1,10 @@ import { Component } from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogTitle from '@material-ui/core/DialogTitle'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; import PropTypes from 'prop-types'; -import { DialogActions, DialogContentText, Typography } from '@material-ui/core'; -import Button from '@material-ui/core/Button'; +import { DialogActions, DialogContentText } from '@mui/material'; +import Button from '@mui/material/Button'; import { isUndefined } from 'lodash'; /** @@ -28,8 +28,8 @@ export class ErrorDialog extends Component { onClose={() => removeError(error.id)} open={hasError} > - <DialogTitle id="error-dialog-title" disableTypography> - <Typography variant="h2">{t('errorDialogTitle')}</Typography> + <DialogTitle id="error-dialog-title"> + {t('errorDialogTitle')} </DialogTitle> <DialogContent> <DialogContentText variant="body2" noWrap color="inherit"> diff --git a/src/components/FullScreenButton.js b/src/components/FullScreenButton.js index 2038fa9bc09f1675a5b0c9d31ea565f308419eee..0aabc6722ef9a9192bfd431c1464a9ea05f7e380 100644 --- a/src/components/FullScreenButton.js +++ b/src/components/FullScreenButton.js @@ -1,6 +1,6 @@ import { Component } from 'react'; -import FullscreenIcon from '@material-ui/icons/FullscreenSharp'; -import FullscreenExitIcon from '@material-ui/icons/FullscreenExitSharp'; +import FullscreenIcon from '@mui/icons-material/FullscreenSharp'; +import FullscreenExitIcon from '@mui/icons-material/FullscreenExitSharp'; import PropTypes from 'prop-types'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import FullScreenContext from '../contexts/FullScreenContext'; diff --git a/src/components/GalleryView.js b/src/components/GalleryView.js index 66d0348db419d1cf8e1ef5925c7cdede472768ed..1738f56baae0c98c0373d9d84d5e9272ecebccfe 100644 --- a/src/components/GalleryView.js +++ b/src/components/GalleryView.js @@ -1,8 +1,20 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Paper from '@material-ui/core/Paper'; +import { styled } from '@mui/material/styles'; +import Paper from '@mui/material/Paper'; import GalleryViewThumbnail from '../containers/GalleryViewThumbnail'; +const Root = styled(Paper, { name: 'GalleryView', slot: 'root' })(({ theme }) => ({ + alignItems: 'flex-start', + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + overflowX: 'hidden', + overflowY: 'scroll', + padding: '50px 0 50px 20px', + width: '100%', +})); + /** * Renders a GalleryView overview of the manifest. */ @@ -12,17 +24,16 @@ export class GalleryView extends Component { */ render() { const { - canvases, classes, viewingDirection, windowId, + canvases, viewingDirection, windowId, } = this.props; const htmlDir = viewingDirection === 'right-to-left' ? 'rtl' : 'ltr'; return ( - <Paper + <Root component="section" aria-label="gallery section" dir={htmlDir} square elevation={0} - className={classes.galleryContainer} id={`${windowId}-gallery`} > { @@ -34,19 +45,17 @@ export class GalleryView extends Component { /> )) } - </Paper> + </Root> ); } } GalleryView.propTypes = { canvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types - classes: PropTypes.objectOf(PropTypes.string), viewingDirection: PropTypes.string, windowId: PropTypes.string.isRequired, }; GalleryView.defaultProps = { - classes: {}, viewingDirection: '', }; diff --git a/src/components/GalleryViewThumbnail.js b/src/components/GalleryViewThumbnail.js index e475c98518f7307d6e3641fc56a54b8460ea48bc..efa36629d24ccc1240441ad26f2017e9a55b6948 100644 --- a/src/components/GalleryViewThumbnail.js +++ b/src/components/GalleryViewThumbnail.js @@ -1,14 +1,52 @@ import { createRef, Component } from 'react'; import PropTypes from 'prop-types'; -import Avatar from '@material-ui/core/Avatar'; -import Chip from '@material-ui/core/Chip'; -import AnnotationIcon from '@material-ui/icons/CommentSharp'; -import SearchIcon from '@material-ui/icons/SearchSharp'; -import classNames from 'classnames'; +import { styled } from '@mui/material/styles'; +import Chip from '@mui/material/Chip'; +import AnnotationIcon from '@mui/icons-material/CommentSharp'; +import SearchIcon from '@mui/icons-material/SearchSharp'; import { InView } from 'react-intersection-observer'; -import MiradorCanvas from '../lib/MiradorCanvas'; import IIIFThumbnail from '../containers/IIIFThumbnail'; +const Root = styled('div', { name: 'GalleryView', slot: 'thumbnail' })(({ ownerState, theme }) => ({ + '&:focus': { + outline: 'none', + }, + '&:hover': { + backgroundColor: theme.palette.action.hover, + }, + border: '2px solid transparent', + ...(ownerState.selected && { + borderColor: theme.palette.primary.main, + }), + ...(!ownerState.selected && ownerState.searchAnnotationsCount > 0 && { + borderColor: theme.palette.action.selected, + }), + cursor: 'pointer', + display: 'inline-block', + margin: theme.spacing(1, 0.5), + maxHeight: ownerState.config.height + 45, + minWidth: '60px', + overflow: 'hidden', + padding: theme.spacing(0.5), + position: 'relative', + width: 'min-content', +})); + +const StyledChipsContainer = styled('div', { name: 'GalleryView', slot: 'chipArea' })(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(0.25), + position: 'absolute', + right: 0, + top: 0, +})); + +const AnnotationChip = styled(Chip, { name: 'GalleryView', slot: 'chip' })(({ theme }) => ({ + backgroundColor: theme.palette.annotations.chipBackground, + opacity: 0.875, + textAlign: 'right', +})); + /** * Represents a WindowViewer in the mirador workspace. Responsible for mounting * OSD and Navigation @@ -102,22 +140,15 @@ export class GalleryViewThumbnail extends Component { render() { const { annotationsCount, searchAnnotationsCount, - canvas, classes, config, selected, + canvas, config, selected, } = this.props; - const miradorCanvas = new MiradorCanvas(canvas); - return ( <InView onChange={this.handleIntersection}> - <div - key={canvas.index} - className={ - classNames( - classes.galleryViewItem, - selected ? classes.selected : '', - searchAnnotationsCount > 0 ? classes.hasAnnotations : '', - ) - } + <Root + ownerState={this.props} + key={canvas.id || canvas.index} + className={selected ? 'selected' : ''} onClick={this.handleSelect} onKeyUp={this.handleKey} ref={this.myRef} @@ -128,45 +159,27 @@ export class GalleryViewThumbnail extends Component { resource={canvas} labelled variant="outside" - maxWidth={config.width} maxHeight={config.height} - style={{ - margin: '0 auto', - maxWidth: `${Math.ceil(config.height * miradorCanvas.aspectRatio)}px`, - }} + maxWidth={config.width} > - <div className={classes.chips}> - { searchAnnotationsCount > 0 && ( - <Chip - avatar={( - <Avatar className={classes.avatar} classes={{ circle: classes.avatarIcon }}> - <SearchIcon fontSize="small" /> - </Avatar> - )} + <StyledChipsContainer> + {searchAnnotationsCount > 0 && ( + <AnnotationChip + icon={<SearchIcon fontSize="small" />} label={searchAnnotationsCount} - className={classNames(classes.searchChip)} size="small" /> )} - { (annotationsCount || 0) > 0 && ( - <Chip - avatar={( - <Avatar className={classes.avatar} classes={{ circle: classes.avatarIcon }}> - <AnnotationIcon className={classes.annotationIcon} /> - </Avatar> - )} + {annotationsCount > 0 && ( + <AnnotationChip + icon={<AnnotationIcon fontSize="small" />} label={annotationsCount} - className={ - classNames( - classes.annotationsChip, - ) - } size="small" /> )} - </div> + </StyledChipsContainer> </IIIFThumbnail> - </div> + </Root> </InView> ); } @@ -175,7 +188,6 @@ export class GalleryViewThumbnail extends Component { GalleryViewThumbnail.propTypes = { annotationsCount: PropTypes.number, canvas: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types - classes: PropTypes.objectOf(PropTypes.string).isRequired, config: PropTypes.shape({ height: PropTypes.number, width: PropTypes.number, diff --git a/src/components/IIIFDropTarget.js b/src/components/IIIFDropTarget.js index 8b48beefc91d35ca4d149023e1f3846f58c4012f..6a15f7123055df530e6f5576d93d48176f3c8697 100644 --- a/src/components/IIIFDropTarget.js +++ b/src/components/IIIFDropTarget.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; -import Backdrop from '@material-ui/core/Backdrop'; -import InsertDriveFileSharpIcon from '@material-ui/icons/InsertDriveFileSharp'; -import { grey } from '@material-ui/core/colors'; +import Backdrop from '@mui/material/Backdrop'; +import InsertDriveFileSharpIcon from '@mui/icons-material/InsertDriveFileSharp'; +import { grey } from '@mui/material/colors'; import { v4 as uuid } from 'uuid'; import { NativeTypes } from 'react-dnd-html5-backend'; import { useDrop } from 'react-dnd'; diff --git a/src/components/IIIFThumbnail.js b/src/components/IIIFThumbnail.js index ade25534a6495af499aa63f2707a98dcf10f8756..1fd61e66fe9a976cee7a569d17c75eb5f580ccf5 100644 --- a/src/components/IIIFThumbnail.js +++ b/src/components/IIIFThumbnail.js @@ -2,17 +2,27 @@ import { Component, useMemo, useEffect, useState, } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; +import { styled } from '@mui/material/styles'; import { useInView } from 'react-intersection-observer'; -import classNames from 'classnames'; import getThumbnail from '../lib/ThumbnailFactory'; +const Root = styled('div', { name: 'IIIFThumbnail', slot: 'root' })({}); + +const Label = styled('span', { name: 'IIIFThumbnail', slot: 'label' })(({ theme }) => ({ + ...theme.typography.caption, +})); + +const Image = styled('img', { name: 'IIIFThumbnail', slot: 'image' })(() => ({ + height: 'auto', + width: 'auto', +})); + /** * A lazy-loaded image that uses IntersectionObserver to determine when to * try to load the image (or even calculate that the image url/height/width are) */ const LazyLoadedImage = ({ - placeholder, style = {}, thumbnail, resource, maxHeight, maxWidth, thumbnailsConfig, ...props + border, placeholder, style = {}, thumbnail, resource, maxHeight, maxWidth, thumbnailsConfig, ...props }) => { const { ref, inView } = useInView(); const [loaded, setLoaded] = useState(false); @@ -38,9 +48,14 @@ const LazyLoadedImage = ({ }, [resource, thumbnail, maxWidth, maxHeight, thumbnailsConfig]); const imageStyles = useMemo(() => { - const styleProps = { height: 'auto', width: 'auto' }; + const styleProps = { + height: undefined, + maxHeight: undefined, + maxWidth: undefined, + width: undefined, + }; - if (!image) return { ...style, height: maxHeight || 'auto', width: maxWidth || 'auto' }; + if (!image) return { ...style, height: maxHeight, width: maxWidth }; const { height: thumbHeight, width: thumbWidth } = image; if (thumbHeight && thumbWidth) { @@ -87,7 +102,8 @@ const LazyLoadedImage = ({ const { url: src = placeholder } = (loaded && (thumbnail || image)) || {}; return ( - <img + <Image + ownerState={{ border }} ref={ref} alt="" role="presentation" @@ -99,6 +115,7 @@ const LazyLoadedImage = ({ }; LazyLoadedImage.propTypes = { + border: PropTypes.bool, maxHeight: PropTypes.number.isRequired, maxWidth: PropTypes.number.isRequired, placeholder: PropTypes.string.isRequired, @@ -113,6 +130,7 @@ LazyLoadedImage.propTypes = { }; LazyLoadedImage.defaultProps = { + border: false, style: {}, thumbnail: null, thumbnailsConfig: {}, @@ -142,8 +160,8 @@ export class IIIFThumbnail extends Component { */ render() { const { + border, children, - classes, imagePlaceholder, labelled, maxHeight, @@ -152,11 +170,10 @@ export class IIIFThumbnail extends Component { style, thumbnail, thumbnailsConfig, - variant, } = this.props; return ( - <div className={classNames(classes.root, { [classes[`${variant}Root`]]: variant })}> + <Root ownerState={this.props}> <LazyLoadedImage placeholder={imagePlaceholder} thumbnail={thumbnail} @@ -165,25 +182,23 @@ export class IIIFThumbnail extends Component { maxWidth={maxWidth} thumbnailsConfig={thumbnailsConfig} style={style} - className={classes.image} + border={border} /> { labelled && ( - <div className={classNames(classes.label, { [classes[`${variant}Label`]]: variant })}> - <Typography variant="caption" classes={{ root: classNames(classes.caption, { [classes[`${variant}Caption`]]: variant }) }}> - {this.label()} - </Typography> - </div> + <Label ownerState={this.props}> + {this.label()} + </Label> )} {children} - </div> + </Root> ); } } IIIFThumbnail.propTypes = { + border: PropTypes.bool, children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string), imagePlaceholder: PropTypes.string, label: PropTypes.string, labelled: PropTypes.bool, @@ -197,12 +212,12 @@ IIIFThumbnail.propTypes = { width: PropTypes.number, }), thumbnailsConfig: PropTypes.object, // eslint-disable-line react/forbid-prop-types - variant: PropTypes.oneOf(['inside', 'outside']), + variant: PropTypes.oneOf(['inside', 'outside']), // eslint-disable-line react/no-unused-prop-types }; IIIFThumbnail.defaultProps = { + border: false, children: null, - classes: {}, // Transparent "gray" imagePlaceholder: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mMMDQmtBwADgwF/Op8FmAAAAABJRU5ErkJggg==', label: undefined, diff --git a/src/components/LabelValueMetadata.js b/src/components/LabelValueMetadata.js index 9cb73df2718649d39f1ae71470b0841d926167d2..b1fe6c9a29d5d11e9f76a56394d30ace140f5fa1 100644 --- a/src/components/LabelValueMetadata.js +++ b/src/components/LabelValueMetadata.js @@ -1,6 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; import SanitizedHtml from '../containers/SanitizedHtml'; import ns from '../config/css-ns'; diff --git a/src/components/LanguageSettings.js b/src/components/LanguageSettings.js index 7901c4e440ada6bd9e5f251b71998158a850ed6a..f0048c4788abbc9b9ef50d49818dbd40cf4f7456 100644 --- a/src/components/LanguageSettings.js +++ b/src/components/LanguageSettings.js @@ -1,8 +1,8 @@ import { Component } from 'react'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import ListItemText from '@material-ui/core/ListItemText'; -import MenuItem from '@material-ui/core/MenuItem'; -import CheckIcon from '@material-ui/icons/CheckSharp'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuItem from '@mui/material/MenuItem'; +import CheckIcon from '@mui/icons-material/CheckSharp'; import PropTypes from 'prop-types'; /** @@ -23,7 +23,6 @@ export class LanguageSettings extends Component { { languages.map(language => ( <MenuItem - button={!language.current} key={language.locale} onClick={() => { handleClick(language.locale); }} > diff --git a/src/components/LocalePicker.js b/src/components/LocalePicker.js index beb212324eb40595e03bde77284bd7a2e3303636..a1f9d8593dea7ff5e7751ea8e583951859c4f22a 100644 --- a/src/components/LocalePicker.js +++ b/src/components/LocalePicker.js @@ -1,9 +1,9 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import MenuItem from '@material-ui/core/MenuItem'; -import FormControl from '@material-ui/core/FormControl'; -import Select from '@material-ui/core/Select'; -import Typography from '@material-ui/core/Typography'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import Typography from '@mui/material/Typography'; /** * Provide a locale picker @@ -16,7 +16,6 @@ export class LocalePicker extends Component { render() { const { availableLocales, - classes, locale, setLocale, } = this.props; @@ -30,14 +29,11 @@ export class LocalePicker extends Component { horizontal: 'left', vertical: 'bottom', }, - getContentAnchorEl: null, }} displayEmpty value={locale} onChange={(e) => { setLocale(e.target.value); }} name="locale" - classes={{ select: classes.select }} - className={classes.selectEmpty} > { availableLocales.map(l => ( @@ -52,14 +48,12 @@ export class LocalePicker extends Component { LocalePicker.propTypes = { availableLocales: PropTypes.arrayOf(PropTypes.string), - classes: PropTypes.objectOf(PropTypes.string), locale: PropTypes.string, setLocale: PropTypes.func, }; LocalePicker.defaultProps = { availableLocales: [], - classes: {}, locale: '', setLocale: undefined, }; diff --git a/src/components/ManifestForm.js b/src/components/ManifestForm.js index 18d304cd6dca0f1301e1dd735d812d9135641eb8..2266db77c9df80ab685463034e7e9872117c2868 100644 --- a/src/components/ManifestForm.js +++ b/src/components/ManifestForm.js @@ -1,8 +1,8 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import Grid from '@material-ui/core/Grid'; -import TextField from '@material-ui/core/TextField'; +import Button from '@mui/material/Button'; +import Grid from '@mui/material/Grid'; +import TextField from '@mui/material/TextField'; /** * Provides a form for user input of a manifest url @@ -68,7 +68,6 @@ export class ManifestForm extends Component { const { formValue } = this.state; const { addResourcesOpen, - classes, onCancel, t, } = this.props; @@ -92,11 +91,19 @@ export class ManifestForm extends Component { shrink: true, }} InputProps={{ - className: classes.input, + style: { typography: 'body1' }, }} /> </Grid> - <Grid item xs={12} sm={4} md={3} className={classes.buttons}> + <Grid + item + xs={12} + sm={4} + md={3} + sx={{ + textAlign: { sm: 'inherit', xs: 'right' }, + }} + > { onCancel && ( <Button onClick={this.handleCancel}> {t('cancel')} @@ -115,14 +122,12 @@ export class ManifestForm extends Component { ManifestForm.propTypes = { addResource: PropTypes.func.isRequired, addResourcesOpen: PropTypes.bool.isRequired, - classes: PropTypes.objectOf(PropTypes.string), onCancel: PropTypes.func, onSubmit: PropTypes.func, t: PropTypes.func, }; ManifestForm.defaultProps = { - classes: {}, onCancel: null, onSubmit: () => {}, t: key => key, diff --git a/src/components/ManifestInfo.js b/src/components/ManifestInfo.js index 4d9b2339257fd314eeae95916a35b6bf71ce453c..e465a054da023abe4245147b934ff4759aef2918 100644 --- a/src/components/ManifestInfo.js +++ b/src/components/ManifestInfo.js @@ -1,6 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; import CollapsibleSection from '../containers/CollapsibleSection'; import SanitizedHtml from '../containers/SanitizedHtml'; import { LabelValueMetadata } from './LabelValueMetadata'; diff --git a/src/components/ManifestListItem.js b/src/components/ManifestListItem.js index accd5ede56d5dadcfb6ab8d791359aaeaf838b13..18e26fbc921ecb673c5afc94870e2e7e214ea555 100644 --- a/src/components/ManifestListItem.js +++ b/src/components/ManifestListItem.js @@ -1,14 +1,42 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import ListItem from '@material-ui/core/ListItem'; -import ButtonBase from '@material-ui/core/ButtonBase'; -import Grid from '@material-ui/core/Grid'; -import Typography from '@material-ui/core/Typography'; -import Skeleton from '@material-ui/lab/Skeleton'; +import { styled } from '@mui/material/styles'; +import ListItem from '@mui/material/ListItem'; +import ButtonBase from '@mui/material/ButtonBase'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import Skeleton from '@mui/material/Skeleton'; import { Img } from 'react-image'; import ManifestListItemError from '../containers/ManifestListItemError'; import ns from '../config/css-ns'; +const Root = styled(ListItem, { name: 'ManifestListItem', slot: 'root' })(({ ownerState, theme }) => ({ + '&:hover,&:focus-within': { + backgroundColor: theme.palette.action.hover, + borderLeftColor: ownerState?.active ? theme.palette.primary.main : theme.palette.action.hover, + }, + borderLeft: '4px solid', + borderLeftColor: ownerState?.active ? theme.palette.primary.main : 'transparent', + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + [theme.breakpoints.up('sm')]: { + paddingLeft: theme.spacing(3), + paddingRight: theme.spacing(3), + }, +})); + +const StyledThumbnail = styled(Img, { name: 'ManifestListItem', slot: 'thumbnail' })(({ theme }) => ({ + maxWidth: '100%', + objectFit: 'contain', +})); + +const StyledLogo = styled(Img, { name: 'ManifestListItem', slot: 'logo' })(({ theme }) => ({ + height: '2.5rem', + maxWidth: '100%', + objectFit: 'contain', + paddingRight: 1, +})); + /** * Represents an item in a list of currently-loaded or loading manifests * @param {object} props @@ -57,7 +85,6 @@ export class ManifestListItem extends Component { thumbnail, manifestLogo, size, - classes, provider, t, error, @@ -68,61 +95,81 @@ export class ManifestListItem extends Component { const placeholder = ( <Grid container className={ns('manifest-list-item')} spacing={2}> <Grid item xs={3} sm={2}> - <Skeleton className={classes.placeholder} variant="rect" height={80} width={120} /> + <Skeleton sx={{ bgcolor: 'grey[300]' }} variant="rectangular" height={80} width={120} /> </Grid> <Grid item xs={9} sm={6}> - <Skeleton className={classes.placeholder} variant="text" /> + <Skeleton sx={{ bgcolor: 'grey[300]' }} variant="text" /> </Grid> <Grid item xs={8} sm={2}> - <Skeleton className={classes.placeholder} variant="text" /> - <Skeleton className={classes.placeholder} variant="text" /> + <Skeleton sx={{ bgcolor: 'grey[300]' }} variant="text" /> + <Skeleton sx={{ bgcolor: 'grey[300]' }} variant="text" /> </Grid> <Grid item xs={4} sm={2}> - <Skeleton className={classes.placeholder} variant="rect" height={60} width={60} /> + <Skeleton sx={{ bgcolor: 'grey[300]' }} variant="rectangular" height={60} width={60} /> </Grid> </Grid> ); if (error) { return ( - <ListItem divider className={classes.root} data-manifestid={manifestId}> + <Root + ownerState={this.props} + divider + selected={active} + className={active ? 'active' : ''} + data-manifestid={manifestId} + > <ManifestListItemError manifestId={manifestId} /> - </ListItem> + </Root> ); } return ( - <ListItem divider className={[classes.root, active ? classes.active : ''].join(' ')} data-manifestid={manifestId}> + <Root + divider + selected={active} + className={active ? 'active' : ''} + data-manifestid={manifestId} + data-active={active} + > {ready ? ( <Grid container className={ns('manifest-list-item')} spacing={2}> - <Grid item xs={12} sm={6} className={classes.buttonGrid}> + <Grid item xs={12} sm={6}> <ButtonBase ref={buttonRef} className={ns('manifest-list-item-title')} style={{ width: '100%' }} onClick={this.handleOpenButtonClick} > - <Grid container spacing={2} className={classes.label} component="span"> + <Grid + container + spacing={2} + sx={{ + textAlign: 'left', + textTransform: 'initial', + }} + component="span" + > <Grid item xs={4} sm={3} component="span"> { thumbnail ? ( - <Img - className={[classes.thumbnail, ns('manifest-list-item-thumb')].join(' ')} + <StyledThumbnail + className={[ns('manifest-list-item-thumb')]} src={[thumbnail]} alt="" height="80" unloader={( <Skeleton - variant="rect" + variant="rectangular" animation={false} - className={classes.placeholder} + sx={{ bgcolor: 'grey[300]' }} height={80} width={120} /> )} /> ) - : <Skeleton className={classes.placeholder} variant="rect" height={80} width={120} />} + : <Skeleton sx={{ bgcolor: 'grey[300]' }} variant="rectangular" height={80} width={120} />} </Grid> <Grid item xs={8} sm={9} component="span"> { isCollection && ( @@ -145,16 +192,15 @@ export class ManifestListItem extends Component { <Grid item xs={4} sm={2}> { manifestLogo && ( - <Img + <StyledLogo src={[manifestLogo]} alt="" role="presentation" - className={classes.logo} unloader={( <Skeleton - variant="rect" + variant="rectangular" animation={false} - className={classes.placeholder} + sx={{ bgcolor: 'grey[300]' }} height={60} width={60} /> @@ -166,7 +212,7 @@ export class ManifestListItem extends Component { ) : ( placeholder )} - </ListItem> + </Root> ); } } @@ -175,7 +221,6 @@ ManifestListItem.propTypes = { active: PropTypes.bool, addWindow: PropTypes.func.isRequired, buttonRef: PropTypes.elementType, - classes: PropTypes.objectOf(PropTypes.string), error: PropTypes.string, fetchManifest: PropTypes.func.isRequired, handleClose: PropTypes.func, @@ -195,7 +240,6 @@ ManifestListItem.propTypes = { ManifestListItem.defaultProps = { active: false, buttonRef: undefined, - classes: {}, error: null, handleClose: () => {}, isCollection: false, diff --git a/src/components/ManifestListItemError.js b/src/components/ManifestListItemError.js index 3d63e48afd3445ff3df92814908d91c9ca19a1d2..b8d5176aa5aaa6a967b432f2af601f48fcd67268 100644 --- a/src/components/ManifestListItemError.js +++ b/src/components/ManifestListItemError.js @@ -1,9 +1,9 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import ErrorIcon from '@material-ui/icons/ErrorOutlineSharp'; -import Grid from '@material-ui/core/Grid'; -import Typography from '@material-ui/core/Typography'; +import Button from '@mui/material/Button'; +import ErrorIcon from '@mui/icons-material/ErrorOutlineSharp'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; /** * ManifestListItemError renders a component displaying a @@ -15,7 +15,7 @@ export class ManifestListItemError extends Component { */ render() { const { - classes, manifestId, onDismissClick, onTryAgainClick, t, + manifestId, onDismissClick, onTryAgainClick, t, } = this.props; return ( @@ -24,12 +24,17 @@ export class ManifestListItemError extends Component { <Grid container item xs={12} sm={6}> <Grid item xs={4} sm={3}> <Grid container justifyContent="center"> - <ErrorIcon className={classes.errorIcon} /> + <ErrorIcon sx={{ + color: 'error.main', + height: '2rem', + width: '2rem', + }} + /> </Grid> </Grid> <Grid item xs={8} sm={9}> <Typography>{t('manifestError')}</Typography> - <Typography className={classes.manifestIdText}>{manifestId}</Typography> + <Typography sx={{ wordBreak: 'break-all' }}>{manifestId}</Typography> </Grid> </Grid> </Grid> @@ -52,7 +57,6 @@ export class ManifestListItemError extends Component { } ManifestListItemError.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, manifestId: PropTypes.string.isRequired, onDismissClick: PropTypes.func.isRequired, onTryAgainClick: PropTypes.func.isRequired, diff --git a/src/components/ManifestRelatedLinks.js b/src/components/ManifestRelatedLinks.js index 62436ea79a4ba2009ff7ae9cee421a83e1995d40..cbba6aefd1defc8c2a02ab6f68a6b459a27d2ec6 100644 --- a/src/components/ManifestRelatedLinks.js +++ b/src/components/ManifestRelatedLinks.js @@ -1,12 +1,19 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; -import Link from '@material-ui/core/Link'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Link from '@mui/material/Link'; import classNames from 'classnames'; import CollapsibleSection from '../containers/CollapsibleSection'; import ns from '../config/css-ns'; import { PluginHook } from './PluginHook'; +const StyledDl = styled('dl')(({ theme }) => ({ + '& dd': { + marginBottom: '.5em', + marginLeft: '0', + }, +})); /** * ManifestRelatedLinks */ @@ -17,7 +24,6 @@ export class ManifestRelatedLinks extends Component { */ render() { const { - classes, homepage, manifestUrl, related, @@ -40,7 +46,7 @@ export class ManifestRelatedLinks extends Component { > {t('links')} </Typography> - <dl className={classNames(ns('label-value-metadata'), classes.labelValueMetadata)}> + <StyledDl className={classNames(ns('label-value-metadata'))}> { homepage && ( <> <Typography variant="subtitle2" component="dt">{t('iiif_homepage')}</Typography> @@ -113,7 +119,7 @@ export class ManifestRelatedLinks extends Component { </Typography> </> )} - </dl> + </StyledDl> <PluginHook {...this.props} /> </CollapsibleSection> ); @@ -121,7 +127,6 @@ export class ManifestRelatedLinks extends Component { } ManifestRelatedLinks.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, homepage: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string, value: PropTypes.string, diff --git a/src/components/MinimalWindow.js b/src/components/MinimalWindow.js index f19b35385f339bf38761b7d6390e421101833330..b04661692437238242a47ea242933c80c82c0bfe 100644 --- a/src/components/MinimalWindow.js +++ b/src/components/MinimalWindow.js @@ -1,15 +1,19 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import MenuIcon from '@material-ui/icons/MenuSharp'; +import { styled } from '@mui/material/styles'; +import MenuIcon from '@mui/icons-material/MenuSharp'; import cn from 'classnames'; -import Paper from '@material-ui/core/Paper'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; -import CloseIcon from '@material-ui/icons/CloseSharp'; +import Paper from '@mui/material/Paper'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import CloseIcon from '@mui/icons-material/CloseSharp'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import ns from '../config/css-ns'; +const StyledMiradorMenuButton = styled(MiradorMenuButton)(() => ({ + marginLeft: 'auto', +})); /** */ export class MinimalWindow extends Component { /** */ @@ -19,7 +23,6 @@ export class MinimalWindow extends Component { allowWindowSideBar, ariaLabel, children, - classes, label, removeWindow, t, @@ -32,17 +35,31 @@ export class MinimalWindow extends Component { elevation={1} id={windowId} className={ - cn(classes.window, ns('placeholder-window')) + cn(ns('placeholder-window')) } + sx={{ + backgroundColor: 'shades.dark', + borderRadius: 0, + display: 'flex', + flexDirection: 'column', + height: '100%', + minHeight: 0, + overflow: 'hidden', + width: '100%', + }} aria-label={label && ariaLabel ? t('window', { label }) : null} > - <AppBar position="relative" color="default"> + <AppBar position="relative" color="default" enableColorOnDark> <Toolbar disableGutters - className={cn( - classes.windowTopBarStyle, - ns('window-top-bar'), - )} + className={cn(ns('window-top-bar'))} + sx={{ + backgroundColor: 'shades.main', + borderTop: '2px solid transparent', + minHeight: 32, + paddingLeft: 0.5, + paddingRight: 0.5, + }} variant="dense" > {allowWindowSideBar && ( @@ -53,20 +70,29 @@ export class MinimalWindow extends Component { <MenuIcon /> </MiradorMenuButton> )} - <Typography variant="h2" noWrap color="inherit" className={classes.title}> + <Typography + variant="h2" + noWrap + color="inherit" + sx={{ + flexGrow: 1, + paddingLeft: 0.5, + typography: 'h6', + }} + > {label} </Typography> {allowClose && removeWindow && ( - <MiradorMenuButton + <StyledMiradorMenuButton aria-label={t('closeWindow')} - className={cn(classes.button, ns('window-close'))} + className={cn(ns('window-close'))} onClick={removeWindow} TooltipProps={{ - tabIndex: ariaLabel ? '0' : '-1', + tabIndex: ariaLabel ? 0 : -1, }} > <CloseIcon /> - </MiradorMenuButton> + </StyledMiradorMenuButton> )} </Toolbar> </AppBar> @@ -81,7 +107,6 @@ MinimalWindow.propTypes = { allowWindowSideBar: PropTypes.bool, ariaLabel: PropTypes.bool, children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string), label: PropTypes.string, removeWindow: PropTypes.func, t: PropTypes.func, @@ -93,7 +118,6 @@ MinimalWindow.defaultProps = { allowWindowSideBar: true, ariaLabel: true, children: null, - classes: {}, label: '', removeWindow: () => {}, t: key => key, diff --git a/src/components/MiradorMenuButton.js b/src/components/MiradorMenuButton.js index 869c1c0a024b9c54df2960d9698da1be2e47d535..e1def5a7b87b9283121bacf3e8a85e04da4e6b2f 100644 --- a/src/components/MiradorMenuButton.js +++ b/src/components/MiradorMenuButton.js @@ -1,7 +1,15 @@ import PropTypes from 'prop-types'; -import Badge from '@material-ui/core/Badge'; -import IconButton from '@material-ui/core/IconButton'; -import Tooltip from '@material-ui/core/Tooltip'; +import { styled } from '@mui/material/styles'; +import Badge from '@mui/material/Badge'; +import IconButton from '@mui/material/IconButton'; +import Tooltip from '@mui/material/Tooltip'; + +const Root = styled(IconButton, { name: 'MiradorMenuButton', slot: 'root' })(({ ownerState, theme }) => ({ + fill: 'currentcolor', + ...(ownerState.selected && { + backgroundColor: theme.palette.action.selected, + }), +})); /** * MiradorMenuButton ~ Wrap the given icon prop in an IconButton and a Tooltip. @@ -17,15 +25,28 @@ export function MiradorMenuButton(props) { dispatch, BadgeProps, TooltipProps, + sx, ...iconButtonProps } = props; const button = ( - <IconButton {...iconButtonProps}> + <Root + ownerState={props} + {...iconButtonProps} + sx={sx} + size="large" + > {badge - ? <Badge overlap="rectangular" {...BadgeProps}>{children}</Badge> + ? ( + <Badge + overlap="rectangular" + {...BadgeProps} + > + {children} + </Badge> + ) : children} - </IconButton> + </Root> ); if (iconButtonProps.disabled) return button; @@ -50,6 +71,8 @@ MiradorMenuButton.propTypes = { children: PropTypes.element.isRequired, container: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), dispatch: PropTypes.func, + selected: PropTypes.bool, + sx: PropTypes.object, // eslint-disable-line react/forbid-prop-types TooltipProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types }; @@ -58,5 +81,7 @@ MiradorMenuButton.defaultProps = { BadgeProps: {}, container: null, dispatch: () => {}, + selected: false, + sx: {}, TooltipProps: {}, }; diff --git a/src/components/NestedMenu.js b/src/components/NestedMenu.js index 65fcfeec57a1d7681510747b3dc3bb120358d57f..b61107786453d1b98aff1749f704089d9e686e10 100644 --- a/src/components/NestedMenu.js +++ b/src/components/NestedMenu.js @@ -1,10 +1,10 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import ListItemText from '@material-ui/core/ListItemText'; -import MenuItem from '@material-ui/core/MenuItem'; -import ExpandLess from '@material-ui/icons/ExpandLessSharp'; -import ExpandMore from '@material-ui/icons/ExpandMoreSharp'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuItem from '@mui/material/MenuItem'; +import ExpandLess from '@mui/icons-material/ExpandLessSharp'; +import ExpandMore from '@mui/icons-material/ExpandMoreSharp'; /** * NestedMenu ~ A presentation component to render a menu item and have diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index f2ce17d670a9b5b5d7c0586fc8bda242fdc92b67..120aba5151fee7995e5985902cf3e8cf8bcdcc84 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -2,6 +2,7 @@ import { createRef, Children, cloneElement, Component, } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import debounce from 'lodash/debounce'; import isEqual from 'lodash/isEqual'; import OpenSeadragon from 'openseadragon'; @@ -12,6 +13,11 @@ import CanvasWorld from '../lib/CanvasWorld'; import { PluginHook } from './PluginHook'; import { OSDReferences } from '../plugins/OSDReferences'; +const StyledSection = styled('section')({ + cursor: 'grab', + flex: 1, + position: 'relative', +}); /** * Represents a OpenSeadragonViewer in the mirador workspace. Responsible for mounting * and rendering OSD. @@ -340,7 +346,7 @@ export class OpenSeadragonViewer extends Component { */ render() { const { - children, classes, label, t, windowId, + children, label, t, windowId, drawAnnotations, } = this.props; const { viewer, grabbing } = this.state; @@ -355,8 +361,8 @@ export class OpenSeadragonViewer extends Component { )); return ( - <section - className={classNames(ns('osd-container'), classes.osdContainer)} + <StyledSection + className={classNames(ns('osd-container'))} style={{ cursor: grabbing ? 'grabbing' : undefined }} id={`${windowId}-osd`} ref={this.ref} @@ -367,7 +373,7 @@ export class OpenSeadragonViewer extends Component { && <AnnotationsOverlay viewer={viewer} windowId={windowId} /> } { enhancedChildren } <PluginHook viewer={viewer} {...{ ...this.props, children: null }} /> - </section> + </StyledSection> ); } } @@ -385,7 +391,6 @@ OpenSeadragonViewer.defaultProps = { OpenSeadragonViewer.propTypes = { canvasWorld: PropTypes.instanceOf(CanvasWorld).isRequired, children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string).isRequired, drawAnnotations: PropTypes.bool, infoResponses: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types label: PropTypes.string, diff --git a/src/components/PrimaryWindow.js b/src/components/PrimaryWindow.js index 988b6784aea40a2167782f35ea06cc8964e10851..30c4d302eb4e9794fc9c9b73e318c02a24e78982 100644 --- a/src/components/PrimaryWindow.js +++ b/src/components/PrimaryWindow.js @@ -1,5 +1,6 @@ import { Component, lazy, Suspense } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import classNames from 'classnames'; import WindowSideBar from '../containers/WindowSideBar'; import CompanionArea from '../containers/CompanionArea'; @@ -16,6 +17,12 @@ GalleryView.displayName = 'GalleryView'; SelectCollection.displayName = 'SelectCollection'; WindowViewer.displayName = 'WindowViewer'; +const Root = styled('div', { name: 'PrimaryWindow', slot: 'root' })(() => ({ + display: 'flex', + flex: 1, + position: 'relative', +})); + /** * PrimaryWindow - component that renders the primary content of a Mirador * window. Right now this differentiates between a Image, Video, or Audio viewer. @@ -74,17 +81,18 @@ export class PrimaryWindow extends Component { */ render() { const { - isCollectionDialogVisible, windowId, classes, children, + isCollectionDialogVisible, windowId, children, className, } = this.props; + return ( - <div className={classNames(ns('primary-window'), classes.primaryWindow)}> + <Root data-testid="test-window" className={classNames(ns('primary-window'), className)}> <WindowSideBar windowId={windowId} /> <CompanionArea windowId={windowId} position="left" /> { isCollectionDialogVisible && <CollectionDialog windowId={windowId} /> } <Suspense fallback={<div />}> {children || this.renderViewer()} </Suspense> - </div> + </Root> ); } } @@ -92,7 +100,7 @@ export class PrimaryWindow extends Component { PrimaryWindow.propTypes = { audioResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string).isRequired, + className: PropTypes.string, isCollection: PropTypes.bool, isCollectionDialogVisible: PropTypes.bool, isFetching: PropTypes.bool, @@ -104,6 +112,7 @@ PrimaryWindow.propTypes = { PrimaryWindow.defaultProps = { audioResources: [], children: undefined, + className: undefined, isCollection: false, isCollectionDialogVisible: false, isFetching: false, diff --git a/src/components/SanitizedHtml.js b/src/components/SanitizedHtml.js index 1cd1f4b24f8e66ba51301bcf4776b116327d2a44..dcbab42e3b20ca80f030dd607c9f5639d4658cb3 100644 --- a/src/components/SanitizedHtml.js +++ b/src/components/SanitizedHtml.js @@ -1,9 +1,12 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import DOMPurify from 'dompurify'; import ns from '../config/css-ns'; import htmlRules from '../lib/htmlRules'; +const Root = styled('span', { name: 'IIIFHtmlContent', slot: 'root' })({}); + /** */ export class SanitizedHtml extends Component { @@ -25,8 +28,8 @@ export class SanitizedHtml extends Component { }); return ( - <span - className={[classes.root, ns('third-party-html')].join(' ')} + <Root + className={[ns('third-party-html'), classes.root].join(' ')} dangerouslySetInnerHTML={{ // eslint-disable-line react/no-danger __html: DOMPurify.sanitize(htmlString, htmlRules[ruleSet]), }} diff --git a/src/components/ScrollIndicatedDialogContent.js b/src/components/ScrollIndicatedDialogContent.js index a8de5229d8d8e09e70aa881432246ccb568c5fc3..01e10ca255355b32e255b4f3380d0bfa6e814fb5 100644 --- a/src/components/ScrollIndicatedDialogContent.js +++ b/src/components/ScrollIndicatedDialogContent.js @@ -1,5 +1,49 @@ import PropTypes from 'prop-types'; -import DialogContent from '@material-ui/core/DialogContent'; +import DialogContent from '@mui/material/DialogContent'; +import { alpha, styled } from '@mui/material/styles'; + +/** + * From https://github.com/mui/material-ui/blob/v5.15.0/packages/mui-material/src/styles/getOverlayAlpha.ts + */ +const getOverlayAlpha = (elevation) => { + let alphaValue; + if (elevation < 1) { + alphaValue = 5.11916 * elevation ** 2; + } else { + alphaValue = 4.5 * Math.log(elevation + 1) + 2; + } + return (alphaValue / 100).toFixed(2); +}; + +const Root = styled(DialogContent, { name: 'ScrollIndicatedDialogContent', slot: 'root' })(({ ownerState, theme }) => { + // In dark mode, paper has a elevation-dependent background color: + // https://github.com/mui/material-ui/blob/v5.15.0/packages/mui-material/src/Paper/Paper.js#L55-L60 + const bgcolor = theme.palette.mode === 'dark' ? { + backgroundImage: `linear-gradient(${alpha( + '#fff', + getOverlayAlpha(ownerState?.elevation || 24), + )}, ${alpha('#fff', getOverlayAlpha(ownerState?.elevation || 24))})`, + } : theme.palette.background.paper; + return { + /* Shadow covers */ + background: `linear-gradient(${bgcolor} 30%, rgba(255, 255, 255, 0)), ` + + `linear-gradient(rgba(255, 255, 255, 0), ${bgcolor} 70%) 0 100%, ` + // Shaddows + + 'radial-gradient(50% 0, farthest-side, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)), ' + + 'radial-gradient(50% 100%, farthest-side, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) 0 100%,', + /* Shadow covers */ + background: `linear-gradient(${bgcolor} 30%, rgba(255, 255, 255, 0)), ` // eslint-disable-line no-dupe-keys + + `linear-gradient(rgba(255, 255, 255, 0), ${bgcolor} 70%) 0 100%, ` + // Shaddows + + 'radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)), ' + + 'radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) 0 100%;', + + backgroundAttachment: 'local, local, scroll, scroll', + backgroundRepeat: 'no-repeat', + backgroundSize: '100% 40px, 100% 40px, 100% 14px, 100% 14px', + overflowY: 'auto', + }; +}); /** * ScrollIndicatedDialogContent ~ Inject a style into the DialogContent component @@ -10,18 +54,21 @@ export function ScrollIndicatedDialogContent(props) { const ourClassName = [className, classes.shadowScrollDialog].join(' '); return ( - <DialogContent className={ourClassName} {...otherProps} /> + <Root + className={ourClassName} + {...otherProps} + /> ); } ScrollIndicatedDialogContent.propTypes = { classes: PropTypes.shape({ shadowScrollDialog: PropTypes.string, - }).isRequired, - + }), className: PropTypes.string, }; ScrollIndicatedDialogContent.defaultProps = { + classes: {}, className: '', }; diff --git a/src/components/ScrollTo.js b/src/components/ScrollTo.js index e73d602cf998bf9d9adcdc17d6b730c4b91406f5..4e11892d7267729ba6619f090c407910785d3612 100644 --- a/src/components/ScrollTo.js +++ b/src/components/ScrollTo.js @@ -1,5 +1,6 @@ -import { createRef, Component } from 'react'; +import { cloneElement, createRef, Component } from 'react'; import PropTypes from 'prop-types'; +import isEmpty from 'lodash/isEmpty'; /** * ScrollTo ~ @@ -36,9 +37,9 @@ export class ScrollTo extends Component { containerBoundingRect() { const { containerRef } = this.props; - if (!containerRef || !containerRef.current || !containerRef.current.domEl) return {}; + if (!containerRef || !containerRef.current) return {}; - return containerRef.current.domEl.getBoundingClientRect(); + return containerRef.current.getBoundingClientRect(); } /** @@ -59,14 +60,14 @@ export class ScrollTo extends Component { } /** - * The container provided in the containersRef dome structure in which scrolling + * The container provided in the containersRef dom structure in which scrolling * should happen. */ scrollableContainer() { const { containerRef } = this.props; - if (!containerRef || !containerRef.current || !containerRef.current.domEl) return null; - return containerRef.current.domEl.getElementsByClassName('mirador-scrollto-scrollable')[0]; + if (!containerRef || !containerRef.current) return null; + return containerRef.current.getElementsByClassName('mirador-scrollto-scrollable')[0]; } /** @@ -104,16 +105,12 @@ export class ScrollTo extends Component { */ render() { const { - children, containerRef, offsetTop, scrollTo, ...otherProps + children, containerRef, offsetTop, scrollTo, nodeId, ...otherProps } = this.props; - if (!scrollTo) return children; + if (!scrollTo && isEmpty(otherProps)) return children; - return ( - <div ref={this.scrollToRef} {...otherProps}> - {children} - </div> - ); + return cloneElement(children, { ref: this.scrollToRef, ...otherProps }); } } @@ -123,6 +120,7 @@ ScrollTo.propTypes = { PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) }), ]).isRequired, + nodeId: PropTypes.string.isRequired, offsetTop: PropTypes.number, scrollTo: PropTypes.bool.isRequired, }; diff --git a/src/components/SearchHit.js b/src/components/SearchHit.js index 6314f2f6add3f3ad81cb003bbb345d91b18f1116..41a8be606d62b03431e3b9c2a283a8591a8a9b1c 100644 --- a/src/components/SearchHit.js +++ b/src/components/SearchHit.js @@ -1,15 +1,49 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import clsx from 'clsx'; -import Button from '@material-ui/core/Button'; -import ListItem from '@material-ui/core/ListItem'; -import ListItemText from '@material-ui/core/ListItemText'; -import Typography from '@material-ui/core/Typography'; -import Chip from '@material-ui/core/Chip'; +import Button from '@mui/material/Button'; +import ListItem from '@mui/material/ListItem'; +import ListItemText from '@mui/material/ListItemText'; +import Typography from '@mui/material/Typography'; +import Chip from '@mui/material/Chip'; +import { styled } from '@mui/material/styles'; import SanitizedHtml from '../containers/SanitizedHtml'; import TruncatedHit from '../lib/TruncatedHit'; import { ScrollTo } from './ScrollTo'; +const Root = styled(ListItem, { name: 'SearchHit', slot: 'root' })(({ ownerState, theme }) => ({ + '&.Mui-focused': { + '&:hover': { + ...(ownerState.windowSelected && { + backgroundColor: 'inherit', + }), + }, + ...(ownerState.windowSelected && { + backgroundColor: 'inherit', + }), + }, + paddingRight: theme.spacing(1), +})); + +const CanvasLabel = styled('h4', { name: 'SearchHit', slot: 'canvasLabel' })(({ theme }) => ({ + display: 'inline', + marginBottom: theme.spacing(1.5), +})); + +const Counter = styled(Chip, { name: 'SearchHit', slot: 'counter' })(({ ownerState, theme }) => ({ + // eslint-disable-next-line no-nested-ternary + backgroundColor: theme.palette.hitCounter.default, + ...(ownerState.windowSelected && { + backgroundColor: theme.palette.highlights.primary, + }), + ...(ownerState.adjacent && !ownerState.windowSelected && { + backgroundColor: theme.palette.highlights.secondary, + }), + height: 30, + marginRight: theme.spacing(1), + typography: 'subtitle2', + verticalAlign: 'inherit', +})); + /** */ export class SearchHit extends Component { /** */ @@ -78,7 +112,6 @@ export class SearchHit extends Component { annotation, annotationLabel, canvasLabel, - classes, companionWindowId, containerRef, hit, @@ -95,6 +128,25 @@ export class SearchHit extends Component { const truncatedHit = focused ? hit : hit && new TruncatedHit(hit, annotation); const truncated = hit && (truncatedHit.before !== hit.before || truncatedHit.after !== hit.after); const canvasLabelHtmlId = `${companionWindowId}-${index}`; + const ownerState = { + adjacent, focused, selected, windowSelected, + }; + + const header = ( + <> + <Counter + component="span" + ownerState={ownerState} + label={index + 1} + /> + <CanvasLabel id={canvasLabelHtmlId}> + {canvasLabel} + {annotationLabel && ( + <Typography component="span" sx={{ display: 'block', marginTop: 1 }}>{annotationLabel}</Typography> + )} + </CanvasLabel> + </> + ); return ( <ScrollTo @@ -102,51 +154,56 @@ export class SearchHit extends Component { offsetTop={96} // offset for the height of the form above scrollTo={windowSelected && !focused} > - <ListItem - className={clsx( - classes.listItem, - { - [classes.adjacent]: adjacent, - [classes.selected]: selected, - [classes.focused]: focused, - [classes.windowSelected]: windowSelected, - }, - )} + <Root + ownerState={ownerState} + className={windowSelected ? 'windowSelected' : ''} + divider button={!selected} component="li" onClick={this.handleClick} selected={selected} > - <ListItemText primaryTypographyProps={{ variant: 'body1' }}> - <Typography variant="subtitle2" className={classes.subtitle}> - <Chip component="span" label={index + 1} className={classes.hitCounter} /> - <span id={canvasLabelHtmlId}> - {canvasLabel} - </span> - </Typography> - {annotationLabel && ( - <Typography variant="subtitle2">{annotationLabel}</Typography> - )} - {hit && ( + <ListItemText + primary={header} + primaryTypographyProps={{ component: 'div', sx: { marginBottom: 1 }, variant: 'subtitle2' }} + secondaryTypographyProps={{ variant: 'body1' }} + secondary={( <> - <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.before} /> - {' '} - <strong> - <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.match} /> - </strong> - {' '} - <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.after} /> - {' '} - { truncated && !focused && ( - <Button className={classes.inlineButton} onClick={showDetails} color="secondary" size="small" aria-describedby={canvasLabelHtmlId}> - {t('more')} - </Button> + {hit && ( + <> + <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.before} /> + {' '} + <strong> + <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.match} /> + </strong> + {' '} + <SanitizedHtml ruleSet="iiif" htmlString={truncatedHit.after} /> + {' '} + {truncated && !focused && ( + <Button + sx={{ + '& span': { + lineHeight: '1.5em', + }, + margin: 0, + padding: 0, + textTransform: 'none', + }} + onClick={showDetails} + color="secondary" + size="small" + aria-describedby={canvasLabelHtmlId} + > + {t('more')} + </Button> + )} + </> )} + {!hit && annotation && <SanitizedHtml ruleSet="iiif" htmlString={annotation.chars} />} </> )} - {!hit && annotation && <SanitizedHtml ruleSet="iiif" htmlString={annotation.chars} />} - </ListItemText> - </ListItem> + /> + </Root> </ScrollTo> ); } @@ -162,7 +219,6 @@ SearchHit.propTypes = { annotationLabel: PropTypes.string, announcer: PropTypes.func, canvasLabel: PropTypes.string, - classes: PropTypes.objectOf(PropTypes.string), companionWindowId: PropTypes.string, containerRef: PropTypes.oneOfType([ PropTypes.func, @@ -191,7 +247,6 @@ SearchHit.defaultProps = { annotationLabel: undefined, announcer: undefined, canvasLabel: undefined, - classes: {}, companionWindowId: undefined, containerRef: undefined, focused: false, diff --git a/src/components/SearchPanel.js b/src/components/SearchPanel.js index 68063764646d16ba6669f11b6464736063b91d2f..42550dddf38e39fe4681302927f4ad07b09a379b 100644 --- a/src/components/SearchPanel.js +++ b/src/components/SearchPanel.js @@ -1,8 +1,8 @@ import { createRef, Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import Chip from '@material-ui/core/Chip'; -import Typography from '@material-ui/core/Typography'; +import Button from '@mui/material/Button'; +import Chip from '@mui/material/Chip'; +import Typography from '@mui/material/Typography'; import CompanionWindow from '../containers/CompanionWindow'; import SearchPanelControls from '../containers/SearchPanelControls'; import SearchResults from '../containers/SearchResults'; @@ -19,7 +19,6 @@ export class SearchPanel extends Component { /** */ render() { const { - classes, fetchSearch, windowId, id, @@ -40,7 +39,7 @@ export class SearchPanel extends Component { query && query !== '' && ( <Chip role="button" - className={classes.clearChip} + sx={{ marginLeft: 1 }} color="secondary" label={t('clearSearch')} onClick={removeSearch} @@ -65,8 +64,12 @@ export class SearchPanel extends Component { /> { fetchSearch && suggestedSearches && query === '' && suggestedSearches.map(search => ( - <Typography component="p" key={search} variant="body1"> - <Button className={classes.inlineButton} color="secondary" onClick={() => fetchSearch(`${searchService.id}?q=${search}`, search)}> + <Typography component="p" key={search} variant="body1" sx={{ margin: 2 }}> + <Button + variant="inlineText" + color="secondary" + onClick={() => fetchSearch(`${searchService.id}?q=${search}`, search)} + > {t('suggestSearch', { query: search })} </Button> </Typography> @@ -78,10 +81,6 @@ export class SearchPanel extends Component { } SearchPanel.propTypes = { - classes: PropTypes.shape({ - clearChip: PropTypes.string, - inlineButton: PropTypes.string, - }), fetchSearch: PropTypes.func, id: PropTypes.string.isRequired, query: PropTypes.string, @@ -95,7 +94,6 @@ SearchPanel.propTypes = { }; SearchPanel.defaultProps = { - classes: {}, fetchSearch: undefined, query: '', suggestedSearches: [], diff --git a/src/components/SearchPanelControls.js b/src/components/SearchPanelControls.js index f16939757a6831f843b1dfd7bcb2d470fb63d4ef..0ebdc7185a243bf533c1263525b3e5dd39996f07 100644 --- a/src/components/SearchPanelControls.js +++ b/src/components/SearchPanelControls.js @@ -1,15 +1,23 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import deburr from 'lodash/deburr'; import debounce from 'lodash/debounce'; import isObject from 'lodash/isObject'; -import Autocomplete from '@material-ui/lab/Autocomplete'; -import CircularProgress from '@material-ui/core/CircularProgress'; -import TextField from '@material-ui/core/TextField'; -import SearchIcon from '@material-ui/icons/SearchSharp'; +import Autocomplete from '@mui/material/Autocomplete'; +import CircularProgress from '@mui/material/CircularProgress'; +import TextField from '@mui/material/TextField'; +import InputAdornment from '@mui/material/InputAdornment'; +import SearchIcon from '@mui/icons-material/SearchSharp'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import SearchPanelNavigation from '../containers/SearchPanelNavigation'; +const StyledForm = styled('form', { name: 'SearchPanelControls', slot: 'form' })(({ theme }) => ({ + paddingBottom: theme.spacing(1), + paddingRight: theme.spacing(1.5), + width: '100%', +})); + /** Sometimes an autocomplete match can be a simple string, other times an object with a `match` property, this function abstracts that away */ const getMatch = (option) => (isObject(option) ? option.match : option); @@ -117,48 +125,62 @@ export class SearchPanelControls extends Component { /** */ render() { const { - classes, companionWindowId, searchIsFetching, t, windowId, + companionWindowId, searchIsFetching, t, windowId, } = this.props; const { search, suggestions } = this.state; const id = `search-${companionWindowId}`; return ( <> - <form aria-label={t('searchTitle')} onSubmit={this.submitSearch} className={classes.form}> + <StyledForm + aria-label={t('searchTitle')} + onSubmit={this.submitSearch} + > <Autocomplete id={id} inputValue={search} options={suggestions} getOptionLabel={getMatch} - getOptionSelected={(option, value) => ( + isOptionEqualToValue={(option, value) => ( deburr(getMatch(option).trim()).toLowerCase() - === deburr(getMatch(value).trim()).toLowerCase() + === deburr(getMatch(value).trim()).toLowerCase() )} noOptionsText="" onChange={this.selectItem} onInputChange={this.handleChange} freeSolo + disableClearable renderInput={params => ( <TextField {...params} label={t('searchInputLabel')} + variant="standard" InputProps={{ ...params.InputProps, endAdornment: ( - <div className={classes.endAdornment}> + <InputAdornment sx={{ position: 'relative' }} position="end"> <MiradorMenuButton aria-label={t('searchSubmitAria')} type="submit"> <SearchIcon /> </MiradorMenuButton> {Boolean(searchIsFetching) && ( - <CircularProgress className={classes.searchProgress} size={50} /> + <CircularProgress + sx={{ + left: '50%', + marginLeft: '-25px', + marginTop: '-25px', + position: 'absolute', + top: '50%', + }} + size={50} + /> )} - </div> + </InputAdornment> ), }} /> )} /> - </form> + </StyledForm> <SearchPanelNavigation windowId={windowId} companionWindowId={companionWindowId} /> </> ); @@ -169,7 +191,6 @@ SearchPanelControls.propTypes = { autocompleteService: PropTypes.shape({ id: PropTypes.string, }), - classes: PropTypes.objectOf(PropTypes.string), companionWindowId: PropTypes.string.isRequired, fetchSearch: PropTypes.func.isRequired, query: PropTypes.string, @@ -183,7 +204,6 @@ SearchPanelControls.propTypes = { SearchPanelControls.defaultProps = { autocompleteService: undefined, - classes: {}, query: '', t: key => key, }; diff --git a/src/components/SearchPanelNavigation.js b/src/components/SearchPanelNavigation.js index 72eb84e1fe181a6756d8a32644721fe28e4f7b40..3ddf2c49b5dc8631e09c7002aef30ef731c3f9e9 100644 --- a/src/components/SearchPanelNavigation.js +++ b/src/components/SearchPanelNavigation.js @@ -1,8 +1,8 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import ChevronLeftIcon from '@material-ui/icons/ChevronLeftSharp'; -import ChevronRightIcon from '@material-ui/icons/ChevronRightSharp'; -import Typography from '@material-ui/core/Typography'; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeftSharp'; +import ChevronRightIcon from '@mui/icons-material/ChevronRightSharp'; +import Typography from '@mui/material/Typography'; import MiradorMenuButton from '../containers/MiradorMenuButton'; /** @@ -42,7 +42,7 @@ export class SearchPanelNavigation extends Component { */ render() { const { - numTotal, searchHits, selectedContentSearchAnnotation, classes, t, direction, + numTotal, searchHits, selectedContentSearchAnnotation, t, direction, } = this.props; const iconStyle = direction === 'rtl' ? { transform: 'rotate(180deg)' } : {}; @@ -57,7 +57,7 @@ export class SearchPanelNavigation extends Component { if (searchHits.length === 0) return null; return ( - <Typography variant="body2" align="center" classes={classes}> + <Typography variant="body2" align="center"> <MiradorMenuButton aria-label={t('searchPreviousResult')} disabled={!this.hasPreviousResult(currentHitIndex)} @@ -80,7 +80,6 @@ export class SearchPanelNavigation extends Component { } } SearchPanelNavigation.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), direction: PropTypes.string.isRequired, numTotal: PropTypes.number, searchHits: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types @@ -93,7 +92,6 @@ SearchPanelNavigation.propTypes = { windowId: PropTypes.string.isRequired, // eslint-disable-line react/no-unused-prop-types }; SearchPanelNavigation.defaultProps = { - classes: {}, numTotal: undefined, searchHits: [], t: key => key, diff --git a/src/components/SearchResults.js b/src/components/SearchResults.js index c38e5c082a2f858ccce60237ddbd1a50c0384a2e..15263db3badcdef2e994cbd17f78073eb9c2c673 100644 --- a/src/components/SearchResults.js +++ b/src/components/SearchResults.js @@ -1,9 +1,9 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import List from '@material-ui/core/List'; -import Typography from '@material-ui/core/Typography'; -import BackIcon from '@material-ui/icons/ArrowBackSharp'; +import Button from '@mui/material/Button'; +import List from '@mui/material/List'; +import Typography from '@mui/material/Typography'; +import BackIcon from '@mui/icons-material/ArrowBackSharp'; import { announce } from '@react-aria/live-announcer'; import SearchHit from '../containers/SearchHit'; import { ScrollTo } from './ScrollTo'; @@ -80,7 +80,6 @@ export class SearchResults extends Component { /** */ render() { const { - classes, companionWindowId, containerRef, isFetching, @@ -106,14 +105,18 @@ export class SearchResults extends Component { <> { focused && ( <ScrollTo containerRef={containerRef} offsetTop={96} scrollTo> - <Button onClick={this.toggleFocus} className={classes.navigation} size="small"> + <Button onClick={this.toggleFocus} sx={{ textTransform: 'none' }} size="small"> <BackIcon /> {t('backToResults')} </Button> </ScrollTo> )} {noResultsState && ( - <Typography className={classes.noResults}> + <Typography sx={{ + padding: 2, + typography: 'h6', + }} + > {t('searchNoResults')} </Typography> )} @@ -122,7 +125,7 @@ export class SearchResults extends Component { </List> { nextSearch && ( <Button - className={classes.moreButton} + sx={{ width: '100%' }} color="secondary" onClick={() => fetchSearch(windowId, companionWindowId, nextSearch, query)} > @@ -137,7 +140,6 @@ export class SearchResults extends Component { } SearchResults.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), companionWindowId: PropTypes.string.isRequired, containerRef: PropTypes.oneOfType([ PropTypes.func, @@ -155,7 +157,6 @@ SearchResults.propTypes = { }; SearchResults.defaultProps = { - classes: {}, containerRef: undefined, isFetching: false, nextSearch: undefined, diff --git a/src/components/SelectCollection.js b/src/components/SelectCollection.js index cc7531393381731a4d8ff87ffc2684e4701d49f3..c51af8f531c25e41baf455a1eacb260a42eae8c2 100644 --- a/src/components/SelectCollection.js +++ b/src/components/SelectCollection.js @@ -1,9 +1,9 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import Grid from '@material-ui/core/Grid'; -import Typography from '@material-ui/core/Typography'; -import ListSharpIcon from '@material-ui/icons/ListSharp'; +import Button from '@mui/material/Button'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import ListSharpIcon from '@mui/icons-material/ListSharp'; /** * diff --git a/src/components/SidebarIndexItem.js b/src/components/SidebarIndexItem.js index d6b1a96668006e072062bd0cd0b1b0d0d1f9dc4e..11bb4af9c08171d10ee6a9a8be4c059a3a7711b1 100644 --- a/src/components/SidebarIndexItem.js +++ b/src/components/SidebarIndexItem.js @@ -1,19 +1,17 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; -import classNames from 'classnames'; +import Typography from '@mui/material/Typography'; /** */ export class SidebarIndexItem extends Component { /** */ render() { const { - classes, label, + label, } = this.props; return ( <Typography - className={classNames(classes.label)} variant="body1" > {label} @@ -23,6 +21,5 @@ export class SidebarIndexItem extends Component { } SidebarIndexItem.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, label: PropTypes.string.isRequired, }; diff --git a/src/components/SidebarIndexList.js b/src/components/SidebarIndexList.js index 5cb8f6d860df95a419309e2ac5b91e704a71adc3..d19394ef97ec83f0718e161c4171f37446829a0d 100644 --- a/src/components/SidebarIndexList.js +++ b/src/components/SidebarIndexList.js @@ -1,12 +1,21 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import MenuList from '@material-ui/core/MenuList'; -import MenuItem from '@material-ui/core/MenuItem'; +import MenuList from '@mui/material/MenuList'; +import MenuItem from '@mui/material/MenuItem'; +import { styled } from '@mui/material/styles'; import { ScrollTo } from './ScrollTo'; import MiradorCanvas from '../lib/MiradorCanvas'; import SidebarIndexItem from '../containers/SidebarIndexItem'; import SidebarIndexThumbnail from '../containers/SidebarIndexThumbnail'; +const StyledItem = styled(MenuItem, { name: 'SidebarIndexList', slot: 'item' })(({ theme }) => ({ + alignItems: 'flex-start', + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(1), + position: 'initial', + whiteSpace: 'normal', +})); + /** */ export class SidebarIndexList extends Component { /** @private */ @@ -23,7 +32,6 @@ export class SidebarIndexList extends Component { render() { const { canvases, - classes, containerRef, selectedCanvasIds, setCanvas, @@ -49,24 +57,22 @@ export class SidebarIndexList extends Component { const onClick = () => { setCanvas(windowId, canvas.id); }; // eslint-disable-line require-jsdoc, max-len return ( - <MenuItem - key={canvas.id} - className={classes.listItem} - alignItems="flex-start" - onClick={onClick} - button - component="li" + <ScrollTo + containerRef={containerRef} + key={`${canvas.id}-${variant}`} + offsetTop={96} // offset for the height of the form above selected={selectedCanvasIds.includes(canvas.id)} + scrollTo={selectedCanvasIds.includes(canvas.id)} > - <ScrollTo - containerRef={containerRef} - key={`${canvas.id}-${variant}`} - offsetTop={96} // offset for the height of the form above - scrollTo={selectedCanvasIds.includes(canvas.id)} + <StyledItem + key={canvas.id} + divider + onClick={onClick} + component="li" > <Item label={canvas.label} canvas={canvases[canvasIndex]} /> - </ScrollTo> - </MenuItem> + </StyledItem> + </ScrollTo> ); }) } @@ -77,7 +83,6 @@ export class SidebarIndexList extends Component { SidebarIndexList.propTypes = { canvases: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types - classes: PropTypes.objectOf(PropTypes.string).isRequired, containerRef: PropTypes.oneOf([PropTypes.func, PropTypes.object]).isRequired, selectedCanvasIds: PropTypes.arrayOf(PropTypes.string), setCanvas: PropTypes.func.isRequired, diff --git a/src/components/SidebarIndexTableOfContents.js b/src/components/SidebarIndexTableOfContents.js index e897a4e411a38738639f0ed6559945182229ee1f..296555e8c4ef73db40f56f37dea7211c62fa5492 100644 --- a/src/components/SidebarIndexTableOfContents.js +++ b/src/components/SidebarIndexTableOfContents.js @@ -1,12 +1,15 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import TreeView from '@material-ui/lab/TreeView'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import ChevronRightIcon from '@material-ui/icons/ChevronRight'; -import TreeItem from '@material-ui/lab/TreeItem'; -import clsx from 'clsx'; +import { alpha, styled } from '@mui/material/styles'; +import { TreeView } from '@mui/x-tree-view/TreeView'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ChevronRightIcon from '@mui/icons-material/ChevronRight'; +import { TreeItem } from '@mui/x-tree-view/TreeItem'; import { ScrollTo } from './ScrollTo'; +const StyledVisibleNode = styled('div')(() => ({ + +})); /** */ function getStartCanvasId(node) { const jsonld = node.data.__jsonld; // eslint-disable-line no-underscore-dangle @@ -65,20 +68,13 @@ export class SidebarIndexTableOfContents extends Component { } /** */ - handleKeyPressed(event, nodeId) { + handleNodeSelect(event, nodeId) { const { toggleNode } = this.props; - if (event.key === 'Enter') { - this.selectTreeItem(nodeId); - } - if (event.key === ' ' || event.key === 'Spacebar') { toggleNode(nodeId); } - } - /** */ - handleNodeSelect(_event, nodeId) { this.selectTreeItem(nodeId); } @@ -109,7 +105,7 @@ export class SidebarIndexTableOfContents extends Component { /** */ render() { const { - classes, treeStructure, visibleNodeIds, expandedNodeIds, containerRef, nodeIdToScrollTo, + treeStructure, visibleNodeIds, expandedNodeIds, containerRef, nodeIdToScrollTo, } = this.props; if (!treeStructure) { @@ -127,22 +123,45 @@ export class SidebarIndexTableOfContents extends Component { > <TreeItem nodeId={node.id} - classes={{ - content: classes.content, - group: classes.group, - label: classes.label, - root: classes.treeItemRoot, - selected: classes.selected, + sx={{ + '& .MuiTreeItem-content': { + alignItems: 'flex-start', + borderLeft: '1px solid transparent', + padding: '8px 16px 8px 0', + width: 'auto', + }, + '& .MuiTreeItem-group': { + borderLeft: '1px solid', + borderLeftColor: 'grey.300', + }, + '& .MuiTreeItem-iconContainer': { + paddingBlockStart: 0.5, + }, + '& .MuiTreeItem-label': { + paddingLeft: 0, + }, + '& .MuiTreeItem-root': { + '&:focus > .MuiTreeItem-content': { + backgroundColor: 'action.selected', + }, + '&:hover > .MuiTreeItem-content': { + backgroundColor: 'action.hover', + }, + '&:hover > .MuiTreeItem-content .MuiTreeItem-label, &:focus > .MuiTreeItem-content .MuiTreeItem-label, &.MuiTreeItem-selected > .MuiTreeItem-content .MuiTreeItem-label, &.MuiTreeItem-selected > .MuiTreeItem-content .MuiTreeItem-label:hover, &.MuiTreeItem-selected:focus > .MuiTreeItem-content .MuiTreeItem-label': { + backgroundColor: 'transparent', + }, + }, }} - onKeyDown={e => this.handleKeyPressed(e, node.id)} label={( - <div - className={clsx({ - [classes.visibleNode]: visibleNodeIds.indexOf(node.id) !== -1, + <StyledVisibleNode + sx={theme => ({ + backgroundColor: visibleNodeIds.indexOf(node.id) !== -1 + && alpha(theme.palette.highlights?.primary || theme.palette.action.selected, 0.35), + display: visibleNodeIds.indexOf(node.id) !== -1 && 'inline', })} > {node.label} - </div> + </StyledVisibleNode> )} > {Array.isArray(node.nodes) ? node.nodes.map((n) => renderTree(n)) : null} @@ -152,7 +171,7 @@ export class SidebarIndexTableOfContents extends Component { return ( <TreeView - className={classes.root} + sx={{ flexGrow: 1 }} defaultCollapseIcon={<ExpandMoreIcon color="action" />} defaultExpandIcon={<ChevronRightIcon color="action" />} defaultEndIcon={null} @@ -167,7 +186,6 @@ export class SidebarIndexTableOfContents extends Component { } SidebarIndexTableOfContents.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, containerRef: PropTypes.oneOfType([ PropTypes.func, PropTypes.shape({ current: PropTypes.instanceOf(Element) }), diff --git a/src/components/SidebarIndexThumbnail.js b/src/components/SidebarIndexThumbnail.js index 7abd1973d6051d0e71e4c18bbaf40bd3a4031669..05126d8de02dc6d6d184bea3d94bb838607e9335 100644 --- a/src/components/SidebarIndexThumbnail.js +++ b/src/components/SidebarIndexThumbnail.js @@ -1,7 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; -import classNames from 'classnames'; +import Typography from '@mui/material/Typography'; import IIIFThumbnail from '../containers/IIIFThumbnail'; /** */ @@ -9,7 +8,7 @@ export class SidebarIndexThumbnail extends Component { /** */ render() { const { - classes, canvas, height, label, width, + canvas, height, label, width, } = this.props; return ( @@ -18,15 +17,11 @@ export class SidebarIndexThumbnail extends Component { <IIIFThumbnail label={label} resource={canvas} - className={classNames(classes.clickable)} maxHeight={height} maxWidth={width} /> </div> - <Typography - className={classNames(classes.label)} - variant="body1" - > + <Typography> {label} </Typography> </> @@ -36,7 +31,6 @@ export class SidebarIndexThumbnail extends Component { SidebarIndexThumbnail.propTypes = { canvas: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types - classes: PropTypes.objectOf(PropTypes.string).isRequired, height: PropTypes.number, label: PropTypes.string.isRequired, width: PropTypes.number, diff --git a/src/components/ThumbnailCanvasGrouping.js b/src/components/ThumbnailCanvasGrouping.js index 6e8902339c361816aad67a35f270418eee0eed4b..f01d399260c4e032e300683e73c133cec53cc130 100644 --- a/src/components/ThumbnailCanvasGrouping.js +++ b/src/components/ThumbnailCanvasGrouping.js @@ -1,9 +1,17 @@ import { PureComponent } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import classNames from 'classnames'; import IIIFThumbnail from '../containers/IIIFThumbnail'; import ns from '../config/css-ns'; +const StyledCanvas = styled('div')(({ theme }) => ({ + boxSizing: 'border-box', + color: theme.palette.common.white, + cursor: 'pointer', + display: 'inline-block', + whiteSpace: 'nowrap', +})); /** */ export class ThumbnailCanvasGrouping extends PureComponent { /** */ @@ -31,7 +39,7 @@ export class ThumbnailCanvasGrouping extends PureComponent { /** */ render() { const { - index, style, data, classes, currentCanvasId, + index, style, data, currentCanvasId, } = this.props; const { canvasGroupings, position, height, @@ -52,24 +60,27 @@ export class ThumbnailCanvasGrouping extends PureComponent { role="gridcell" aria-colindex={index + 1} > - <div + <StyledCanvas role="button" data-canvas-id={currentGroupings[0].id} data-canvas-index={currentGroupings[0].index} onKeyUp={this.setCanvas} onClick={this.setCanvas} tabIndex={-1} - style={{ + sx={theme => ({ + '&:hover': { + outline: `9px solid ${theme.palette.action.hover}`, + outlineOffset: '-2px', + }, height: (position === 'far-right') ? 'auto' : `${height - SPACING}px`, + outline: currentGroupings.map(canvas => canvas.id).includes(currentCanvasId) ? `2px solid ${theme.palette.primary.main}` : 0, + ...(currentGroupings.map(canvas => canvas.id).includes(currentCanvasId) && { + outlineOffset: '3px', + }), width: (position === 'far-bottom') ? 'auto' : `${style.width}px`, - }} + })} className={classNames( ns(['thumbnail-nav-canvas', `thumbnail-nav-canvas-${index}`, this.currentCanvasClass(currentGroupings.map(canvas => canvas.index))]), - classes.canvas, - { - [classes.currentCanvas]: currentGroupings - .map(canvas => canvas.id).includes(currentCanvasId), - }, )} > {currentGroupings.map((canvas, i) => ( @@ -81,14 +92,13 @@ export class ThumbnailCanvasGrouping extends PureComponent { variant="inside" /> ))} - </div> + </StyledCanvas> </div> ); } } ThumbnailCanvasGrouping.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, currentCanvasId: PropTypes.string.isRequired, data: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types index: PropTypes.number.isRequired, diff --git a/src/components/ThumbnailNavigation.js b/src/components/ThumbnailNavigation.js index 59219d4446528d1b66c3cd300f5e137e1b1c20b3..1a4ce20cf69e8e7c9daf22151cc1c8aa4cadab01 100644 --- a/src/components/ThumbnailNavigation.js +++ b/src/components/ThumbnailNavigation.js @@ -1,6 +1,6 @@ import { createRef, Component } from 'react'; import PropTypes from 'prop-types'; -import Paper from '@material-ui/core/Paper'; +import Paper from '@mui/material/Paper'; import AutoSizer from 'react-virtualized-auto-sizer'; import { VariableSizeList as List } from 'react-window'; import classNames from 'classnames'; @@ -176,7 +176,6 @@ export class ThumbnailNavigation extends Component { const { t, canvasGroupings, - classes, position, thumbnailNavigation, viewingDirection, @@ -196,8 +195,13 @@ export class ThumbnailNavigation extends Component { <Paper className={classNames( ns('thumb-navigation'), - classes.thumbNavigation, )} + sx={{ + '&:focus': { + boxShadow: 0, + outline: 0, + }, + }} aria-label={t('thumbnailNavigation')} square elevation={0} @@ -235,7 +239,6 @@ export class ThumbnailNavigation extends Component { ThumbnailNavigation.propTypes = { canvasGroupings: PropTypes.array.isRequired, // eslint-disable-line react/forbid-prop-types canvasIndex: PropTypes.number.isRequired, - classes: PropTypes.objectOf(PropTypes.string).isRequired, hasNextCanvas: PropTypes.bool, hasPreviousCanvas: PropTypes.bool, position: PropTypes.string.isRequired, diff --git a/src/components/VideoViewer.js b/src/components/VideoViewer.js index e8a3d617df6b3f516fbacb7abcfd7b05c2894a78..a133aceed3b06116bd430d162afcbd70d6d8fa3d 100644 --- a/src/components/VideoViewer.js +++ b/src/components/VideoViewer.js @@ -1,5 +1,17 @@ import { Component, Fragment } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; + +const StyledContainer = styled('div')(() => ({ + alignItems: 'center', + display: 'flex', + width: '100%', +})); + +const StyledVideo = styled('video')(() => ({ + maxHeight: '100%', + width: '100%', +})); /** */ export class VideoViewer extends Component { @@ -7,11 +19,11 @@ export class VideoViewer extends Component { /** */ render() { const { - captions, classes, videoOptions, videoResources, + captions, videoOptions, videoResources, } = this.props; return ( - <div className={classes.container}> - <video className={classes.video} {...videoOptions}> + <StyledContainer> + <StyledVideo {...videoOptions}> {videoResources.map(video => ( <Fragment key={video.id}> <source src={video.id} type={video.getFormat()} /> @@ -22,8 +34,8 @@ export class VideoViewer extends Component { <track src={caption.id} label={caption.getDefaultLabel()} srcLang={caption.getProperty('language')} /> </Fragment> ))} - </video> - </div> + </StyledVideo> + </StyledContainer> ); } /* eslint-enable jsx-a11y/media-has-caption */ @@ -31,7 +43,6 @@ export class VideoViewer extends Component { VideoViewer.propTypes = { captions: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types - classes: PropTypes.objectOf(PropTypes.string).isRequired, videoOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types videoResources: PropTypes.arrayOf(PropTypes.object), // eslint-disable-line react/forbid-prop-types }; diff --git a/src/components/ViewerInfo.js b/src/components/ViewerInfo.js index 595ed37d0f7c94bbef408772c3e39c419a14a481..1958bea5e714189630e6ecc586dcaa0f7b5b646b 100644 --- a/src/components/ViewerInfo.js +++ b/src/components/ViewerInfo.js @@ -1,9 +1,19 @@ import { Component } from 'react'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import classNames from 'classnames'; import ns from '../config/css-ns'; +const StyledOsdInfo = styled('div')(() => ({ + overflow: 'hidden', + paddingBottom: 0.5, + textOverflow: 'ellipsis', + unicodeBidi: 'plaintext', + whiteSpace: 'nowrap', + width: '100%', +})); + /** * */ @@ -14,19 +24,18 @@ export class ViewerInfo extends Component { canvasCount, canvasIndex, canvasLabel, - classes, t, } = this.props; return ( - <div className={classNames(ns('osd-info'), classes.osdInfo)}> + <StyledOsdInfo className={classNames(ns('osd-info'))}> <Typography display="inline" variant="caption" className={ns('canvas-count')}> { t('pagination', { current: canvasIndex + 1, total: canvasCount }) } </Typography> <Typography display="inline" variant="caption" className={ns('canvas-label')}> {canvasLabel && ` • ${canvasLabel}`} </Typography> - </div> + </StyledOsdInfo> ); } } @@ -40,6 +49,5 @@ ViewerInfo.propTypes = { canvasCount: PropTypes.number.isRequired, canvasIndex: PropTypes.number.isRequired, canvasLabel: PropTypes.string, - classes: PropTypes.objectOf(PropTypes.string).isRequired, t: PropTypes.func, }; diff --git a/src/components/ViewerNavigation.js b/src/components/ViewerNavigation.js index 0253cefa5278df9db46b91480be5dac6c12362e2..e22b4ce4fac1b116b72654e80acd030042850e2e 100644 --- a/src/components/ViewerNavigation.js +++ b/src/components/ViewerNavigation.js @@ -1,5 +1,5 @@ import { Component } from 'react'; -import NavigationIcon from '@material-ui/icons/PlayCircleOutlineSharp'; +import NavigationIcon from '@mui/icons-material/PlayCircleOutlineSharp'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import MiradorMenuButton from '../containers/MiradorMenuButton'; @@ -14,7 +14,7 @@ export class ViewerNavigation extends Component { render() { const { hasNextCanvas, hasPreviousCanvas, setNextCanvas, setPreviousCanvas, t, - classes, viewingDirection, + viewingDirection, } = this.props; let htmlDir = 'ltr'; @@ -41,7 +41,7 @@ export class ViewerNavigation extends Component { return ( <div - className={classNames(ns('osd-navigation'), classes.osdNavigation)} + className={classNames(ns('osd-navigation'))} dir={htmlDir} > <MiradorMenuButton @@ -66,7 +66,6 @@ export class ViewerNavigation extends Component { } ViewerNavigation.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, hasNextCanvas: PropTypes.bool, hasPreviousCanvas: PropTypes.bool, setNextCanvas: PropTypes.func, diff --git a/src/components/Window.js b/src/components/Window.js index 6033c76919ba2e2364984d351fa2267eb4d93447..4610fb6cf2d4116f9edd1901bf6988a88b9c5b05 100644 --- a/src/components/Window.js +++ b/src/components/Window.js @@ -1,7 +1,7 @@ -import { Component } from 'react'; +import { Component, useContext } from 'react'; import PropTypes from 'prop-types'; -import cn from 'classnames'; -import Paper from '@material-ui/core/Paper'; +import { styled } from '@mui/material/styles'; +import Paper from '@mui/material/Paper'; import { MosaicWindowContext } from 'react-mosaic-component/lib/contextTypes'; import ns from '../config/css-ns'; import WindowTopBar from '../containers/WindowTopBar'; @@ -12,6 +12,68 @@ import ErrorContent from '../containers/ErrorContent'; import IIIFAuthentication from '../containers/IIIFAuthentication'; import { PluginHook } from './PluginHook'; +const rowMixin = { + display: 'flex', + flex: '1', + flexDirection: 'row', + minHeight: 0, +}; + +const columnMixin = { + display: 'flex', + flex: '1', + flexDirection: 'column', + minHeight: 0, +}; + +const Root = styled(Paper, { name: 'Window', slot: 'root' })(({ ownerState, theme }) => ({ + ...columnMixin, + backgroundColor: theme.palette.shades?.dark, + borderRadius: 0, + height: '100%', + overflow: 'hidden', + width: '100%', + ...(ownerState?.maximized && { + left: 0, + position: 'absolute', + top: 0, + zIndex: theme.zIndex.modal - 1, + }), +})); + +const ContentRow = styled('div', { name: 'Window', slot: 'row' })(() => ({ + ...rowMixin, +})); + +const ContentColumn = styled('div', { name: 'Window', slot: 'column' })(() => ({ + ...columnMixin, +})); + +const StyledPrimaryWindow = styled(PrimaryWindow, { name: 'Window', slot: 'primary' })(() => ({ + ...rowMixin, + height: '300px', + position: 'relative', +})); + +const StyledCompanionAreaBottom = styled(CompanionArea, { name: 'Window', slot: 'bottom' })(() => ({ + ...rowMixin, + flex: '0', + flexBasis: 'auto', +})); + +const StyledCompanionAreaRight = styled('div', { name: 'Window', slot: 'right' })(() => ({ + ...rowMixin, + flex: '0 1 auto', +})); + +/** Window title bar wrapper for drag controls in the mosaic view */ +const DraggableNavBar = ({ children, ...props }) => { + const { mosaicWindowActions } = useContext(MosaicWindowContext); + return mosaicWindowActions.connectDragSource( + <nav {...props}>{children}</nav>, + ); +}; + /** * Represents a Window in the mirador workspace * @param {object} window @@ -29,40 +91,13 @@ export class Window extends Component { return { error, hasError: true }; } - /** - * wrappedTopBar - will conditionally wrap a WindowTopBar for needed - * additional functionality based on workspace type - */ - wrappedTopBar() { - const { - windowId, workspaceType, windowDraggable, - } = this.props; - - const topBar = ( - <div> - <WindowTopBar - windowId={windowId} - windowDraggable={windowDraggable} - /> - <IIIFAuthentication windowId={windowId} /> - </div> - ); - if (workspaceType === 'mosaic' && windowDraggable) { - const { mosaicWindowActions } = this.context; - return mosaicWindowActions.connectDragSource( - topBar, - ); - } - return topBar; - } - /** * Renders things */ render() { const { - focusWindow, label, isFetching, maximized, sideBarOpen, - view, windowId, classes, t, + focusWindow, label, isFetching, sideBarOpen, + view, windowDraggable, windowId, workspaceType, t, manifestError, } = this.props; @@ -77,44 +112,40 @@ export class Window extends Component { } return ( - <Paper + <Root onFocus={focusWindow} + ownerState={this.props} component="section" elevation={1} id={windowId} - className={ - cn( - classes.window, - ns('window'), - maximized ? classes.maximized : null, - ) -} + className={ns('window')} aria-label={t('window', { label })} > - {this.wrappedTopBar()} + <WindowTopBar + component={workspaceType === 'mosaic' && windowDraggable ? DraggableNavBar : undefined} + windowId={windowId} + windowDraggable={windowDraggable} + /> + <IIIFAuthentication windowId={windowId} /> { manifestError && <ErrorContent error={{ stack: manifestError }} windowId={windowId} /> } - <div className={classes.middle}> - <div className={classes.middleLeft}> - <div className={classes.primaryWindow}> - <PrimaryWindow - view={view} - windowId={windowId} - isFetching={isFetching} - sideBarOpen={sideBarOpen} - /> - </div> - <div className={classes.companionAreaBottom}> - <CompanionArea windowId={windowId} position="bottom" /> - </div> - </div> - <div className={classes.companionAreaRight}> + <ContentRow> + <ContentColumn> + <StyledPrimaryWindow + view={view} + windowId={windowId} + isFetching={isFetching} + sideBarOpen={sideBarOpen} + /> + <StyledCompanionAreaBottom windowId={windowId} position="bottom" /> + </ContentColumn> + <StyledCompanionAreaRight> <CompanionArea windowId={windowId} position="right" /> <CompanionArea windowId={windowId} position="far-right" /> - </div> - </div> + </StyledCompanionAreaRight> + </ContentRow> <CompanionArea windowId={windowId} position="far-bottom" /> <PluginHook {...this.props} /> - </Paper> + </Root> ); } } @@ -122,7 +153,6 @@ export class Window extends Component { Window.contextType = MosaicWindowContext; Window.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), focusWindow: PropTypes.func, isFetching: PropTypes.bool, label: PropTypes.string, @@ -137,7 +167,6 @@ Window.propTypes = { }; Window.defaultProps = { - classes: {}, focusWindow: () => {}, isFetching: false, label: null, diff --git a/src/components/WindowAuthenticationBar.js b/src/components/WindowAuthenticationBar.js index bbfbd5a5388cc7889297364298b394c23edcdb96..05d2a365f69dd75d1c3bd331d9c8d8a9079e376f 100644 --- a/src/components/WindowAuthenticationBar.js +++ b/src/components/WindowAuthenticationBar.js @@ -1,14 +1,26 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Button from '@material-ui/core/Button'; -import Paper from '@material-ui/core/Paper'; -import Collapse from '@material-ui/core/Collapse'; -import DialogActions from '@material-ui/core/DialogActions'; -import Typography from '@material-ui/core/Typography'; -import LockIcon from '@material-ui/icons/LockSharp'; +import { alpha, styled } from '@mui/material/styles'; +import Button from '@mui/material/Button'; +import Paper from '@mui/material/Paper'; +import Collapse from '@mui/material/Collapse'; +import DialogActions from '@mui/material/DialogActions'; +import Typography from '@mui/material/Typography'; +import LockIcon from '@mui/icons-material/LockSharp'; import SanitizedHtml from '../containers/SanitizedHtml'; import { PluginHook } from './PluginHook'; +const StyledTopBar = styled('div')(({ theme }) => ({ + '&:hover': { + backgroundColor: theme.palette.secondary.main, + }, + alignItems: 'center', + display: 'flex', +})); + +const StyledFauxButton = styled('span')(({ theme }) => ({ + marginLeft: theme.spacing(2.5), +})); /** */ export class WindowAuthenticationBar extends Component { /** */ @@ -35,7 +47,7 @@ export class WindowAuthenticationBar extends Component { /** */ render() { const { - classes, confirmButton, continueLabel, + confirmButton, continueLabel, header, description, icon, label, t, ruleSet, hasLogoutService, status, ConfirmProps, } = this.props; @@ -45,47 +57,90 @@ export class WindowAuthenticationBar extends Component { const { open } = this.state; const button = ( - <Button onClick={this.onSubmit} className={classes.buttonInvert} color="secondary" {...ConfirmProps}> + <Button + onClick={this.onSubmit} + color="secondary" + sx={(theme) => ({ + '&:hover': { + backgroundColor: alpha(theme.palette.secondary.contrastText, 1 - theme.palette.action.hoverOpacity), + }, + backgroundColor: theme.palette.secondary.contrastText, + })} + {...ConfirmProps} + > {confirmButton || t('login')} </Button> ); if (!description && !header) { return ( - <Paper square elevation={4} color="secondary" classes={{ root: classes.paper }}> - <div className={classes.topBar}> - { icon || <LockIcon className={classes.icon} /> } - <Typography className={classes.label} component="h3" variant="body1" color="inherit"> + <Paper + square + elevation={4} + color="secondary" + > + <StyledTopBar> + { icon || ( + <LockIcon sx={{ marginInlineEnd: 1.5 }} /> + ) } + <Typography component="h3" variant="body1" color="inherit"> { ruleSet ? <SanitizedHtml htmlString={label} ruleSet={ruleSet} /> : label } </Typography> <PluginHook {...this.props} /> { button } - </div> + </StyledTopBar> </Paper> ); } return ( - <Paper square elevation={4} color="secondary" classes={{ root: classes.paper }}> - <Button fullWidth className={classes.topBar} onClick={() => this.setOpen(true)} component="div" color="inherit"> - { icon || <LockIcon className={classes.icon} /> } - <Typography className={classes.label} component="h3" variant="body1" color="inherit"> + <Paper + square + elevation={4} + color="secondary" + > + <Button + fullWidth + onClick={() => this.setOpen(true)} + component="div" + color="inherit" + sx={(theme) => ({ + '&:hover': { + backgroundColor: theme.palette.secondary.main, + }, + backgroundColor: theme.palette.secondary.main, + borderRadius: 0, + color: theme.palette.secondary.contrastText, + justifyContent: 'start', + textTransform: 'none', + })} + > + { icon || ( + <LockIcon sx={{ marginInlineEnd: 1.5 }} /> + ) } + <Typography sx={{ paddingBlockEnd: 1, paddingBlockStart: 1 }} component="h3" variant="body1" color="inherit"> { ruleSet ? <SanitizedHtml htmlString={label} ruleSet={ruleSet} /> : label } </Typography> <PluginHook {...this.props} /> - <span className={classes.fauxButton}> + <StyledFauxButton> { !open && ( <Typography variant="button" color="inherit"> { continueLabel || t('continue') } </Typography> )} - </span> + </StyledFauxButton> </Button> <Collapse + sx={(theme) => ({ + backgroundColor: theme.palette.secondary.main, + color: theme.palette.secondary.contrastText, + paddingInlineEnd: theme.spacing(1), + paddingInlineStart: theme.spacing(1), + })} in={open} onClose={() => this.setOpen(false)} > - <Typography variant="body1" color="inherit" className={classes.expanded}> + <Typography variant="body1" color="inherit"> { ruleSet ? <SanitizedHtml htmlString={header} ruleSet={ruleSet} /> : header } { header && description ? ': ' : '' } { ruleSet ? <SanitizedHtml htmlString={description} ruleSet={ruleSet} /> : description } @@ -104,7 +159,6 @@ export class WindowAuthenticationBar extends Component { } WindowAuthenticationBar.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, confirmButton: PropTypes.string, ConfirmProps: PropTypes.object, // eslint-disable-line react/forbid-prop-types continueLabel: PropTypes.string, diff --git a/src/components/WindowCanvasNavigationControls.js b/src/components/WindowCanvasNavigationControls.js index eaee4dc152d82477faf268ccb1f636010fda3647..df9e3ae73788c18d6993a394211704d329d8edda 100644 --- a/src/components/WindowCanvasNavigationControls.js +++ b/src/components/WindowCanvasNavigationControls.js @@ -1,14 +1,33 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { alpha, styled } from '@mui/material/styles'; import classNames from 'classnames'; -import Paper from '@material-ui/core/Paper'; -import Typography from '@material-ui/core/Typography'; +import Paper from '@mui/material/Paper'; +import Stack from '@mui/material/Stack'; +import Divider from '@mui/material/Divider'; +import Typography from '@mui/material/Typography'; +import { visuallyHidden } from '@mui/utils'; import ZoomControls from '../containers/ZoomControls'; import ViewerInfo from '../containers/ViewerInfo'; import ViewerNavigation from '../containers/ViewerNavigation'; import ns from '../config/css-ns'; import { PluginHook } from './PluginHook'; +const Root = styled(Paper, { name: 'WindowCanvasNavigationControls', slot: 'root' })(({ theme }) => ({ + alignItems: 'center', + backgroundColor: alpha(theme.palette.background.paper, 0.5), + bottom: 0, + cursor: 'default', + display: 'flex', + flexDirection: 'column', + flexWrap: 'wrap', + justifyContent: 'center', + position: 'absolute', + textAlign: 'center', + width: '100%', + zIndex: 50, +})); + /** * Represents the viewer controls in the mirador workspace. */ @@ -25,41 +44,40 @@ export class WindowCanvasNavigationControls extends Component { /** */ render() { const { - classes, visible, windowId, zoomToWorld, + showZoomControls, visible, windowId, zoomToWorld, } = this.props; - if (!visible) return (<Typography variant="srOnly" component="div"><ViewerInfo windowId={windowId} /></Typography>); + if (!visible) return (<Typography style={visuallyHidden} component="div"><ViewerInfo windowId={windowId} /></Typography>); return ( - <Paper + <Root square className={ classNames( - classes.controls, ns('canvas-nav'), - classes.canvasNav, this.canvasNavControlsAreStacked() ? ns('canvas-nav-stacked') : null, - this.canvasNavControlsAreStacked() ? classes.canvasNavStacked : null, ) -} + } elevation={0} > - <ZoomControls - displayDivider={!this.canvasNavControlsAreStacked()} - windowId={windowId} - zoomToWorld={zoomToWorld} - /> - <ViewerNavigation windowId={windowId} /> + <Stack + direction={this.canvasNavControlsAreStacked() ? 'column' : 'row'} + divider={<Divider orientation={this.canvasNavControlsAreStacked() ? 'horizontal' : 'vertical'} variant="middle" flexItem />} + spacing={0} + > + { showZoomControls && <ZoomControls windowId={windowId} zoomToWorld={zoomToWorld} /> } + <ViewerNavigation windowId={windowId} /> + </Stack> <ViewerInfo windowId={windowId} /> <PluginHook {...this.props} /> - </Paper> + </Root> ); } } WindowCanvasNavigationControls.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), + showZoomControls: PropTypes.bool, size: PropTypes.shape({ width: PropTypes.number }).isRequired, visible: PropTypes.bool, windowId: PropTypes.string.isRequired, @@ -67,6 +85,6 @@ WindowCanvasNavigationControls.propTypes = { }; WindowCanvasNavigationControls.defaultProps = { - classes: {}, + showZoomControls: false, visible: true, }; diff --git a/src/components/WindowList.js b/src/components/WindowList.js index 8ed83f402ca09c3a7c495b3ae1094229e9ff85ec..2db96ba42b70f6672147013dfadac62309f3ad2f 100644 --- a/src/components/WindowList.js +++ b/src/components/WindowList.js @@ -1,8 +1,8 @@ import { Component } from 'react'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import ListItemText from '@material-ui/core/ListItemText'; -import ListSubheader from '@material-ui/core/ListSubheader'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import ListItemText from '@mui/material/ListItemText'; +import ListSubheader from '@mui/material/ListSubheader'; import PropTypes from 'prop-types'; /** diff --git a/src/components/WindowListButton.js b/src/components/WindowListButton.js index 68a9bd2ac974089ddfb88609ab29b27bb4cf5954..758e227ac71202a71e2da63f7474852d608de962 100644 --- a/src/components/WindowListButton.js +++ b/src/components/WindowListButton.js @@ -1,7 +1,6 @@ import { Component } from 'react'; -import BookmarksIcon from '@material-ui/icons/BookmarksSharp'; +import BookmarksIcon from '@mui/icons-material/BookmarksSharp'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; import WindowList from '../containers/WindowList'; import MiradorMenuButton from '../containers/MiradorMenuButton'; @@ -33,7 +32,7 @@ export class WindowListButton extends Component { */ render() { const { - classes, disabled, t, windowCount, + disabled, t, windowCount, } = this.props; const { windowListAnchor } = this.state; @@ -43,24 +42,29 @@ export class WindowListButton extends Component { aria-haspopup="true" aria-label={t('listAllOpenWindows')} aria-owns={windowListAnchor ? 'window-list' : null} - className={ - classNames(classes.ctrlBtn, (windowListAnchor ? classes.ctrlBtnSelected : null)) - } + selected={Boolean(windowListAnchor)} disabled={disabled} badge - BadgeProps={{ badgeContent: windowCount, classes: { badge: classes.badge } }} - onClick={e => this.handleOpen(e)} + BadgeProps={{ + badgeContent: windowCount, + sx: { + '.MuiBadge-badge': { + paddingLeft: 1.5, + }, + }, + }} + onClick={(e) => this.handleOpen(e)} > <BookmarksIcon /> </MiradorMenuButton> {Boolean(windowListAnchor) && ( - <WindowList - anchorEl={windowListAnchor} - id="window-list" - open={Boolean(windowListAnchor)} - handleClose={this.handleClose} - /> + <WindowList + anchorEl={windowListAnchor} + id="window-list" + open={Boolean(windowListAnchor)} + handleClose={this.handleClose} + /> )} </> ); @@ -68,12 +72,10 @@ export class WindowListButton extends Component { } WindowListButton.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), disabled: PropTypes.bool, t: PropTypes.func.isRequired, windowCount: PropTypes.number.isRequired, }; WindowListButton.defaultProps = { - classes: {}, disabled: false, }; diff --git a/src/components/WindowSideBar.js b/src/components/WindowSideBar.js index c6fbc6ef3f75ecd493fc2a36b9df35fe234539f6..7750eeb294d9e3e9e42651fbd7870da8a999e37b 100644 --- a/src/components/WindowSideBar.js +++ b/src/components/WindowSideBar.js @@ -1,9 +1,20 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import Drawer from '@material-ui/core/Drawer'; +import { styled } from '@mui/material/styles'; +import Drawer from '@mui/material/Drawer'; import WindowSideBarButtons from '../containers/WindowSideBarButtons'; +const Root = styled(Drawer, { name: 'WindowSideBar', slot: 'root' })(({ theme }) => ({ + flexShrink: 0, + order: -1000, + zIndex: theme.zIndex.appBar - 1, +})); + +const Nav = styled('nav', { name: 'WindowSideBar', slot: 'nav' })({ + position: 'relative !important', + width: 48, +}); + /** * WindowSideBar */ @@ -18,27 +29,26 @@ export class WindowSideBar extends Component { } = this.props; return ( - <Drawer + <Root variant="persistent" - className={classNames(classes.drawer)} - classes={{ paper: classNames(classes.paper) }} + className={classes.drawer} anchor={direction === 'rtl' ? 'right' : 'left'} PaperProps={{ 'aria-label': t('sidebarPanelsNavigation'), - component: 'nav', - style: { height: '100%', position: 'relative' }, + component: Nav, + variant: 'outlined', }} SlideProps={{ direction: direction === 'rtl' ? 'left' : 'right', mountOnEnter: true, unmountOnExit: true }} open={sideBarOpen} > <WindowSideBarButtons windowId={windowId} /> - </Drawer> + </Root> ); } } WindowSideBar.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, + classes: PropTypes.objectOf(PropTypes.string), direction: PropTypes.string.isRequired, sideBarOpen: PropTypes.bool, t: PropTypes.func.isRequired, @@ -46,5 +56,6 @@ WindowSideBar.propTypes = { }; WindowSideBar.defaultProps = { + classes: {}, sideBarOpen: false, }; diff --git a/src/components/WindowSideBarAnnotationsPanel.js b/src/components/WindowSideBarAnnotationsPanel.js index f5182e6443dcfc8430aba05f808200e6ac83f25b..d2e6f781d0c3b160395df19809fa5f2ea39dc846 100644 --- a/src/components/WindowSideBarAnnotationsPanel.js +++ b/src/components/WindowSideBarAnnotationsPanel.js @@ -1,9 +1,10 @@ import { createRef, Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; +import Typography from '@mui/material/Typography'; import AnnotationSettings from '../containers/AnnotationSettings'; import CanvasAnnotations from '../containers/CanvasAnnotations'; import CompanionWindow from '../containers/CompanionWindow'; +import { CompanionWindowSection } from './CompanionWindowSection'; import ns from '../config/css-ns'; /** @@ -22,7 +23,7 @@ export class WindowSideBarAnnotationsPanel extends Component { */ render() { const { - annotationCount, classes, canvasIds, t, windowId, id, + annotationCount, canvasIds, t, windowId, id, } = this.props; return ( <CompanionWindow @@ -31,12 +32,11 @@ export class WindowSideBarAnnotationsPanel extends Component { windowId={windowId} id={id} ref={this.containerRef} - otherRef={this.containerRef} titleControls={<AnnotationSettings windowId={windowId} />} > - <div className={classes.section}> + <CompanionWindowSection> <Typography component="p" variant="subtitle2">{t('showingNumAnnotations', { count: annotationCount, number: annotationCount })}</Typography> - </div> + </CompanionWindowSection> {canvasIds.map((canvasId, index) => ( <CanvasAnnotations @@ -56,7 +56,6 @@ export class WindowSideBarAnnotationsPanel extends Component { WindowSideBarAnnotationsPanel.propTypes = { annotationCount: PropTypes.number.isRequired, canvasIds: PropTypes.arrayOf(PropTypes.string), - classes: PropTypes.objectOf(PropTypes.string).isRequired, id: PropTypes.string.isRequired, t: PropTypes.func, windowId: PropTypes.string.isRequired, diff --git a/src/components/WindowSideBarButtons.js b/src/components/WindowSideBarButtons.js index e4b0a2f6cc7cd6d1128d40e392b7d3d59a49eff9..bba780d3d62a01e7da62d25287bc628aa4b7af42 100644 --- a/src/components/WindowSideBarButtons.js +++ b/src/components/WindowSideBarButtons.js @@ -1,21 +1,62 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Badge from '@material-ui/core/Badge'; -import Tabs from '@material-ui/core/Tabs'; -import Tab from '@material-ui/core/Tab'; -import Tooltip from '@material-ui/core/Tooltip'; -import InfoIcon from '@material-ui/icons/InfoSharp'; -import AnnotationIcon from '@material-ui/icons/CommentSharp'; -import AttributionIcon from '@material-ui/icons/CopyrightSharp'; -import LayersIcon from '@material-ui/icons/LayersSharp'; -import SearchIcon from '@material-ui/icons/SearchSharp'; +import { styled } from '@mui/material/styles'; +import Badge from '@mui/material/Badge'; +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; +import Tooltip from '@mui/material/Tooltip'; +import InfoIcon from '@mui/icons-material/InfoSharp'; +import AnnotationIcon from '@mui/icons-material/CommentSharp'; +import AttributionIcon from '@mui/icons-material/CopyrightSharp'; +import LayersIcon from '@mui/icons-material/LayersSharp'; +import SearchIcon from '@mui/icons-material/SearchSharp'; import CanvasIndexIcon from './icons/CanvasIndexIcon'; +const Root = styled(Tabs, { name: 'WindowSideBarButtons', slot: 'root' })({ + '& .MuiTabs-flexContainer': { + flexDirection: 'column', + }, + '&.MuiTabs-indicator': { + display: 'none', + }, +}); + +const StyledTabButton = styled(Tab, { name: 'WindowSideBarButtons', slot: 'button' })(({ theme }) => ({ + '&.Mui-selected': { + borderRight: '2px solid', + borderRightColor: theme.palette.primary.main, + }, + '&.MuiTab-root': { + '&:active': { + backgroundColor: theme.palette.action.active, + }, + '&:focus': { + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + backgroundColor: theme.palette.action.hover, + textDecoration: 'none', + // Reset on touch devices, it doesn't add specificity + }, + '&:hover': { + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + backgroundColor: theme.palette.action.hover, + textDecoration: 'none', + // Reset on touch devices, it doesn't add specificity + }, + borderRight: '2px solid transparent', + minWidth: 'auto', + }, + fill: 'currentcolor', +})); + /** */ function TabButton({ t, value, ...tabProps }) { return ( <Tooltip title={t('openCompanionWindow', { context: value })}> - <Tab + <StyledTabButton {...tabProps} value={value} aria-label={ @@ -59,7 +100,6 @@ export class WindowSideBarButtons extends Component { */ render() { const { - classes, hasAnnotations, hasAnyAnnotations, hasAnyLayers, @@ -73,8 +113,7 @@ export class WindowSideBarButtons extends Component { } = this.props; return ( - <Tabs - classes={{ flexContainer: classes.tabsFlexContainer, indicator: classes.tabsIndicator }} + <Root value={sideBarPanel === 'closed' ? false : sideBarPanel} onChange={this.handleChange} variant="fullWidth" @@ -88,7 +127,6 @@ export class WindowSideBarButtons extends Component { <TabButton value="info" onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} icon={(<InfoIcon />)} /> @@ -97,7 +135,6 @@ export class WindowSideBarButtons extends Component { <TabButton value="attribution" onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} icon={(<AttributionIcon />)} /> @@ -106,7 +143,6 @@ export class WindowSideBarButtons extends Component { <TabButton value="canvas" onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} icon={(<CanvasIndexIcon />)} /> @@ -115,10 +151,9 @@ export class WindowSideBarButtons extends Component { <TabButton value="annotations" onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} icon={( - <Badge overlap="rectangular" classes={{ badge: classes.badge }} invisible={!hasAnnotations} variant="dot"> + <Badge overlap="rectangular" color="notification" invisible={!hasAnnotations} variant="dot"> <AnnotationIcon /> </Badge> )} @@ -128,10 +163,9 @@ export class WindowSideBarButtons extends Component { <TabButton value="search" onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} icon={( - <Badge overlap="rectangular" classes={{ badge: classes.badge }} invisible={!hasSearchResults} variant="dot"> + <Badge overlap="rectangular" color="notification" invisible={!hasSearchResults} variant="dot"> <SearchIcon /> </Badge> )} @@ -141,10 +175,9 @@ export class WindowSideBarButtons extends Component { <TabButton value="layers" onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} icon={( - <Badge overlap="rectangular" classes={{ badge: classes.badge }} invisible={!hasCurrentLayers} variant="dot"> + <Badge overlap="rectangular" color="notification" invisible={!hasCurrentLayers} variant="dot"> <LayersIcon /> </Badge> )} @@ -154,21 +187,19 @@ export class WindowSideBarButtons extends Component { && PluginComponents.map(PluginComponent => ( <TabButton onKeyUp={this.handleKeyUp} - classes={{ root: classes.tab, selected: classes.tabSelected }} t={t} key={PluginComponent.value} value={PluginComponent.value} icon={<PluginComponent />} /> ))} - </Tabs> + </Root> ); } } WindowSideBarButtons.propTypes = { addCompanionWindow: PropTypes.func.isRequired, - classes: PropTypes.objectOf(PropTypes.string), hasAnnotations: PropTypes.bool, hasAnyAnnotations: PropTypes.bool, hasAnyLayers: PropTypes.bool, @@ -182,7 +213,6 @@ WindowSideBarButtons.propTypes = { }; WindowSideBarButtons.defaultProps = { - classes: {}, hasAnnotations: false, hasAnyAnnotations: false, hasAnyLayers: false, diff --git a/src/components/WindowSideBarCanvasPanel.js b/src/components/WindowSideBarCanvasPanel.js index d785b5abe23635693be6a1d8616fd8eaaf9d3779..768f8c65d5a4e5d062479d8ca9c57719e13bc5b2 100644 --- a/src/components/WindowSideBarCanvasPanel.js +++ b/src/components/WindowSideBarCanvasPanel.js @@ -1,21 +1,26 @@ import { createRef, Component } from 'react'; import PropTypes from 'prop-types'; -import Tabs from '@material-ui/core/Tabs'; -import Tab from '@material-ui/core/Tab'; -import Tooltip from '@material-ui/core/Tooltip'; -import Button from '@material-ui/core/Button'; -import ItemListIcon from '@material-ui/icons/ReorderSharp'; -import TocIcon from '@material-ui/icons/SortSharp'; -import ThumbnailListIcon from '@material-ui/icons/ViewListSharp'; -import Typography from '@material-ui/core/Typography'; -import ArrowForwardIcon from '@material-ui/icons/ArrowForwardSharp'; -import FormControl from '@material-ui/core/FormControl'; -import Select from '@material-ui/core/Select'; -import MenuItem from '@material-ui/core/MenuItem'; +import { styled } from '@mui/material/styles'; +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; +import Tooltip from '@mui/material/Tooltip'; +import Button from '@mui/material/Button'; +import ItemListIcon from '@mui/icons-material/ReorderSharp'; +import TocIcon from '@mui/icons-material/SortSharp'; +import ThumbnailListIcon from '@mui/icons-material/ViewListSharp'; +import Typography from '@mui/material/Typography'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForwardSharp'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; import CompanionWindow from '../containers/CompanionWindow'; import SidebarIndexList from '../containers/SidebarIndexList'; import SidebarIndexTableOfContents from '../containers/SidebarIndexTableOfContents'; +const StyledBreak = styled('div')(() => ({ + flexBasis: '100%', + height: 0, +})); /** * a panel showing the canvases for a given manifest */ @@ -57,7 +62,6 @@ export class WindowSideBarCanvasPanel extends Component { */ render() { const { - classes, collection, id, showMultipart, @@ -95,7 +99,6 @@ export class WindowSideBarCanvasPanel extends Component { id={id} windowId={windowId} ref={this.containerRef} - otherRef={this.containerRef} titleControls={( <> { @@ -107,14 +110,19 @@ export class WindowSideBarCanvasPanel extends Component { horizontal: 'left', vertical: 'bottom', }, - getContentAnchorEl: null, }} displayEmpty value={sequenceId} onChange={this.handleSequenceChange} name="sequenceId" - classes={{ select: classes.select }} - className={classes.selectEmpty} + sx={{ + '&.MuiSelect-select': { + '&:focus': { + backgroundColor: 'background.paper', + }, + }, + backgroundColor: 'background.paper', + }} data-testid="sequence-select" > { sequences.map((s, i) => <MenuItem value={s.id} key={s.id}><Typography variant="body2">{ WindowSideBarCanvasPanel.getUseableLabel(s, i) }</Typography></MenuItem>) } @@ -122,7 +130,7 @@ export class WindowSideBarCanvasPanel extends Component { </FormControl> ) } - <div className={classes.break} /> + <StyledBreak /> <Tabs value={variant} onChange={this.handleVariantChange} @@ -131,10 +139,10 @@ export class WindowSideBarCanvasPanel extends Component { textColor="primary" > {showToc && ( - <Tooltip title={t('tableOfContentsList')} value="tableOfContents"><Tab className={classes.variantTab} value="tableOfContents" aria-label={t('tableOfContentsList')} aria-controls={`tab-panel-${id}`} icon={<TocIcon style={{ transform: 'scale(-1, 1)' }} />} /></Tooltip> + <Tooltip title={t('tableOfContentsList')} value="tableOfContents"><Tab sx={{ minWidth: 'auto' }} value="tableOfContents" aria-label={t('tableOfContentsList')} aria-controls={`tab-panel-${id}`} icon={<TocIcon style={{ transform: 'scale(-1, 1)' }} />} /></Tooltip> )} - <Tooltip title={t('itemList')} value="item"><Tab className={classes.variantTab} value="item" aria-label={t('itemList')} aria-controls={`tab-panel-${id}`} icon={<ItemListIcon />} /></Tooltip> - <Tooltip title={t('thumbnailList')} value="thumbnail"><Tab className={classes.variantTab} value="thumbnail" aria-label={t('thumbnailList')} aria-controls={`tab-panel-${id}`} icon={<ThumbnailListIcon />} /></Tooltip> + <Tooltip title={t('itemList')} value="item"><Tab sx={{ minWidth: 'auto' }} value="item" aria-label={t('itemList')} aria-controls={`tab-panel-${id}`} icon={<ItemListIcon />} /></Tooltip> + <Tooltip title={t('thumbnailList')} value="thumbnail"><Tab sx={{ minWidth: 'auto' }} value="thumbnail" aria-label={t('thumbnailList')} aria-controls={`tab-panel-${id}`} icon={<ThumbnailListIcon />} /></Tooltip> </Tabs> </> )} @@ -146,7 +154,7 @@ export class WindowSideBarCanvasPanel extends Component { onClick={showMultipart} endIcon={<ArrowForwardIcon />} > - <Typography className={classes.collectionNavigationButton}> + <Typography sx={{ textTransform: 'none' }}> {WindowSideBarCanvasPanel.getUseableLabel(collection)} </Typography> </Button> @@ -159,7 +167,6 @@ export class WindowSideBarCanvasPanel extends Component { } WindowSideBarCanvasPanel.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types id: PropTypes.string.isRequired, sequenceId: PropTypes.string, diff --git a/src/components/WindowSideBarCollectionPanel.js b/src/components/WindowSideBarCollectionPanel.js index a6964ca77705542d3092fabc33881113f91abad8..f55a2dbdef728fbc29d65c9c04cb965644182325 100644 --- a/src/components/WindowSideBarCollectionPanel.js +++ b/src/components/WindowSideBarCollectionPanel.js @@ -1,14 +1,14 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import List from '@material-ui/core/List'; -import ListItem from '@material-ui/core/ListItem'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import ListItemText from '@material-ui/core/ListItemText'; -import MenuList from '@material-ui/core/MenuList'; -import MenuItem from '@material-ui/core/MenuItem'; -import Typography from '@material-ui/core/Typography'; -import Skeleton from '@material-ui/lab/Skeleton'; -import ArrowUpwardIcon from '@material-ui/icons/ArrowUpwardSharp'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuList from '@mui/material/MenuList'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; +import Skeleton from '@mui/material/Skeleton'; +import ArrowUpwardIcon from '@mui/icons-material/ArrowUpwardSharp'; import CompanionWindow from '../containers/CompanionWindow'; import IIIFThumbnail from '../containers/IIIFThumbnail'; @@ -20,7 +20,12 @@ function Item({ <MenuItem alignItems="flex-start" button + divider component="li" + variant="multiline" + sx={{ + paddingRight: 1, + }} {...otherProps} > { variant === 'thumbnail' && ( @@ -74,7 +79,6 @@ export class WindowSideBarCollectionPanel extends Component { render() { const { canvasNavigation, - classes, collectionPath, collection, id, @@ -114,7 +118,7 @@ export class WindowSideBarCollectionPanel extends Component { )} <Typography variant="h6"> { collection && WindowSideBarCollectionPanel.getUseableLabel(collection)} - { isFetching && <Skeleton className={classes.placeholder} variant="text" />} + { isFetching && <Skeleton variant="text" />} </Typography> </> )} @@ -123,9 +127,9 @@ export class WindowSideBarCollectionPanel extends Component { { isFetching && ( <MenuItem> <ListItemText> - <Skeleton className={classes.placeholder} variant="text" /> - <Skeleton className={classes.placeholder} variant="text" /> - <Skeleton className={classes.placeholder} variant="text" /> + <Skeleton variant="text" /> + <Skeleton variant="text" /> + <Skeleton variant="text" /> </ListItemText> </MenuItem> )} @@ -144,7 +148,6 @@ export class WindowSideBarCollectionPanel extends Component { canvasNavigation={canvasNavigation} manifest={manifest} variant={variant} - className={classes.menuItem} selected={manifestId === manifest.id} /> ); @@ -167,7 +170,6 @@ export class WindowSideBarCollectionPanel extends Component { canvasNavigation={canvasNavigation} manifest={manifest} variant={variant} - className={classes.menuItem} selected={manifestId === manifest.id} /> ); @@ -184,7 +186,6 @@ WindowSideBarCollectionPanel.propTypes = { height: PropTypes.number, width: PropTypes.number, }).isRequired, - classes: PropTypes.objectOf(PropTypes.string).isRequired, collection: PropTypes.object, // eslint-disable-line react/forbid-prop-types collectionPath: PropTypes.arrayOf(PropTypes.string), id: PropTypes.string.isRequired, diff --git a/src/components/WindowSideBarInfoPanel.js b/src/components/WindowSideBarInfoPanel.js index e2931f70e34426beac5cb635019dff157a79f99e..3261679503f289fa29caab0b4fb986e8bb963069 100644 --- a/src/components/WindowSideBarInfoPanel.js +++ b/src/components/WindowSideBarInfoPanel.js @@ -1,6 +1,7 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; import CompanionWindow from '../containers/CompanionWindow'; +import { CompanionWindowSection } from './CompanionWindowSection'; import CanvasInfo from '../containers/CanvasInfo'; import LocalePicker from '../containers/LocalePicker'; import ManifestInfo from '../containers/ManifestInfo'; @@ -21,7 +22,6 @@ export class WindowSideBarInfoPanel extends Component { windowId, id, canvasIds, - classes, collectionPath, t, locale, @@ -49,7 +49,9 @@ export class WindowSideBarInfoPanel extends Component { > { canvasIds.map((canvasId, index) => ( - <div key={canvasId} className={classes.section}> + <CompanionWindowSection + key={canvasId} + > <CanvasInfo id={id} canvasId={canvasId} @@ -57,22 +59,22 @@ export class WindowSideBarInfoPanel extends Component { totalSize={canvasIds.length} windowId={windowId} /> - </div> + </CompanionWindowSection> )) } { collectionPath.length > 0 && ( - <div className={classes.section}> + <CompanionWindowSection> <CollectionInfo id={id} windowId={windowId} /> - </div> + </CompanionWindowSection> )} - <div className={classes.section}> + <CompanionWindowSection> <ManifestInfo id={id} windowId={windowId} /> - </div> + </CompanionWindowSection> - <div className={classes.section}> + <CompanionWindowSection> <ManifestRelatedLinks id={id} windowId={windowId} /> - </div> + </CompanionWindowSection> </CompanionWindow> ); } @@ -81,7 +83,6 @@ export class WindowSideBarInfoPanel extends Component { WindowSideBarInfoPanel.propTypes = { availableLocales: PropTypes.arrayOf(PropTypes.string), canvasIds: PropTypes.arrayOf(PropTypes.string), - classes: PropTypes.objectOf(PropTypes.string), collectionPath: PropTypes.arrayOf(PropTypes.string), id: PropTypes.string.isRequired, locale: PropTypes.string, @@ -94,7 +95,6 @@ WindowSideBarInfoPanel.propTypes = { WindowSideBarInfoPanel.defaultProps = { availableLocales: [], canvasIds: [], - classes: {}, collectionPath: [], locale: '', setLocale: undefined, diff --git a/src/components/WindowThumbnailSettings.js b/src/components/WindowThumbnailSettings.js index 22b7eb57db316222e7a93a9e18b8301d138e7fb3..042355e4d01054086ec6cd21195670e166a06ffd 100644 --- a/src/components/WindowThumbnailSettings.js +++ b/src/components/WindowThumbnailSettings.js @@ -1,11 +1,25 @@ import { Component } from 'react'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import ListSubheader from '@material-ui/core/ListSubheader'; -import MenuItem from '@material-ui/core/MenuItem'; -import ThumbnailsOffIcon from '@material-ui/icons/CropDinSharp'; +import { styled } from '@mui/material/styles'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import ListSubheader from '@mui/material/ListSubheader'; +import MenuItem from '@mui/material/MenuItem'; +import ThumbnailsOffIcon from '@mui/icons-material/CropDinSharp'; import PropTypes from 'prop-types'; import ThumbnailNavigationBottomIcon from './icons/ThumbnailNavigationBottomIcon'; import ThumbnailNavigationRightIcon from './icons/ThumbnailNavigationRightIcon'; + +const ThumbnailOption = styled(MenuItem, { name: 'WindowThumbnailSettings', slot: 'option' })(({ selected, theme }) => ({ + '& .MuiFormControlLabel-label': { + borderBottom: '2px solid transparent', + ...(selected && { + borderBottomColor: theme.palette.secondary.main, + }), + }, + backgroundColor: 'transparent !important', + color: selected ? theme.palette.secondary.main : undefined, + display: 'inline-block', +})); + /** * */ @@ -34,56 +48,53 @@ export class WindowThumbnailSettings extends Component { */ render() { const { - classes, handleClose, t, thumbnailNavigationPosition, direction, + handleClose, t, thumbnailNavigationPosition, direction, } = this.props; return ( <> - <ListSubheader role="presentation" disableSticky tabIndex="-1">{t('thumbnails')}</ListSubheader> + <ListSubheader role="presentation" disableSticky tabIndex={-1}>{t('thumbnails')}</ListSubheader> - <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('off'); handleClose(); }}> + <ThumbnailOption selected={thumbnailNavigationPosition === 'off'} onClick={() => { this.handleChange('off'); handleClose(); }}> <FormControlLabel value="off" - classes={{ label: thumbnailNavigationPosition === 'off' ? classes.selectedLabel : classes.label }} control={ - <ThumbnailsOffIcon color={thumbnailNavigationPosition === 'off' ? 'secondary' : undefined} /> + <ThumbnailsOffIcon color={thumbnailNavigationPosition === 'off' ? 'secondary' : undefined} fill="currentcolor" /> } label={t('off')} labelPlacement="bottom" /> - </MenuItem> - <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('far-bottom'); handleClose(); }}> + </ThumbnailOption> + <ThumbnailOption selected={thumbnailNavigationPosition === 'far-bottom'} onClick={() => { this.handleChange('far-bottom'); handleClose(); }}> <FormControlLabel value="far-bottom" - classes={{ label: thumbnailNavigationPosition === 'far-bottom' ? classes.selectedLabel : classes.label }} control={ - <ThumbnailNavigationBottomIcon color={thumbnailNavigationPosition === 'far-bottom' ? 'secondary' : undefined} /> + <ThumbnailNavigationBottomIcon color={thumbnailNavigationPosition === 'far-bottom' ? 'secondary' : undefined} fill="currentcolor" /> } label={t('bottom')} labelPlacement="bottom" /> - </MenuItem> - <MenuItem className={classes.MenuItem} onClick={() => { this.handleChange('far-right'); handleClose(); }}> + </ThumbnailOption> + <ThumbnailOption selected={thumbnailNavigationPosition === 'far-right'} onClick={() => { this.handleChange('far-right'); handleClose(); }}> <FormControlLabel value="far-right" - classes={{ label: thumbnailNavigationPosition === 'far-right' ? classes.selectedLabel : classes.label }} control={( <ThumbnailNavigationRightIcon color={thumbnailNavigationPosition === 'far-right' ? 'secondary' : undefined} + fill="currentcolor" style={direction === 'rtl' ? { transform: 'rotate(180deg)' } : {}} /> )} label={t('right')} labelPlacement="bottom" /> - </MenuItem> + </ThumbnailOption> </> ); } } WindowThumbnailSettings.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, direction: PropTypes.string.isRequired, handleClose: PropTypes.func, setWindowThumbnailPosition: PropTypes.func.isRequired, diff --git a/src/components/WindowTopBar.js b/src/components/WindowTopBar.js index cf204b2f88c260c8f4a40c08ca594a209deb1de2..5e32f8cd0787303091ddba23f3fb1843cf6d40d6 100644 --- a/src/components/WindowTopBar.js +++ b/src/components/WindowTopBar.js @@ -1,9 +1,10 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import MenuIcon from '@material-ui/icons/MenuSharp'; -import CloseIcon from '@material-ui/icons/CloseSharp'; -import Toolbar from '@material-ui/core/Toolbar'; -import AppBar from '@material-ui/core/AppBar'; +import { styled } from '@mui/material/styles'; +import MenuIcon from '@mui/icons-material/MenuSharp'; +import CloseIcon from '@mui/icons-material/CloseSharp'; +import Toolbar from '@mui/material/Toolbar'; +import AppBar from '@mui/material/AppBar'; import classNames from 'classnames'; import WindowTopMenuButton from '../containers/WindowTopMenuButton'; import WindowTopBarPluginArea from '../containers/WindowTopBarPluginArea'; @@ -15,6 +16,22 @@ import WindowMaxIcon from './icons/WindowMaxIcon'; import WindowMinIcon from './icons/WindowMinIcon'; import ns from '../config/css-ns'; +const Root = styled(AppBar, { name: 'WindowTopBar', slot: 'root' })(() => ({ + zIndex: 1100, +})); + +const StyledToolbar = styled(Toolbar, { name: 'WindowTopBar', slot: 'toolbar' })(({ ownerState, theme }) => ({ + backgroundColor: theme.palette.shades?.main, + borderTop: '2px solid', + borderTopColor: ownerState?.focused ? theme.palette.primary.main : 'transparent', + minHeight: 32, + paddingLeft: theme.spacing(0.5), + paddingRight: theme.spacing(0.5), + ...(ownerState?.windowDraggable && { + cursor: 'move', + }), +})); + /** * WindowTopBar */ @@ -25,66 +42,61 @@ export class WindowTopBar extends Component { */ render() { const { - removeWindow, windowId, classes, toggleWindowSideBar, t, windowDraggable, - maximizeWindow, maximized, minimizeWindow, focused, allowClose, allowMaximize, + removeWindow, windowId, toggleWindowSideBar, t, + maximizeWindow, maximized, minimizeWindow, allowClose, allowMaximize, focusWindow, allowFullscreen, allowTopMenuButton, allowWindowSideBar, + component, } = this.props; return ( - <AppBar position="relative" color="default"> - <nav aria-label={t('windowNavigation')}> - <Toolbar - disableGutters - onMouseDown={focusWindow} - className={classNames( - classes.windowTopBarStyle, - windowDraggable ? classes.windowTopBarStyleDraggable : null, - focused ? classes.focused : null, - ns('window-top-bar'), - )} - variant="dense" - > - {allowWindowSideBar && ( - <MiradorMenuButton - aria-label={t('toggleWindowSideBar')} - onClick={toggleWindowSideBar} - className={ns('window-menu-btn')} - > - <MenuIcon /> - </MiradorMenuButton> - )} - <WindowTopBarTitle - windowId={windowId} - /> - {allowTopMenuButton && ( - <WindowTopMenuButton windowId={windowId} className={ns('window-menu-btn')} /> - )} - <WindowTopBarPluginArea windowId={windowId} /> - <WindowTopBarPluginMenu windowId={windowId} /> - {allowMaximize && ( - <MiradorMenuButton - aria-label={(maximized ? t('minimizeWindow') : t('maximizeWindow'))} - className={classNames(ns('window-maximize'), ns('window-menu-btn'))} - onClick={(maximized ? minimizeWindow : maximizeWindow)} - > - {(maximized ? <WindowMinIcon /> : <WindowMaxIcon />)} - </MiradorMenuButton> - )} - {allowFullscreen && ( - <FullScreenButton className={ns('window-menu-btn')} /> - )} - {allowClose && ( - <MiradorMenuButton - aria-label={t('closeWindow')} - className={classNames(ns('window-close'), ns('window-menu-btn'))} - onClick={removeWindow} - > - <CloseIcon /> - </MiradorMenuButton> - )} - </Toolbar> - </nav> - </AppBar> + <Root component={component} aria-label={t('windowNavigation')} position="relative" color="default" enableColorOnDark> + <StyledToolbar + disableGutters + onMouseDown={focusWindow} + ownerState={this.props} + className={classNames(ns('window-top-bar'))} + variant="dense" + > + {allowWindowSideBar && ( + <MiradorMenuButton + aria-label={t('toggleWindowSideBar')} + onClick={toggleWindowSideBar} + className={ns('window-menu-btn')} + > + <MenuIcon /> + </MiradorMenuButton> + )} + <WindowTopBarTitle + windowId={windowId} + /> + {allowTopMenuButton && ( + <WindowTopMenuButton windowId={windowId} className={ns('window-menu-btn')} /> + )} + <WindowTopBarPluginArea windowId={windowId} /> + <WindowTopBarPluginMenu windowId={windowId} /> + {allowMaximize && ( + <MiradorMenuButton + aria-label={(maximized ? t('minimizeWindow') : t('maximizeWindow'))} + className={classNames(ns('window-maximize'), ns('window-menu-btn'))} + onClick={(maximized ? minimizeWindow : maximizeWindow)} + > + {(maximized ? <WindowMinIcon /> : <WindowMaxIcon />)} + </MiradorMenuButton> + )} + {allowFullscreen && ( + <FullScreenButton className={ns('window-menu-btn')} /> + )} + {allowClose && ( + <MiradorMenuButton + aria-label={t('closeWindow')} + className={classNames(ns('window-close'), ns('window-menu-btn'))} + onClick={removeWindow} + > + <CloseIcon /> + </MiradorMenuButton> + )} + </StyledToolbar> + </Root> ); } } @@ -95,8 +107,8 @@ WindowTopBar.propTypes = { allowMaximize: PropTypes.bool, allowTopMenuButton: PropTypes.bool, allowWindowSideBar: PropTypes.bool, - classes: PropTypes.objectOf(PropTypes.string).isRequired, - focused: PropTypes.bool, + component: PropTypes.elementType, + focused: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types focusWindow: PropTypes.func, maximized: PropTypes.bool, maximizeWindow: PropTypes.func, @@ -104,7 +116,7 @@ WindowTopBar.propTypes = { removeWindow: PropTypes.func.isRequired, t: PropTypes.func, toggleWindowSideBar: PropTypes.func.isRequired, - windowDraggable: PropTypes.bool, + windowDraggable: PropTypes.bool, // eslint-disable-line react/no-unused-prop-types windowId: PropTypes.string.isRequired, }; @@ -114,6 +126,7 @@ WindowTopBar.defaultProps = { allowMaximize: true, allowTopMenuButton: true, allowWindowSideBar: true, + component: 'nav', focused: false, focusWindow: () => {}, maximized: false, diff --git a/src/components/WindowTopBarPluginMenu.js b/src/components/WindowTopBarPluginMenu.js index 78f95a73ec5566fab2d4565c34d86b3ce2a0d732..972cbf544b6c32b1aa87f2d8fcfff1e770ef0b54 100644 --- a/src/components/WindowTopBarPluginMenu.js +++ b/src/components/WindowTopBarPluginMenu.js @@ -1,7 +1,7 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import MoreVertIcon from '@material-ui/icons/MoreVertSharp'; -import Menu from '@material-ui/core/Menu'; +import MoreVertIcon from '@mui/icons-material/MoreVertSharp'; +import Menu from '@mui/material/Menu'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import { PluginHook } from './PluginHook'; @@ -47,7 +47,7 @@ export class WindowTopBarPluginMenu extends Component { */ render() { const { - classes, container, PluginComponents, t, windowId, menuIcon, + container, PluginComponents, t, windowId, menuIcon, } = this.props; const { anchorEl, open } = this.state; const windowPluginMenuId = `window-plugin-menu_${windowId}`; @@ -59,7 +59,7 @@ export class WindowTopBarPluginMenu extends Component { aria-haspopup="true" aria-label={t('windowPluginMenu')} aria-owns={open ? windowPluginMenuId : undefined} - className={open ? classes.ctrlBtnSelected : null} + selected={open} onClick={this.handleMenuClick} > {menuIcon} @@ -77,7 +77,6 @@ export class WindowTopBarPluginMenu extends Component { horizontal: 'right', vertical: 'top', }} - getContentAnchorEl={null} open={open} onClose={() => this.handleMenuClose()} > @@ -90,9 +89,6 @@ export class WindowTopBarPluginMenu extends Component { WindowTopBarPluginMenu.propTypes = { anchorEl: PropTypes.object, // eslint-disable-line react/forbid-prop-types - classes: PropTypes.shape({ - ctrlBtnSelected: PropTypes.string, - }), container: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), menuIcon: PropTypes.element, open: PropTypes.bool, @@ -105,7 +101,6 @@ WindowTopBarPluginMenu.propTypes = { WindowTopBarPluginMenu.defaultProps = { anchorEl: null, - classes: {}, container: null, menuIcon: <MoreVertIcon />, open: false, diff --git a/src/components/WindowTopBarTitle.js b/src/components/WindowTopBarTitle.js index b47f00c2c2e03433cf198724553503e38fecb5ec..5c91250e12861c7e2a4426e214fdc5499e663bba 100644 --- a/src/components/WindowTopBarTitle.js +++ b/src/components/WindowTopBarTitle.js @@ -1,9 +1,21 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Typography from '@material-ui/core/Typography'; -import Skeleton from '@material-ui/lab/Skeleton'; -import ErrorIcon from '@material-ui/icons/ErrorOutlineSharp'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Skeleton from '@mui/material/Skeleton'; +import ErrorIcon from '@mui/icons-material/ErrorOutlineSharp'; +const StyledTitleTypography = styled(TitleTypography)(({ theme }) => ({ + ...theme.typography.h6, + flexGrow: 1, + paddingLeft: theme.spacing(0.5), +})); + +const StyledTitle = styled('div')(({ theme }) => ({ + ...theme.typography.h6, + flexGrow: 1, + paddingLeft: theme.spacing(0.5), +})); /** */ function TitleTypography({ children, ...props }) { return ( @@ -27,32 +39,32 @@ export class WindowTopBarTitle extends Component { */ render() { const { - classes, error, hideWindowTitle, isFetching, manifestTitle, + error, hideWindowTitle, isFetching, manifestTitle, } = this.props; let title = null; if (isFetching) { title = ( - <TitleTypography className={classes.title}> + <StyledTitleTypography> <Skeleton variant="text" /> - </TitleTypography> + </StyledTitleTypography> ); } else if (error) { title = ( <> <ErrorIcon color="error" /> - <TitleTypography color="textSecondary" className={classes.title}> + <StyledTitleTypography color="textSecondary"> {error} - </TitleTypography> + </StyledTitleTypography> </> ); } else if (hideWindowTitle) { - title = (<div className={classes.title} />); + title = (<StyledTitle />); } else { title = ( - <TitleTypography className={classes.title}> + <StyledTitleTypography> {manifestTitle} - </TitleTypography> + </StyledTitleTypography> ); } return title; @@ -60,7 +72,6 @@ export class WindowTopBarTitle extends Component { } WindowTopBarTitle.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, error: PropTypes.string, hideWindowTitle: PropTypes.bool, isFetching: PropTypes.bool, diff --git a/src/components/WindowTopMenu.js b/src/components/WindowTopMenu.js index d978876a33debb3f9ea428b398720c1dc21094f4..feb45377bd37aafa51e484b40216faf69c2d2340 100644 --- a/src/components/WindowTopMenu.js +++ b/src/components/WindowTopMenu.js @@ -1,6 +1,6 @@ import { Component } from 'react'; -import Menu from '@material-ui/core//Menu'; -import ListSubheader from '@material-ui/core/ListSubheader'; +import Menu from '@mui/material//Menu'; +import ListSubheader from '@mui/material/ListSubheader'; import PropTypes from 'prop-types'; import WindowThumbnailSettings from '../containers/WindowThumbnailSettings'; import WindowViewSettings from '../containers/WindowViewSettings'; @@ -11,7 +11,7 @@ function PluginHookWithHeader(props) { const { PluginComponents, t } = props; // eslint-disable-line react/prop-types return PluginComponents ? ( <> - <ListSubheader role="presentation" disableSticky tabIndex="-1">{t('windowPluginButtons')}</ListSubheader> + <ListSubheader role="presentation" disableSticky tabIndex={-1}>{t('windowPluginButtons')}</ListSubheader> <PluginHook {...props} /> </> ) : null; @@ -47,7 +47,6 @@ export class WindowTopMenu extends Component { onExit: toggleDraggingEnabled, }} orientation="horizontal" - getContentAnchorEl={null} anchorEl={anchorEl} open={open} > diff --git a/src/components/WindowTopMenuButton.js b/src/components/WindowTopMenuButton.js index ca9ab8db0109377d74a17f229cbf40a25bd40ffb..e48bd84d2c3c5aa0d6fbbab209a1d1f28be262d8 100644 --- a/src/components/WindowTopMenuButton.js +++ b/src/components/WindowTopMenuButton.js @@ -1,6 +1,5 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; import WindowTopMenu from '../containers/WindowTopMenu'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import WindowOptionsIcon from './icons/WindowOptionsIcon'; @@ -47,7 +46,7 @@ export class WindowTopMenuButton extends Component { */ render() { const { - classes, className, t, windowId, + classes, t, windowId, } = this.props; const { open, anchorEl } = this.state; const menuId = `window-menu_${windowId}`; @@ -57,7 +56,8 @@ export class WindowTopMenuButton extends Component { aria-haspopup="true" aria-label={t('windowMenu')} aria-owns={open ? menuId : undefined} - className={classNames(className, open ? classes.ctrlBtnSelected : null)} + className={open ? classes.ctrlBtnSelected : undefined} + selected={open} onClick={this.handleMenuClick} > <WindowOptionsIcon /> @@ -75,13 +75,12 @@ export class WindowTopMenuButton extends Component { } WindowTopMenuButton.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, - className: PropTypes.string, + classes: PropTypes.objectOf(PropTypes.string), t: PropTypes.func, windowId: PropTypes.string.isRequired, }; WindowTopMenuButton.defaultProps = { - className: '', + classes: {}, t: key => key, }; diff --git a/src/components/WindowViewSettings.js b/src/components/WindowViewSettings.js index 90c56b823269cf733b54c88132b4cb732467883e..e693b2ffcdc62caab53200c3178d6597257169ea 100644 --- a/src/components/WindowViewSettings.js +++ b/src/components/WindowViewSettings.js @@ -1,13 +1,26 @@ import { Component } from 'react'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import MenuItem from '@material-ui/core/MenuItem'; -import ListSubheader from '@material-ui/core/ListSubheader'; -import SingleIcon from '@material-ui/icons/CropOriginalSharp'; -import ScrollViewIcon from '@material-ui/icons/ViewColumn'; +import { styled } from '@mui/material/styles'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import MenuItem from '@mui/material/MenuItem'; +import ListSubheader from '@mui/material/ListSubheader'; +import SingleIcon from '@mui/icons-material/CropOriginalSharp'; +import ScrollViewIcon from '@mui/icons-material/ViewColumn'; import PropTypes from 'prop-types'; import BookViewIcon from './icons/BookViewIcon'; import GalleryViewIcon from './icons/GalleryViewIcon'; +const ViewOption = styled(MenuItem, { name: 'WindowViewSettings', slot: 'option' })(({ selected, theme }) => ({ + '& .MuiFormControlLabel-label': { + borderBottom: '2px solid transparent', + ...(selected && { + borderBottomColor: theme.palette.secondary.main, + }), + }, + backgroundColor: 'transparent !important', + color: selected ? theme.palette.secondary.main : undefined, + display: 'inline-block', +})); + /** * */ @@ -36,7 +49,7 @@ export class WindowViewSettings extends Component { */ render() { const { - classes, handleClose, t, windowViewType, viewTypes, + handleClose, t, windowViewType, viewTypes, } = this.props; const iconMap = { @@ -49,26 +62,25 @@ export class WindowViewSettings extends Component { /** Suspiciously similar to a component, yet if it is invoked through JSX none of the click handlers work? */ const menuItem = ({ value, Icon }) => ( - <MenuItem + <ViewOption + selected={windowViewType === value} key={value} - className={classes.MenuItem} autoFocus={windowViewType === value} onClick={() => { this.handleChange(value); handleClose(); }} > <FormControlLabel value={value} - classes={{ label: windowViewType === value ? classes.selectedLabel : classes.label }} - control={<Icon color={windowViewType === value ? 'secondary' : undefined} />} + control={<Icon fill="currentcolor" color={windowViewType === value ? 'secondary' : undefined} />} label={t(value)} labelPlacement="bottom" /> - </MenuItem> + </ViewOption> ); if (viewTypes.length === 0) return null; return ( <> - <ListSubheader role="presentation" disableSticky tabIndex="-1">{t('view')}</ListSubheader> + <ListSubheader role="presentation" disableSticky tabIndex={-1}>{t('view')}</ListSubheader> { viewTypes.map(value => menuItem({ Icon: iconMap[value], value })) } </> ); @@ -76,7 +88,6 @@ export class WindowViewSettings extends Component { } WindowViewSettings.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, handleClose: PropTypes.func, setWindowViewType: PropTypes.func.isRequired, t: PropTypes.func, diff --git a/src/components/Workspace.js b/src/components/Workspace.js index f99f61d4e4351078b811d874dba828aca7d7ad6f..c94f8a30b3fc1eb1550ff0fe3a90211bedc14c5b 100644 --- a/src/components/Workspace.js +++ b/src/components/Workspace.js @@ -1,14 +1,22 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import classNames from 'classnames'; -import Grid from '@material-ui/core/Grid'; -import Typography from '@material-ui/core/Typography'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; +import { visuallyHidden } from '@mui/utils'; import Window from '../containers/Window'; import WorkspaceMosaic from '../containers/WorkspaceMosaic'; import WorkspaceElastic from '../containers/WorkspaceElastic'; import ns from '../config/css-ns'; import { IIIFDropTarget } from './IIIFDropTarget'; +const Root = styled('div', { name: 'Workspace', slot: 'root' })(() => ({ + height: '100%', + position: 'relative', + width: '100%', +})); + /** * Represents a work area that contains any number of windows * @memberof Workspace @@ -62,26 +70,28 @@ export class Workspace extends Component { const { t } = this.props; return ( - <Grid - alignItems="center" - container - style={{ - height: '100%', - }} - > + <Root> <Grid - xs={12} - item + alignItems="center" + container + style={{ + height: '100%', + }} > - <Typography - variant="h1" - component="div" - align="center" + <Grid + xs={12} + item > - {t('welcome')} - </Typography> + <Typography + variant="h1" + component="div" + align="center" + > + {t('welcome')} + </Typography> + </Grid> </Grid> - </Grid> + </Root> ); } @@ -107,23 +117,21 @@ export class Workspace extends Component { * render */ render() { - const { classes, isWorkspaceControlPanelVisible, t } = this.props; + const { t } = this.props; return ( <IIIFDropTarget onDrop={this.handleDrop}> - <div + <Root + ownerState={this.props} className={ classNames( ns('workspace-viewport'), - (isWorkspaceControlPanelVisible && ns('workspace-with-control-panel')), - (isWorkspaceControlPanelVisible && classes.workspaceWithControlPanel), - classes.workspaceViewport, ) } > - <Typography variant="srOnly" component="h1">{t('miradorViewer')}</Typography> + <Typography style={visuallyHidden} component="h1">{t('miradorViewer')}</Typography> {this.workspaceByType()} - </div> + </Root> </IIIFDropTarget> ); } @@ -132,8 +140,6 @@ export class Workspace extends Component { Workspace.propTypes = { addWindow: PropTypes.func, allowNewWindows: PropTypes.bool, - classes: PropTypes.objectOf(PropTypes.string).isRequired, - isWorkspaceControlPanelVisible: PropTypes.bool.isRequired, maximizedWindowIds: PropTypes.arrayOf(PropTypes.string), t: PropTypes.func.isRequired, windowIds: PropTypes.arrayOf(PropTypes.string), diff --git a/src/components/WorkspaceAdd.js b/src/components/WorkspaceAdd.js index 172c8d3474970c769e39084dbde0bad3b43c846f..6e744878c40c1503314274002e1fe6cca2c6de97 100644 --- a/src/components/WorkspaceAdd.js +++ b/src/components/WorkspaceAdd.js @@ -1,16 +1,18 @@ import { createRef, Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import classNames from 'classnames'; -import AddIcon from '@material-ui/icons/AddSharp'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMoreSharp'; -import AppBar from '@material-ui/core/AppBar'; -import Drawer from '@material-ui/core/Drawer'; -import Grid from '@material-ui/core/Grid'; -import Fab from '@material-ui/core/Fab'; -import List from '@material-ui/core/List'; -import Paper from '@material-ui/core/Paper'; -import Toolbar from '@material-ui/core/Toolbar'; -import Typography from '@material-ui/core/Typography'; +import AddIcon from '@mui/icons-material/AddSharp'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMoreSharp'; +import AppBar from '@mui/material/AppBar'; +import Drawer from '@mui/material/Drawer'; +import Grid from '@mui/material/Grid'; +import Fab from '@mui/material/Fab'; +import List from '@mui/material/List'; +import Paper from '@mui/material/Paper'; +import Toolbar from '@mui/material/Toolbar'; +import Typography from '@mui/material/Typography'; +import { visuallyHidden } from '@mui/utils'; import ns from '../config/css-ns'; import ManifestForm from '../containers/ManifestForm'; import ManifestListItem from '../containers/ManifestListItem'; @@ -18,6 +20,17 @@ import MiradorMenuButton from '../containers/MiradorMenuButton'; import { IIIFDropTarget } from './IIIFDropTarget'; import { PluginHook } from './PluginHook'; +const StyledWorkspaceAdd = styled('div')(() => ({ + boxSizing: 'border-box', + height: '100%', + overflowX: 'hidden', + overflowY: 'auto', +})); + +const StyledMiradorMenuButton = styled(MiradorMenuButton)(() => ({ + marginLeft: -12, + marginRight: 20, +})); /** * An area for managing manifests and adding them to workspace * @memberof Workspace @@ -75,7 +88,7 @@ export class WorkspaceAdd extends Component { */ render() { const { - catalog, setWorkspaceAddVisibility, t, classes, + catalog, setWorkspaceAddVisibility, t, } = this.props; const { addResourcesOpen } = this.state; @@ -91,7 +104,7 @@ export class WorkspaceAdd extends Component { return ( <IIIFDropTarget onDrop={this.handleDrop}> - <div ref={this.ref} className={classNames(ns('workspace-add'), classes.workspaceAdd)}> + <StyledWorkspaceAdd ref={this.ref} className={classNames(ns('workspace-add'))}> {catalog.length < 1 ? ( <Grid alignItems="center" @@ -114,8 +127,8 @@ export class WorkspaceAdd extends Component { </Grid> </Grid> ) : ( - <Paper className={classes.list}> - <Typography variant="srOnly" component="h1">{t('miradorResources')}</Typography> + <Paper sx={{ margin: 2 }}> + <Typography style={visuallyHidden} component="h1">{t('miradorResources')}</Typography> <PluginHook {...this.props} /> <List disablePadding> {manifestList} @@ -125,7 +138,12 @@ export class WorkspaceAdd extends Component { <Fab variant="extended" disabled={addResourcesOpen} - className={classNames(classes.fab, ns('add-resource-button'))} + sx={(theme) => ({ + bottom: theme.spacing(2), + position: 'absolute', + right: theme.spacing(2), + })} + className={classNames(ns('add-resource-button'))} color="primary" onClick={() => (this.setAddResourcesVisibility(true))} > @@ -134,10 +152,18 @@ export class WorkspaceAdd extends Component { </Fab> <Drawer - className={classNames({ - [classes.displayNone]: !addResourcesOpen, + sx={theme => ({ + '.MuiDrawer-paper': { + borderTop: '0', + left: '0', + [theme.breakpoints.up('sm')]: { + left: '65px', + }, + }, + ...(!addResourcesOpen && { + display: 'none', + }), })} - classes={{ paper: classes.paper }} variant="persistent" anchor="bottom" open={addResourcesOpen} @@ -148,18 +174,25 @@ export class WorkspaceAdd extends Component { }} > <Paper - className={classes.form} + sx={{ + left: '0', + marginTop: 6, + paddingBottom: 2, + paddingLeft: { sm: 3, xs: 2 }, + paddingRight: { sm: 3, xs: 2 }, + paddingTop: 2, + right: '0', + }} > - <AppBar position="absolute" color="primary" onClick={() => (this.setAddResourcesVisibility(false))}> + <AppBar position="absolute" color="primary" enableColorOnDark onClick={() => (this.setAddResourcesVisibility(false))}> <Toolbar variant="dense"> - <MiradorMenuButton + <StyledMiradorMenuButton aria-label={t('closeAddResourceForm')} - className={classes.menuButton} color="inherit" > <ExpandMoreIcon /> - </MiradorMenuButton> - <Typography variant="h2" noWrap color="inherit" className={classes.typographyBody}> + </StyledMiradorMenuButton> + <Typography variant="h2" noWrap color="inherit" sx={{ flexGrow: 1 }}> {t('addResource')} </Typography> </Toolbar> @@ -171,7 +204,7 @@ export class WorkspaceAdd extends Component { /> </Paper> </Drawer> - </div> + </StyledWorkspaceAdd> </IIIFDropTarget> ); } @@ -183,7 +216,6 @@ WorkspaceAdd.propTypes = { manifestId: PropTypes.string.isRequired, provider: PropTypes.string, })), - classes: PropTypes.objectOf(PropTypes.string), setWorkspaceAddVisibility: PropTypes.func.isRequired, t: PropTypes.func, }; @@ -191,6 +223,5 @@ WorkspaceAdd.propTypes = { WorkspaceAdd.defaultProps = { addResource: () => {}, catalog: [], - classes: {}, t: key => key, }; diff --git a/src/components/WorkspaceAddButton.js b/src/components/WorkspaceAddButton.js index b80ec8136911b0e8a34a16b74ab6c748de3ce6f2..b50679226e4082364041ce4eb77c73b56d1579ca 100644 --- a/src/components/WorkspaceAddButton.js +++ b/src/components/WorkspaceAddButton.js @@ -1,9 +1,14 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import Fab from '@material-ui/core/Fab'; -import Tooltip from '@material-ui/core/Tooltip'; -import AddIcon from '@material-ui/icons/AddSharp'; -import CloseIcon from '@material-ui/icons/CloseSharp'; +import Fab from '@mui/material/Fab'; +import Tooltip from '@mui/material/Tooltip'; +import AddIcon from '@mui/icons-material/AddSharp'; +import CloseIcon from '@mui/icons-material/CloseSharp'; +import { styled } from '@mui/material/styles'; + +const Root = styled(Fab, { name: 'WorkspaceAddButton', slot: 'root' })(({ theme }) => ({ + marginBottom: theme.spacing(1), +})); /** */ @@ -14,22 +19,19 @@ export class WorkspaceAddButton extends Component { */ render() { const { - classes, t, setWorkspaceAddVisibility, isWorkspaceAddVisible, useExtendedFab, + t, setWorkspaceAddVisibility, isWorkspaceAddVisible, useExtendedFab, } = this.props; return ( <Tooltip title={isWorkspaceAddVisible ? t('closeAddResourceMenu') : t('addResource')}> - <Fab + <Root size="medium" color="primary" id="addBtn" - disableRipple aria-label={ isWorkspaceAddVisible ? t('closeAddResourceMenu') : ((useExtendedFab && t('startHere')) || t('addResource')) } - className={classes.fab} - classes={{ primary: classes.fabPrimary, secondary: classes.fabSecondary }} variant={useExtendedFab ? 'extended' : 'circular'} onClick={() => { setWorkspaceAddVisibility(!isWorkspaceAddVisible); }} > @@ -39,14 +41,13 @@ export class WorkspaceAddButton extends Component { : <AddIcon /> } { useExtendedFab && t('startHere') } - </Fab> + </Root> </Tooltip> ); } } WorkspaceAddButton.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, isWorkspaceAddVisible: PropTypes.bool, setWorkspaceAddVisibility: PropTypes.func.isRequired, t: PropTypes.func, diff --git a/src/components/WorkspaceArea.js b/src/components/WorkspaceArea.js index b6e2d10ba71a2fff9a5744cb89acb484e76acdb5..16200f8d817f18c107737aa30cdf3f7703f082fc 100644 --- a/src/components/WorkspaceArea.js +++ b/src/components/WorkspaceArea.js @@ -1,6 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; +import { styled, lighten, darken } from '@mui/material/styles'; import ErrorDialog from '../containers/ErrorDialog'; import WorkspaceControlPanel from '../containers/WorkspaceControlPanel'; import Workspace from '../containers/Workspace'; @@ -8,6 +8,29 @@ import WorkspaceAdd from '../containers/WorkspaceAdd'; import BackgroundPluginArea from '../containers/BackgroundPluginArea'; import ns from '../config/css-ns'; +const Root = styled('div', { name: 'WorkspaceArea', slot: 'root' })(({ theme }) => { + const getBackgroundColor = theme.palette.mode === 'light' ? darken : lighten; + + return { + background: getBackgroundColor(theme.palette.shades.light, 0.1), + bottom: 0, + display: 'flex', + flexDirection: 'column', + left: 0, + position: 'absolute', + right: 0, + top: 0, + [theme.breakpoints.up('sm')]: { + flexDirection: 'row', + }, + }; +}); + +const ViewerArea = styled('main', { name: 'WorkspaceArea', slot: 'viewer' })(() => ({ + flexGrow: 1, + position: 'relative', +})); + /** * This is the top level Mirador component. * @prop {Object} manifests @@ -20,7 +43,6 @@ export class WorkspaceArea extends Component { render() { const { areaRef, - classes, controlPanelVariant, isWorkspaceAddVisible, isWorkspaceControlPanelVisible, @@ -29,13 +51,13 @@ export class WorkspaceArea extends Component { } = this.props; return ( - <> + <Root ownerState={this.props}> { isWorkspaceControlPanelVisible && <WorkspaceControlPanel variant={controlPanelVariant} /> } - <main - className={classNames(classes.viewer, ns('viewer'))} + <ViewerArea + className={ns('viewer')} lang={lang} aria-label={t('workspace')} {...(areaRef ? { ref: areaRef } : {})} @@ -47,15 +69,14 @@ export class WorkspaceArea extends Component { } <ErrorDialog /> <BackgroundPluginArea /> - </main> - </> + </ViewerArea> + </Root> ); } } WorkspaceArea.propTypes = { areaRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - classes: PropTypes.objectOf(PropTypes.string).isRequired, controlPanelVariant: PropTypes.string, isWorkspaceAddVisible: PropTypes.bool, isWorkspaceControlPanelVisible: PropTypes.bool.isRequired, diff --git a/src/components/WorkspaceControlPanel.js b/src/components/WorkspaceControlPanel.js index 376558285d3ac7154425a555690f1a08220f5fc0..1331341330cc8ff29e1199eb1f21f98ad6b2b325 100644 --- a/src/components/WorkspaceControlPanel.js +++ b/src/components/WorkspaceControlPanel.js @@ -1,13 +1,59 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import classNames from 'classnames'; -import AppBar from '@material-ui/core/AppBar'; -import Toolbar from '@material-ui/core/Toolbar'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; import WorkspaceAddButton from '../containers/WorkspaceAddButton'; import WorkspaceControlPanelButtons from '../containers/WorkspaceControlPanelButtons'; import Branding from '../containers/Branding'; import ns from '../config/css-ns'; +const Root = styled(AppBar, { name: 'WorkspaceControlPanel', slot: 'root' })(({ ownerState, theme }) => ({ + display: 'flex', + height: 64, + padding: theme.spacing(1), + paddingBottom: 0, + position: 'relative', + [theme.breakpoints.up('sm')]: { + height: 'auto', + width: ownerState.variant === 'wide' ? 'auto' : 64, + }, + ...(ownerState.variant === 'wide' && { + width: 'auto', + }), +})); + +const StyledToolbar = styled(Toolbar, { name: 'WorkspaceControlPanel', slot: 'toolbar' })(({ theme }) => ({ + display: 'flex', + flexGrow: 1, + justifyContent: 'space-between', + [theme.breakpoints.up('sm')]: { + flexDirection: 'column', + justifyContent: 'flex-start', + minHeight: 0, + }, +})); + +const StyledWorkspaceButtons = styled('div', { name: 'WorkspaceControlPanel', slot: 'buttonArea' })(({ theme }) => ({ + [theme.breakpoints.up('sm')]: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + marginBottom: theme.spacing(1), + marginTop: theme.spacing(1), + }, +})); + +const StyledBranding = styled(Branding, { name: 'WorkspaceControlPanel', slot: 'branding' })(({ theme }) => ({ + [theme.breakpoints.up('xs')]: { + display: 'none', + }, + [theme.breakpoints.up('sm')]: { + display: 'flex', + }, +})); + /** * Provides the panel responsible for controlling the entire workspace */ @@ -17,34 +63,36 @@ export class WorkspaceControlPanel extends Component { * @return {String} - HTML markup for the component */ render() { - const { classes, t, variant } = this.props; + const { t, variant } = this.props; return ( - <AppBar - className={classNames(classes.root, ns('workspace-control-panel'), variant === 'wide' ? classes.wide : null)} + <Root + ownerState={this.props} + className={classNames(ns('workspace-control-panel'))} color="default" + enableColorOnDark position="absolute" component="nav" aria-label={t('workspaceNavigation')} > - <Toolbar disableGutters className={classes.toolbar}> + <StyledToolbar + disableGutters + > <WorkspaceAddButton /> - <div className={classes.workspaceButtons}> + <StyledWorkspaceButtons> <WorkspaceControlPanelButtons /> - </div> - </Toolbar> - <Branding className={classes.branding} t={t} variant={variant} /> - </AppBar> + </StyledWorkspaceButtons> + </StyledToolbar> + <StyledBranding t={t} variant={variant} /> + </Root> ); } } WorkspaceControlPanel.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), t: PropTypes.func.isRequired, variant: PropTypes.oneOf(['default', 'wide']), }; WorkspaceControlPanel.defaultProps = { - classes: {}, variant: 'default', }; diff --git a/src/components/WorkspaceControlPanelButtons.js b/src/components/WorkspaceControlPanelButtons.js index b07adfc54298955b08cbfc1ffd60299bbad125f9..34cbf840ea9a1810a90e13bbbae9776b684cfea5 100644 --- a/src/components/WorkspaceControlPanelButtons.js +++ b/src/components/WorkspaceControlPanelButtons.js @@ -1,5 +1,4 @@ import { Component } from 'react'; -import PropTypes from 'prop-types'; import FullScreenButton from '../containers/FullScreenButton'; import WorkspaceMenuButton from '../containers/WorkspaceMenuButton'; import WorkspaceOptionsButton from '../containers/WorkspaceOptionsButton'; @@ -16,23 +15,14 @@ export class WorkspaceControlPanelButtons extends Component { * @return {type} description */ render() { - const { classes } = this.props; return ( <> <WindowListButton /> <WorkspaceMenuButton /> <WorkspaceOptionsButton /> - <FullScreenButton className={classes.ctrlBtn} /> + <FullScreenButton /> <PluginHook {...this.props} /> </> ); } } - -WorkspaceControlPanelButtons.propTypes = { - classes: PropTypes.objectOf(PropTypes.string), -}; - -WorkspaceControlPanelButtons.defaultProps = { - classes: {}, -}; diff --git a/src/components/WorkspaceDialog.js b/src/components/WorkspaceDialog.js new file mode 100644 index 0000000000000000000000000000000000000000..0928db6502f527924127ff0032422de6fa8aed1b --- /dev/null +++ b/src/components/WorkspaceDialog.js @@ -0,0 +1,13 @@ +import { + Dialog, +} from '@mui/material'; +import { styled } from '@mui/material/styles'; + +export const WorkspaceDialog = styled(Dialog, { name: 'WorkspaceDialog', slot: 'root' })(({ theme, variant }) => ({ + '& .MuiDialogTitle-root': theme.unstable_sx({ typography: 'h2' }), + ...(variant === 'menu' && { + '& .MuiDialogContent-root': { + padding: 0, + }, + }), +})); diff --git a/src/components/WorkspaceElastic.js b/src/components/WorkspaceElastic.js index 01c2e79d2beba8f64d1d76b0607a3a2d69569a5f..f871ab9983bac48fd8445862cfda64de337265b3 100644 --- a/src/components/WorkspaceElastic.js +++ b/src/components/WorkspaceElastic.js @@ -1,11 +1,29 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import { Rnd } from 'react-rnd'; import ResizeObserver from 'react-resize-observer'; -import classNames from 'classnames'; import WorkspaceElasticWindow from '../containers/WorkspaceElasticWindow'; import ns from '../config/css-ns'; +const Root = styled('div', { name: 'WorkspaceElastic', slot: 'root' })({ + height: '100%', + position: 'relative', + width: '100%', +}); + +const StyledRnd = styled(Rnd)({ + boxSizing: 'border-box', + margin: 0, + position: 'absolute', + transitionDuration: '.7s', + // order matters + // eslint-disable-next-line sort-keys + '&.react-draggable-dragging': { + transitionDuration: 'unset', + }, +}); + /** * Represents a work area that contains any number of windows * @memberof Workspace @@ -16,7 +34,6 @@ class WorkspaceElastic extends Component { */ render() { const { - classes, workspace, elasticLayout, setWorkspaceViewportDimensions, @@ -28,13 +45,13 @@ class WorkspaceElastic extends Component { const offsetY = workspace.height / 2; return ( - <div style={{ height: '100%', position: 'relative', width: '100%' }}> + <Root> <ResizeObserver onReflow={() => {}} onResize={(rect) => { setWorkspaceViewportDimensions(rect); }} /> - <Rnd + <StyledRnd size={{ height: workspace.height, width: workspace.width, @@ -56,7 +73,7 @@ class WorkspaceElastic extends Component { setWorkspaceViewportPosition({ x: -1 * d.x - offsetX, y: -1 * d.y - offsetY }); }} cancel={`.${ns('window')}`} - className={classNames(classes.workspace, ns('workspace'))} + className={ns('workspace')} disableDragging={!workspace.draggingEnabled} > { @@ -67,14 +84,13 @@ class WorkspaceElastic extends Component { /> )) } - </Rnd> - </div> + </StyledRnd> + </Root> ); } } WorkspaceElastic.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, elasticLayout: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types setWorkspaceViewportDimensions: PropTypes.func.isRequired, setWorkspaceViewportPosition: PropTypes.func.isRequired, diff --git a/src/components/WorkspaceElasticWindow.js b/src/components/WorkspaceElasticWindow.js index 54224913c4c213bf3f2f8058f9f8e05e520b6113..dea8c87b6ad8318300aae46219ba30f763da09af 100644 --- a/src/components/WorkspaceElasticWindow.js +++ b/src/components/WorkspaceElasticWindow.js @@ -1,9 +1,14 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; import { Rnd } from 'react-rnd'; import Window from '../containers/Window'; import ns from '../config/css-ns'; +const StyledRnd = styled(Rnd)(({ focused, theme }) => ({ + zIndex: focused ? theme.zIndex.modal - 1 : 'auto', +})); + /** * Represents a work area that contains any number of windows * @memberof Workspace @@ -27,7 +32,9 @@ class WorkspaceElasticWindow extends Component { const offsetY = workspace.height / 2; return ( - <Rnd + <StyledRnd + focused={focused} + className={focused ? classes.focused : undefined} key={`${layout.windowId}-${workspace.id}`} size={{ height: layout.height + companionWindowDimensions.height, @@ -52,14 +59,11 @@ class WorkspaceElasticWindow extends Component { }} dragHandleClassName={ns('window-top-bar')} cancel={`.${ns('window-menu-btn')}`} - className={ - focused ? classes.focused : null - } > <Window windowId={layout.windowId} /> - </Rnd> + </StyledRnd> ); } } diff --git a/src/components/WorkspaceExport.js b/src/components/WorkspaceExport.js index 6e2a1b18a6fe1740125174c21cfd96a907c83154..d6a7962bd3f262c099ad38869a0a1f859bf536ed 100644 --- a/src/components/WorkspaceExport.js +++ b/src/components/WorkspaceExport.js @@ -1,19 +1,19 @@ import { Component } from 'react'; -import Button from '@material-ui/core/Button'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogTitle from '@material-ui/core/DialogTitle'; -import DialogContent from '@material-ui/core/DialogContent'; -import Typography from '@material-ui/core/Typography'; -import Snackbar from '@material-ui/core/Snackbar'; -import IconButton from '@material-ui/core/IconButton'; -import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import CloseIcon from '@material-ui/icons/Close'; -import Accordion from '@material-ui/core/Accordion'; -import AccordionSummary from '@material-ui/core/AccordionSummary'; -import AccordionDetails from '@material-ui/core/AccordionDetails'; +import Button from '@mui/material/Button'; +import DialogActions from '@mui/material/DialogActions'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import Typography from '@mui/material/Typography'; +import Snackbar from '@mui/material/Snackbar'; +import IconButton from '@mui/material/IconButton'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import CloseIcon from '@mui/icons-material/Close'; +import Accordion from '@mui/material/Accordion'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; import PropTypes from 'prop-types'; import { CopyToClipboard } from 'react-copy-to-clipboard'; +import { WorkspaceDialog } from './WorkspaceDialog'; /** */ @@ -54,7 +54,7 @@ export class WorkspaceExport extends Component { */ render() { const { - children, classes, container, open, t, + children, container, open, t, } = this.props; const { copied } = this.state; @@ -79,7 +79,7 @@ export class WorkspaceExport extends Component { } return ( - <Dialog + <WorkspaceDialog id="workspace-export" container={container} open={open} @@ -88,19 +88,18 @@ export class WorkspaceExport extends Component { fullWidth maxWidth="sm" > - <DialogTitle id="form-dialog-title" disableTypography> - <Typography variant="h2">{t('downloadExport')}</Typography> + <DialogTitle id="form-dialog-title"> + {t('downloadExport')} </DialogTitle> <DialogContent> - <Accordion elevation={0}> + <Accordion elevation={2}> <AccordionSummary - classes={{ root: classes.accordionTitle }} expandIcon={<ExpandMoreIcon />} > <Typography variant="h4">{t('viewWorkspaceConfiguration')}</Typography> </AccordionSummary> - <AccordionDetails> + <AccordionDetails sx={{ overflow: 'scroll' }}> {children} <pre> {this.exportedState()} @@ -118,14 +117,13 @@ export class WorkspaceExport extends Component { <Button variant="contained" color="primary">{t('copy')}</Button> </CopyToClipboard> </DialogActions> - </Dialog> + </WorkspaceDialog> ); } } WorkspaceExport.propTypes = { children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string), container: PropTypes.object, // eslint-disable-line react/forbid-prop-types exportableState: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types handleClose: PropTypes.func.isRequired, @@ -135,7 +133,6 @@ WorkspaceExport.propTypes = { WorkspaceExport.defaultProps = { children: null, - classes: {}, container: null, open: false, t: key => key, diff --git a/src/components/WorkspaceImport.js b/src/components/WorkspaceImport.js index b0f2c347274ce179f6ddb02e6e9982896e238bad..3a50c482871fa14b6be568a083e83edea289c799 100644 --- a/src/components/WorkspaceImport.js +++ b/src/components/WorkspaceImport.js @@ -1,13 +1,12 @@ import { Component } from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogTitle from '@mui/material/DialogTitle'; import PropTypes from 'prop-types'; import { DialogActions, TextField, - Typography, -} from '@material-ui/core'; -import Button from '@material-ui/core/Button'; +} from '@mui/material'; +import Button from '@mui/material/Button'; +import { WorkspaceDialog } from './WorkspaceDialog'; import ScrollIndicatedDialogContent from '../containers/ScrollIndicatedDialogContent'; /** @@ -65,7 +64,7 @@ export class WorkspaceImport extends Component { } = this.props; return ( - <Dialog + <WorkspaceDialog aria-labelledby="workspace-import-title" id="workspace-import" onClose={handleClose} @@ -73,8 +72,8 @@ export class WorkspaceImport extends Component { fullWidth maxWidth="sm" > - <DialogTitle id="workspace-import-title" disableTypography> - <Typography variant="h2">{t('importWorkspace')}</Typography> + <DialogTitle id="workspace-import-title"> + {t('importWorkspace')} </DialogTitle> <ScrollIndicatedDialogContent> <TextField @@ -84,19 +83,23 @@ export class WorkspaceImport extends Component { onChange={this.handleChange} minRows={15} variant="filled" - inputProps={{ autoFocus: 'autofocus', className: classes.textInput }} + sx={{ + '& .MuiInputBase-input': { fontFamily: 'monospace' }, + width: '100%', + }} + inputProps={{ autoFocus: 'autofocus' }} helperText={t('importWorkspaceHint')} /> </ScrollIndicatedDialogContent> <DialogActions> - <Button className={classes.cancelBtn} onClick={handleClose}> + <Button onClick={handleClose}> {t('cancel')} </Button> <Button color="primary" onClick={this.handleImportConfig} variant="contained"> {t('import')} </Button> </DialogActions> - </Dialog> + </WorkspaceDialog> ); } } diff --git a/src/components/WorkspaceMenu.js b/src/components/WorkspaceMenu.js index 65e01ba2ed6dca3fff716f0c250a32cb0e9ea8d4..c6e9a191a60a864ef82114775e7d57a413c3e394 100644 --- a/src/components/WorkspaceMenu.js +++ b/src/components/WorkspaceMenu.js @@ -1,7 +1,7 @@ import { Component } from 'react'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import Typography from '@material-ui/core/Typography'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; import PropTypes from 'prop-types'; import LanguageSettings from '../containers/LanguageSettings'; import { NestedMenu } from './NestedMenu'; @@ -71,8 +71,10 @@ export class WorkspaceMenu extends Component { t, showZoomControls, toggleZoomControls, - ...menuProps + ...rest } = this.props; + const menuProps = { ...rest }; + delete menuProps.tReady; const { changeTheme, diff --git a/src/components/WorkspaceMenuButton.js b/src/components/WorkspaceMenuButton.js index 71d8db245f5f7eeb25313dd953e8230be9058d6b..1ff00786511d9794a21b77b5230d4682a110fbe5 100644 --- a/src/components/WorkspaceMenuButton.js +++ b/src/components/WorkspaceMenuButton.js @@ -1,7 +1,6 @@ import { Component } from 'react'; -import SettingsIcon from '@material-ui/icons/SettingsSharp'; +import SettingsIcon from '@mui/icons-material/SettingsSharp'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; import WorkspaceMenu from '../containers/WorkspaceMenu'; import MiradorMenuButton from '../containers/MiradorMenuButton'; @@ -46,7 +45,7 @@ export class WorkspaceMenuButton extends Component { * @return */ render() { - const { classes, t } = this.props; + const { t } = this.props; const { anchorEl, open } = this.state; return ( @@ -55,7 +54,7 @@ export class WorkspaceMenuButton extends Component { aria-haspopup="true" aria-label={t('workspaceMenu')} aria-owns={open ? 'workspace-menu' : undefined} - className={classNames(classes.ctrlBtn, (open ? classes.ctrlBtnSelected : null))} + selected={open} id="menuBtn" onClick={this.handleMenuClick} > @@ -73,7 +72,6 @@ export class WorkspaceMenuButton extends Component { } WorkspaceMenuButton.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, t: PropTypes.func, }; diff --git a/src/components/WorkspaceMosaic.js b/src/components/WorkspaceMosaic.js index aad63fe5014fa3d760b79f87000ee56a7bb87702..0ac4faa5725b56d46a6691b3f1ba2c853b1d2191 100644 --- a/src/components/WorkspaceMosaic.js +++ b/src/components/WorkspaceMosaic.js @@ -1,5 +1,7 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import GlobalStyles from '@mui/material/GlobalStyles'; import { DndContext } from 'react-dnd'; import { Mosaic, MosaicWindow, getLeaves, createBalancedTreeFromLeaves, @@ -10,6 +12,22 @@ import classNames from 'classnames'; import MosaicRenderPreview from '../containers/MosaicRenderPreview'; import Window from '../containers/Window'; import MosaicLayout from '../lib/MosaicLayout'; +import globalReactMosaicStyles from '../styles/react-mosaic-component'; + +const StyledMosaic = styled(Mosaic)({ + '& .mosaic-preview': { + boxShadow: 'none', + }, + '& .mosaic-tile': { + boxShadow: '0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .2), 0 2px 1px -1px rgba(0, 0, 0, .2)', + }, + '& .mosaic-window': { + boxShadow: 'none', + }, + '& .mosaic-window-toolbar': { + display: 'none !important', + }, +}); /** * Represents a work area that contains any number of windows @@ -143,18 +161,21 @@ export class WorkspaceMosaic extends Component { /** */ render() { - const { layout, classes } = this.props; + const { layout } = this.props; return ( <DndContext.Consumer> {(ctx) => ( - <Mosaic - dragAndDropManager={ctx.dragDropManager} - renderTile={this.tileRenderer} - initialValue={layout || this.determineWorkspaceLayout()} - onChange={this.mosaicChange} - className={classNames('mirador-mosaic', classes.root)} - zeroStateView={this.zeroStateView} - /> + <> + <GlobalStyles styles={{ ...globalReactMosaicStyles }} /> + <StyledMosaic + dragAndDropManager={ctx.dragDropManager} + renderTile={this.tileRenderer} + initialValue={layout || this.determineWorkspaceLayout()} + onChange={this.mosaicChange} + className={classNames('mirador-mosaic')} + zeroStateView={this.zeroStateView} + /> + </> )} </DndContext.Consumer> ); @@ -162,7 +183,6 @@ export class WorkspaceMosaic extends Component { } WorkspaceMosaic.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, layout: PropTypes.oneOfType( [PropTypes.object, PropTypes.string], ), // eslint-disable-line react/forbid-prop-types diff --git a/src/components/WorkspaceOptionsButton.js b/src/components/WorkspaceOptionsButton.js index bfde64bfb78daff64e4517f63240ff5261c21cd3..ad549551f2fe45cc99475b120ed0f6f57c317274 100644 --- a/src/components/WorkspaceOptionsButton.js +++ b/src/components/WorkspaceOptionsButton.js @@ -1,7 +1,6 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import MoreHorizontalIcon from '@material-ui/icons/MoreHorizSharp'; +import MoreHorizontalIcon from '@mui/icons-material/MoreHorizSharp'; import MiradorMenuButton from '../containers/MiradorMenuButton'; import WorkspaceOptionsMenu from '../containers/WorkspaceOptionsMenu'; @@ -46,21 +45,18 @@ export class WorkspaceOptionsButton extends Component { * Returns the rendered component */ render() { - const { classes, t } = this.props; + const { t } = this.props; const { anchorEl, open } = this.state; return ( <> <MiradorMenuButton aria-label={t('workspaceOptions')} - className={ - classNames(classes.ctrlBtn, (open ? classes.ctrlBtnSelected : null)) - } onClick={this.handleMenuClick} + selected={open} > <MoreHorizontalIcon /> </MiradorMenuButton> - <WorkspaceOptionsMenu anchorEl={anchorEl} handleClose={this.handleMenuClose} @@ -72,6 +68,5 @@ export class WorkspaceOptionsButton extends Component { } WorkspaceOptionsButton.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, t: PropTypes.func.isRequired, }; diff --git a/src/components/WorkspaceOptionsMenu.js b/src/components/WorkspaceOptionsMenu.js index 6da23860f7748820f2ab7b2dd08b0028877f17bb..75aac27a94e95a337614cd8126e09cd823524530 100644 --- a/src/components/WorkspaceOptionsMenu.js +++ b/src/components/WorkspaceOptionsMenu.js @@ -1,11 +1,11 @@ import { Component } from 'react'; import PropTypes from 'prop-types'; -import ImportIcon from '@material-ui/icons/Input'; -import SaveAltIcon from '@material-ui/icons/SaveAltSharp'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import Typography from '@material-ui/core/Typography'; +import ImportIcon from '@mui/icons-material/Input'; +import SaveAltIcon from '@mui/icons-material/SaveAltSharp'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import Typography from '@mui/material/Typography'; import WorkspaceExport from '../containers/WorkspaceExport'; import WorkspaceImport from '../containers/WorkspaceImport'; import { PluginHook } from './PluginHook'; diff --git a/src/components/WorkspaceSelectionDialog.js b/src/components/WorkspaceSelectionDialog.js index f1efbf48d942d886ce160885882dfb157478282b..e606ef872fe8974f1ceba61c6e5c3f53afdd7922 100644 --- a/src/components/WorkspaceSelectionDialog.js +++ b/src/components/WorkspaceSelectionDialog.js @@ -1,18 +1,23 @@ import { Component } from 'react'; -import Dialog from '@material-ui/core/Dialog'; -import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogTitle from '@mui/material/DialogTitle'; import { Card, CardContent, MenuList, MenuItem, Typography, -} from '@material-ui/core'; +} from '@mui/material'; import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import { WorkspaceDialog } from './WorkspaceDialog'; import WorkspaceTypeElasticIcon from './icons/WorkspaceTypeElasticIcon'; import WorkspaceTypeMosaicIcon from './icons/WorkspaceTypeMosaicIcon'; import ScrollIndicatedDialogContent from '../containers/ScrollIndicatedDialogContent'; +const StyledDetails = styled('div')(() => ({ + display: 'flex', + flexDirection: 'column', +})); /** */ export class WorkspaceSelectionDialog extends Component { @@ -42,80 +47,133 @@ export class WorkspaceSelectionDialog extends Component { */ render() { const { - classes, container, handleClose, open, children, t, workspaceType, + container, handleClose, open, children, t, workspaceType, } = this.props; return ( - <Dialog + <WorkspaceDialog aria-labelledby="workspace-selection-dialog-title" container={container} id="workspace-selection-dialog" onClose={handleClose} open={open} > - <DialogTitle id="workspace-selection-dialog-title" disableTypography> - <Typography variant="h2">{t('workspaceSelectionTitle')}</Typography> + <DialogTitle id="workspace-selection-dialog-title"> + {t('workspaceSelectionTitle')} </DialogTitle> <ScrollIndicatedDialogContent> {children} <MenuList - classes={{ root: classes.list }} + sx={{ + '&active': { + outline: 'none', + }, + '&focus': { + outline: 'none', + }, + outline: 'none', + }} selected={workspaceType} autoFocusItem > <MenuItem - className={classes.menuItem} + sx={{ + height: 'auto', + overflow: 'auto', + whiteSpace: 'inherit', + }} onClick={() => this.handleWorkspaceTypeChange('elastic')} selected={workspaceType === 'elastic'} value="elastic" > - <Card className={classes.card}> + <Card sx={{ + backgroundColor: 'transparent', + borderRadius: '0', + boxShadow: '0 0 transparent', + display: 'flex', + }} + > <WorkspaceTypeElasticIcon - className={classes.svgIcon} + sx={{ + flexShrink: 0, + height: '90px', + width: '120px', + }} viewBox="0 0 120 90" /> - <div className={classes.details}> + <StyledDetails> <CardContent - classes={{ root: classes.root }} - className={classes.content} + sx={{ + '&.MuiCardContent-root': { + '&:last-child': { + paddingBottom: '12px', + }, + paddingBottom: 0, + paddingTop: 0, + textAlign: 'left', + }, + flex: '1 0 auto', + }} > - <Typography className={classes.headline} component="p" variant="h3">{t('elastic')}</Typography> + <Typography sx={{ paddingBottom: '6px' }} component="p" variant="h3">{t('elastic')}</Typography> <Typography variant="body1">{t('elasticDescription')}</Typography> </CardContent> - </div> + </StyledDetails> </Card> </MenuItem> <MenuItem - className={classes.menuItem} + sx={{ + height: 'auto', + overflow: 'auto', + whiteSpace: 'inherit', + }} onClick={() => this.handleWorkspaceTypeChange('mosaic')} selected={workspaceType === 'mosaic'} value="mosaic" > - <Card className={classes.card}> + <Card sx={{ + backgroundColor: 'transparent', + borderRadius: '0', + boxShadow: '0 0 transparent', + display: 'flex', + }} + > <WorkspaceTypeMosaicIcon - className={classes.svgIcon} + sx={{ + flexShrink: 0, + height: '90px', + width: '120px', + }} viewBox="0 0 120 90" /> - <div className={classes.details}> + <StyledDetails> <CardContent - className={classes.content} - classes={{ root: classes.root }} + sx={{ + '&.MuiCardContent-root': { + '&:last-child': { + paddingBottom: '12px', + }, + paddingBottom: 0, + paddingTop: 0, + textAlign: 'left', + }, + flex: '1 0 auto', + }} > - <Typography className={classes.headline} component="p" variant="h3">{t('mosaic')}</Typography> + <Typography sx={{ paddingBottom: '6px' }} component="p" variant="h3">{t('mosaic')}</Typography> <Typography variant="body1">{t('mosaicDescription')}</Typography> </CardContent> - </div> + </StyledDetails> </Card> </MenuItem> </MenuList> </ScrollIndicatedDialogContent> - </Dialog> + </WorkspaceDialog> ); } } WorkspaceSelectionDialog.propTypes = { children: PropTypes.node, - classes: PropTypes.objectOf(PropTypes.string).isRequired, container: PropTypes.object, // eslint-disable-line react/forbid-prop-types handleClose: PropTypes.func.isRequired, open: PropTypes.bool, diff --git a/src/components/ZoomControls.js b/src/components/ZoomControls.js index 133c9950a8d8facc4598ac95c74a034d4e1d1e7a..19dc0002291173fe8c4de3467cd11a91b4519b77 100644 --- a/src/components/ZoomControls.js +++ b/src/components/ZoomControls.js @@ -1,10 +1,17 @@ import { Component } from 'react'; -import AddCircleIcon from '@material-ui/icons/AddCircleOutlineSharp'; -import RemoveCircleIcon from '@material-ui/icons/RemoveCircleOutlineSharp'; +import AddCircleIcon from '@mui/icons-material/AddCircleOutlineSharp'; +import RemoveCircleIcon from '@mui/icons-material/RemoveCircleOutlineSharp'; +import { styled } from '@mui/material/styles'; import PropTypes from 'prop-types'; import RestoreZoomIcon from './icons/RestoreZoomIcon'; import MiradorMenuButton from '../containers/MiradorMenuButton'; +const StyledZoomControlsWrapper = styled('div')({ + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', +}); + /** */ export class ZoomControls extends Component { @@ -46,17 +53,11 @@ export class ZoomControls extends Component { */ render() { const { - displayDivider, showZoomControls, classes, t, zoomToWorld, + t, zoomToWorld, } = this.props; - if (!showZoomControls) { - return ( - <> - </> - ); - } return ( - <div className={classes.zoom_controls}> + <StyledZoomControlsWrapper> <MiradorMenuButton aria-label={t('zoomIn')} onClick={this.handleZoomInClick}> <AddCircleIcon /> </MiradorMenuButton> @@ -66,16 +67,12 @@ export class ZoomControls extends Component { <MiradorMenuButton aria-label={t('zoomReset')} onClick={() => zoomToWorld(false)}> <RestoreZoomIcon /> </MiradorMenuButton> - {displayDivider && <span className={classes.divider} />} - </div> + </StyledZoomControlsWrapper> ); } } ZoomControls.propTypes = { - classes: PropTypes.objectOf(PropTypes.string).isRequired, - displayDivider: PropTypes.bool, - showZoomControls: PropTypes.bool, t: PropTypes.func, updateViewport: PropTypes.func, viewer: PropTypes.shape({ @@ -88,8 +85,6 @@ ZoomControls.propTypes = { }; ZoomControls.defaultProps = { - displayDivider: true, - showZoomControls: false, t: key => key, updateViewport: () => {}, viewer: {}, diff --git a/src/components/icons/BookViewIcon.js b/src/components/icons/BookViewIcon.js index a6668b1f30dd3f2334232b6a78defa451bf7e982..d26b9fe3ccb9071786167e894586d2f44d406e24 100644 --- a/src/components/icons/BookViewIcon.js +++ b/src/components/icons/BookViewIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * BookViewIcon ~ diff --git a/src/components/icons/CanvasIndexIcon.js b/src/components/icons/CanvasIndexIcon.js index 8d5af98b10ffc6d5a8b644a75b88260ccf3ef5af..2bac4808d03e9a90c20f4dd4531c8a5aa99cfb90 100644 --- a/src/components/icons/CanvasIndexIcon.js +++ b/src/components/icons/CanvasIndexIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * Render the canvas index svg diff --git a/src/components/icons/GalleryViewIcon.js b/src/components/icons/GalleryViewIcon.js index 9a410033404f1fb35a6260eae3892bb9f91f8d11..cccd9d2c711c176af48926783c844e35d6a35600 100644 --- a/src/components/icons/GalleryViewIcon.js +++ b/src/components/icons/GalleryViewIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * GalleryViewIcon ~ diff --git a/src/components/icons/MiradorIcon.js b/src/components/icons/MiradorIcon.js index 920256dd9bf3c856729973c4343eff41265b0d60..43301fe4cb9c8bdf808da08666d037a97db9ee0b 100644 --- a/src/components/icons/MiradorIcon.js +++ b/src/components/icons/MiradorIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * ThumbnailNavigationRightIcon ~ diff --git a/src/components/icons/RestoreZoomIcon.js b/src/components/icons/RestoreZoomIcon.js index 2005e6e89c5b61f851bcf398014e26581008eb7e..f1cf1e2d1e3eafc22b8eb6777115b7ea6ee0b898 100644 --- a/src/components/icons/RestoreZoomIcon.js +++ b/src/components/icons/RestoreZoomIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * RestoreZoomIcon ~ diff --git a/src/components/icons/ThumbnailNavigationBottomIcon.js b/src/components/icons/ThumbnailNavigationBottomIcon.js index 5c7080a787b68f4431e3f5b31b73d9996a859eb3..4ef2b95448cac052e7cd245fe94e6fcf4fcd7e36 100644 --- a/src/components/icons/ThumbnailNavigationBottomIcon.js +++ b/src/components/icons/ThumbnailNavigationBottomIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * ThumbnailNavigationBottomIcon ~ diff --git a/src/components/icons/ThumbnailNavigationRightIcon.js b/src/components/icons/ThumbnailNavigationRightIcon.js index 8f22b31d00ac81fffb763d4fc82e49c2eedc1ae6..6690b01d280f0e7b0362ca138f9cda8d4fbaa875 100644 --- a/src/components/icons/ThumbnailNavigationRightIcon.js +++ b/src/components/icons/ThumbnailNavigationRightIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * ThumbnailNavigationRightIcon ~ diff --git a/src/components/icons/WindowMaxIcon.js b/src/components/icons/WindowMaxIcon.js index 9590bbb180d5ac1bbb1b322dfadc7960604efb70..3de34b6bf5e3d9efffcf7eefe179c5b229157546 100644 --- a/src/components/icons/WindowMaxIcon.js +++ b/src/components/icons/WindowMaxIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * WindowMaxIcon ~ diff --git a/src/components/icons/WindowMinIcon.js b/src/components/icons/WindowMinIcon.js index 7a2ef3be9ca1ec7ffd926f0b5edad8f8b66b247c..3118b915f7e0fcd8f0a22fc1269e8bd9fa62f3ca 100644 --- a/src/components/icons/WindowMinIcon.js +++ b/src/components/icons/WindowMinIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * WindowMinIcon ~ diff --git a/src/components/icons/WindowOptionsIcon.js b/src/components/icons/WindowOptionsIcon.js index 3e365c29307ee2d02b5970e4a4253f84caa5f657..e87dd282539643d1c8049c9b2a44dd4ef37913c0 100644 --- a/src/components/icons/WindowOptionsIcon.js +++ b/src/components/icons/WindowOptionsIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * WindowMinIcon ~ diff --git a/src/components/icons/WorkspaceTypeElasticIcon.js b/src/components/icons/WorkspaceTypeElasticIcon.js index c817330897909d6a7daafa8fbb88d29074187a48..e1ee53918ad5afed6ff58cad42078605514b34e9 100644 --- a/src/components/icons/WorkspaceTypeElasticIcon.js +++ b/src/components/icons/WorkspaceTypeElasticIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * ElasticWorkspaceIcon ~ diff --git a/src/components/icons/WorkspaceTypeMosaicIcon.js b/src/components/icons/WorkspaceTypeMosaicIcon.js index 5cdb0c34a10bed5204c03410330c7439bf0894ee..c1724ee7e88ed1b7dbf86f150f5935dcd13cf648 100644 --- a/src/components/icons/WorkspaceTypeMosaicIcon.js +++ b/src/components/icons/WorkspaceTypeMosaicIcon.js @@ -1,4 +1,4 @@ -import SvgIcon from '@material-ui/core/SvgIcon'; +import SvgIcon from '@mui/material/SvgIcon'; /** * WorkspaceTypeMosaicIcon ~ diff --git a/src/config/settings.js b/src/config/settings.js index 5aa08c8f99e97afc8a1d755cbf2a2677def553f9..caaebe2e15e340a62508363a3947e9618805e314 100644 --- a/src/config/settings.js +++ b/src/config/settings.js @@ -12,7 +12,7 @@ export default { themes: { dark: { palette: { - type: 'dark', + mode: 'dark', primary: { main: '#4db6ac', }, @@ -28,13 +28,13 @@ export default { }, light: { palette: { - type: 'light', + mode: 'light', } } }, theme: { // Sets up a MaterialUI theme. See https://material-ui.com/customization/default-theme/ palette: { - type: 'light', + mode: 'light', primary: { main: '#1967d2', // Controls the color of the Add button and current window indicator }, @@ -61,6 +61,7 @@ export default { }, section_divider: 'rgba(0, 0, 0, 0.25)', annotations: { + chipBackground: '#e0e0e0', hidden: { globalAlpha: 0 }, default: { strokeStyle: '#00BFFF', globalAlpha: 1 }, hovered: { strokeStyle: '#BF00FF', globalAlpha: 1 }, @@ -167,51 +168,248 @@ export default { }, useNextVariants: true // set so that console deprecation warning is removed }, - overrides: { - MuiListSubheader: { - root: { - '&[role="presentation"]:focus': { - outline: 0, + components: { + MuiMenuItem: { + variants: [ + { + props: { variant: 'multiline' }, + style: { whiteSpace: 'normal' } }, - }, + ] }, - MuiTooltip: { // Overridden from https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Tooltip/Tooltip.js#L40-L70 - tooltipPlacementLeft: { - ['@media (min-width:600px)']: { - margin: 0, + CompanionWindow: { + styleOverrides: { + closeButton: { + order: 4, }, - }, - tooltipPlacementRight: { - ['@media (min-width:600px)']: { - margin: 0, + contents: { + overflowY: 'auto', + wordBreak: 'break-word', }, - }, - tooltipPlacementTop: { - ['@media (min-width:600px)']: { - margin: 0, + controls: ({ ownerState }) => ({ + alignItems: 'center', + display: 'flex', + flexFlow: 'row wrap', + flexGrow: 1, + justifyContent: (ownerState?.position === 'bottom' || ownerState?.position === 'far-bottom') ? 'flex-end' : 'flex-start', + minHeight: 48, + order: 3 + }), + positionButton: { + marginLeft: -16, + order: -100, + width: 24, }, + resize: ({ ownerState }) => ({ + display: 'flex', + flexDirection: 'column', + minHeight: 50, + minWidth: (ownerState?.position === 'left') ? 235 : 100, + position: 'relative', + }), + root: ({ ownerState }) => ({ + boxShadow: 'none', + boxSizing: 'border-box', + display: 'flex', + flexDirection: 'column', + minHeight: 0, + ...(ownerState?.position === 'right' && { + borderLeft: '0.5px solid rgba(0, 0, 0, 0.125)' + }), + ...(ownerState?.position === 'left' && { + borderRight: '0.5px solid rgba(0, 0, 0, 0.125)' + }), + ...(ownerState?.position === 'bottom' && { + borderTop: '0.5px solid rgba(0, 0, 0, 0.125)' + }), + }), + title: ({ theme }) => ({ + ...theme.typography.subtitle1, + alignSelf: 'center', + flexGrow: 1, + width: 160 + }), + toolbar: ({ theme }) => ({ + alignItems: 'flex-start', + backgroundColor: theme.palette.shades.light, + flexWrap: 'wrap', + justifyContent: 'space-between', + minHeight: 'max-content', + paddingInlineStart: '1rem', + }), }, - tooltipPlacementBottom: { - ['@media (min-width:600px)']: { - margin: 0, + }, + CompanionWindowSection: { + styleOverrides: { + root: { + borderBlockEnd: '.5px solid rgba(0, 0, 0, 0.25)' }, }, }, - MuiTouchRipple: { - childPulsate: { - animation: 'none', + IIIFHtmlContent: { + styleOverrides: { + root: ({ theme }) => ({ + '& a': { + color: theme.palette.primary.main, + textDecoration: 'underline', + }, + }), }, - rippleVisible: { - animation: 'none', + }, + IIIFThumbnail: { + styleOverrides: { + root: ({ ownerState }) => ({ + ...(ownerState?.variant === 'inside' && { + display: 'inline-block', + height: 'inherit', + position: 'relative', + }), + }), + label: ({ ownerState }) => ({ + overflow: 'hidden', + textOverflow: 'ellipsis', + lineHeight: '1.5em', + wordBreak: 'break-word', + ...(ownerState?.variant === 'inside' && { + color: '#ffffff', + WebkitLineClamp: 1, + whiteSpace: 'nowrap', + }), + ...(ownerState?.variant === 'outside' && { + display: '-webkit-box', + maxHeight: '3em', + MozBoxOrient: 'vertical', + WebkitLineClamp: 2, + }), + ...(ownerState?.variant === 'inside' && { + background: 'linear-gradient(to top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.3) 70%, rgba(0,0,0,0) 100%)', + bottom: '5px', + boxSizing: 'border-box', + left: '0px', + padding: '4px', + position: 'absolute', + width: '100%', + }), + }), + image: ({ ownerState }) => ({ + ...(ownerState?.border && { + border: '1px solid rgba(0, 0, 0, 0.125)', + }), + }) + } + }, + ThemeIcon: { + styleOverrides: { + icon: ({ ownerState }) => ({ + color: (ownerState?.value === 'dark' ? '#000000' : undefined) + }), }, }, - }, - props: { + MuiAccordion: { + variants: [ + { + props: { variant: 'compact' }, + style: { + '& .MuiAccordionSummary-root': { + minHeight: 'unset', + padding: 0, + }, + '& .MuiAccordionSummary-content': { + margin: 0, + }, + '& .MuiAccordionDetails-root': { + padding: 0, + }, + }, + }, + ], + }, + MuiButton: { + styleOverrides: { + inlineText: { + lineHeight: '1.5em', + padding: 0, + textAlign: 'inherit', + textTransform: 'none', + + }, + inlineTextSecondary: ({ theme }) => ({ + color: theme.palette.secondary.main, + }), + } + }, MuiButtonBase: { - disableTouchRipple: true, + defaultProps: { + disableTouchRipple: true, + }, + }, + MuiDialog: { + variants: [ + { + props: { variant: 'contained' }, + style: { + position: 'absolute', + '& .MuiBackdrop-root': { + position: 'absolute' + } + }, + } + ] + }, + MuiFab: { + styleOverrides: { + root: { + transition: 'none', + } + }, }, MuiLink: { - underline: 'always' + defaultProps: { + underline: 'always' + }, + }, + MuiListSubheader: { + styleOverrides: { + root: { + '&[role="presentation"]:focus': { + outline: 0, + }, + }, + }, + }, + MuiTooltip: { // Overridden from https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Tooltip/Tooltip.js#L40-L70 + styleOverrides: { + tooltipPlacementLeft: { + ['@media (min-width:600px)']: { + margin: '0 !important', + }, + }, + tooltipPlacementRight: { + ['@media (min-width:600px)']: { + margin: '0 !important', + }, + }, + tooltipPlacementTop: { + ['@media (min-width:600px)']: { + margin: '0 !important', + }, + }, + tooltipPlacementBottom: { + ['@media (min-width:600px)']: { + margin: '0 !important', + }, + }, + }, + }, + MuiTouchRipple: { + styleOverrides: { + childPulsate: { + animation: 'none', + }, + rippleVisible: { + animation: 'none', + }, + }, }, }, }, diff --git a/src/containers/AppProviders.js b/src/containers/AppProviders.js index 22e8a3a06d4381880ecb305bea240a6df748ac1b..1ad1c6784dde9f7e5b583f13b2e39851bc397f40 100644 --- a/src/containers/AppProviders.js +++ b/src/containers/AppProviders.js @@ -11,7 +11,6 @@ import { AppProviders } from '../components/AppProviders'; */ const mapStateToProps = state => ( { - createGenerateClassNameOptions: getConfig(state).createGenerateClassNameOptions, language: getConfig(state).language, theme: getTheme(state), translations: getConfig(state).translations, diff --git a/src/containers/AttributionPanel.js b/src/containers/AttributionPanel.js index 8e0801444dd60e1786f4924b52bc0c3b3316fe7d..94ae930e0f78abee992b3e42f660b539f9a3696d 100644 --- a/src/containers/AttributionPanel.js +++ b/src/containers/AttributionPanel.js @@ -1,13 +1,12 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; -import { withPlugins } from '../extend/withPlugins'; import { getManifestLogo, getRequiredStatement, getRights, } from '../state/selectors'; +import { withPlugins } from '../extend/withPlugins'; import { AttributionPanel } from '../components/AttributionPanel'; /** @@ -21,29 +20,7 @@ const mapStateToProps = (state, { id, windowId }) => ({ rights: getRights(state, { windowId }), }); -/** - * - * @param theme - * @returns {label: {paddingLeft: number}}} - */ -const styles = theme => ({ - logo: { - maxWidth: '100%', - }, - placeholder: { - backgroundColor: theme.palette.grey[300], - }, - section: { - borderBottom: `.5px solid ${theme.palette.section_divider}`, - paddingBottom: theme.spacing(1), - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(1), - paddingTop: theme.spacing(2), - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps), withPlugins('AttributionPanel'), diff --git a/src/containers/AudioViewer.js b/src/containers/AudioViewer.js index e422c6d6b6e00458773de3c365080307590b9fd9..2bcd274096fcff1ff41ce76fd584853c964734ed 100644 --- a/src/containers/AudioViewer.js +++ b/src/containers/AudioViewer.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { AudioViewer } from '../components/AudioViewer'; import { getConfig, getVisibleCanvasAudioResources, getVisibleCanvasCaptions } from '../state/selectors'; @@ -15,21 +14,8 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** */ -const styles = () => ({ - audio: { - width: '100%', - }, - container: { - alignItems: 'center', - display: 'flex', - width: '100%', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, null), withPlugins('AudioViewer'), ); diff --git a/src/containers/CanvasAnnotations.js b/src/containers/CanvasAnnotations.js index 5bebeae7885399b8685ac82bab9462b38020add4..5d420e745df44d7853cea0faacdf14aea44c0606 100644 --- a/src/containers/CanvasAnnotations.js +++ b/src/containers/CanvasAnnotations.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { @@ -49,35 +48,8 @@ const mapDispatchToProps = { selectAnnotation: actions.selectAnnotation, }; -/** For withStlyes */ -const styles = theme => ({ - annotationListItem: { - '&$hovered': { - backgroundColor: theme.palette.action.hover, - }, - '&:hover,&:focus': { - backgroundColor: theme.palette.action.hover, - }, - borderBottom: `0.5px solid ${theme.palette.divider}`, - cursor: 'pointer', - whiteSpace: 'normal', - }, - chip: { - backgroundColor: theme.palette.background.paper, - marginRight: theme.spacing(0.5), - marginTop: theme.spacing(1), - }, - hovered: {}, - sectionHeading: { - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(1), - paddingTop: theme.spacing(2), - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('CanvasAnnotations'), ); diff --git a/src/containers/CanvasLayers.js b/src/containers/CanvasLayers.js index 87041bed9628dd6faf56f67ebe2711078f0e60b3..1b5baf2ffb1580d4fc5cf233bdec4e11bcb9b263 100644 --- a/src/containers/CanvasLayers.js +++ b/src/containers/CanvasLayers.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import * as actions from '../state/actions'; import { getCanvasLabel, @@ -26,81 +25,8 @@ const mapDispatchToProps = { updateLayers: actions.updateLayers, }; -/** For withStlyes */ -const styles = theme => ({ - dragging: {}, - dragHandle: { - alignItems: 'center', - borderRight: `0.5px solid ${theme.palette.divider}`, - display: 'flex', - flex: 1, - flexDirection: 'row', - marginBottom: -1 * theme.spacing(2) + 0.5, - marginRight: theme.spacing(1), - marginTop: -1 * theme.spacing(2), - maxWidth: theme.spacing(3), - width: theme.spacing(3), - }, - image: { - borderBottom: `1px solid ${theme.palette.divider}`, - }, - label: { - paddingLeft: theme.spacing(1), - }, - list: { - paddingTop: 0, - }, - listItem: { - '& $dragHandle': { - '&:hover': { - backgroundColor: theme.palette.action.hover, - }, - backgroundColor: theme.palette.shades?.light, - }, - '&$dragging': { - '& $dragHandle, & $dragHandle:hover': { - backgroundColor: theme.palette.action.selected, - }, - backgroundColor: theme.palette.action.hover, - }, - alignItems: 'stretch', - borderBottom: `0.5px solid ${theme.palette.divider}`, - cursor: 'pointer', - paddingBottom: theme.spacing(2), - paddingRight: theme.spacing(1), - paddingTop: theme.spacing(2), - }, - opacityIcon: { - marginRight: theme.spacing(0.5), - }, - opacityInput: { - ...theme.typography.caption, - '&::-webkit-outer-spin-button,&::-webkit-inner-spin-button': { - '-webkit-appearance': 'none', - margin: 0, - }, - '-moz-appearance': 'textfield', - textAlign: 'right', - width: '3ch', - }, - sectionHeading: { - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(1), - paddingTop: theme.spacing(2), - }, - slider: { - marginLeft: theme.spacing(2), - marginRight: theme.spacing(2), - maxWidth: 150, - }, - thumbnail: { - minWidth: 50, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), ); diff --git a/src/containers/ChangeThemeDialog.js b/src/containers/ChangeThemeDialog.js index ade8c4eeebe49ebeb76b720f099b1ec4be4863c6..a473f22f1b8346f7d7c7fee65d637eee463b79cb 100644 --- a/src/containers/ChangeThemeDialog.js +++ b/src/containers/ChangeThemeDialog.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; @@ -26,31 +25,8 @@ const mapStateToProps = state => ({ themeIds: getThemeIds(state), }); -/** */ -const styles = theme => ({ - dark: { - color: '#000000', - }, - dialogContent: { - padding: 0, - }, - light: { - color: '#BDBDBD', - }, - listitem: { - '&:focus': { - backgroundColor: theme.palette.action.focus, - }, - '&:hover': { - backgroundColor: theme.palette.action.hover, - }, - cursor: 'pointer', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('ChangeThemeDialog'), ); diff --git a/src/containers/CollapsibleSection.js b/src/containers/CollapsibleSection.js index 3daa9258b1ae29be903287fca527ea8ba41a459d..9e32b49c242eff19469c3f98207e15b163544b80 100644 --- a/src/containers/CollapsibleSection.js +++ b/src/containers/CollapsibleSection.js @@ -1,24 +1,9 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { CollapsibleSection } from '../components/CollapsibleSection'; -const styles = { - button: { - padding: 0, - }, - container: { - display: 'flex', - justifyContent: 'space-between', - }, - heading: { - cursor: 'pointer', - }, -}; - const enhance = compose( withTranslation(), - withStyles(styles), ); export default enhance(CollapsibleSection); diff --git a/src/containers/CollectionDialog.js b/src/containers/CollectionDialog.js index 1920dee3cd50bdb1303849969f2887d823aca7bd..02297bccbcae46e1233f62c569e812bbad4790b1 100644 --- a/src/containers/CollectionDialog.js +++ b/src/containers/CollectionDialog.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; @@ -48,44 +47,8 @@ const mapStateToProps = (state, { windowId }) => { }; }; -/** */ -const styles = theme => ({ - collectionFilter: { - padding: '16px', - paddingTop: 0, - }, - collectionItem: { - whiteSpace: 'normal', - }, - collectionMetadata: { - padding: '16px', - }, - dark: { - color: '#000000', - }, - dialog: { - position: 'absolute !important', - }, - dialogContent: { - padding: theme.spacing(1), - }, - light: { - color: theme.palette.grey[400], - }, - listitem: { - '&:focus': { - backgroundColor: theme.palette.action.focus, - }, - '&:hover': { - backgroundColor: theme.palette.action.hover, - }, - cursor: 'pointer', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), withWorkspaceContext, connect(mapStateToProps, mapDispatchToProps), withPlugins('CollectionDialog'), diff --git a/src/containers/CompanionArea.js b/src/containers/CompanionArea.js index 8ff8b9cb628481f47bbb5728f4b15dec52c463fb..f403a90849fa55e4ae43f23f98385d885eb3c8bc 100644 --- a/src/containers/CompanionArea.js +++ b/src/containers/CompanionArea.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import { @@ -21,43 +20,8 @@ const mapDispatchToProps = ({ setCompanionAreaOpen: actions.setCompanionAreaOpen, }); -/** */ -const styles = theme => ({ - horizontal: { - flexDirection: 'column', - width: '100%', - }, - left: { - minWidth: 235, - }, - root: { - display: 'flex', - minHeight: 0, - position: 'relative', - zIndex: theme.zIndex.appBar - 2, - }, - toggle: { - backgroundColor: theme.palette.background.paper, - border: `1px solid ${theme.palette.shades?.dark}`, - borderRadius: 0, - height: '48px', - left: '100%', - marginTop: '1rem', - padding: 2, - position: 'absolute', - width: '23px', - zIndex: theme.zIndex.drawer, - }, - toggleButton: { - marginBottom: 12, - marginTop: 12, - padding: 0, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('CompanionArea'), ); diff --git a/src/containers/CompanionWindow.js b/src/containers/CompanionWindow.js index 9def7a3c3c0e468c0f643c9a302b42655cacb6a3..64a1806440f48be886d013f8cb4878be2b1f409d 100644 --- a/src/containers/CompanionWindow.js +++ b/src/containers/CompanionWindow.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withSize } from 'react-sizeme'; import { withPlugins } from '../extend/withPlugins'; import { withRef } from '../extend/withRef'; @@ -45,95 +44,9 @@ const mapDispatchToProps = (dispatch, { windowId, id }) => ({ ), }); -/** - * - * @param theme - * @returns {{closeButton: {top: number, position: string, right: number}, - * root: {overflowY: string, width: string}}} - */ -const styles = theme => ({ - closeButton: { - order: 4, - }, - 'companionWindow-bottom': { - borderTop: `0.5px solid ${theme.palette.divider}`, - }, - 'companionWindow-left': { - borderRight: `0.5px solid ${theme.palette.divider}`, - }, - 'companionWindow-right': { - borderLeft: `0.5px solid ${theme.palette.divider}`, - }, - companionWindowHeader: { - flexWrap: 'wrap', - }, - companionWindowTitleControls: { - flexGrow: 1, - order: 1000, - }, - companionWindowTitleControlsBottom: { - order: 'unset', - }, - content: { - overflowY: 'auto', - wordBreak: 'break-word', - }, - horizontal: { - }, - positionButton: { - marginLeft: -16, - order: -100, - width: 24, - }, - rnd: { - display: 'flex', - flexDirection: 'column', - minHeight: 0, - }, - root: { - boxShadow: 'none', - boxSizing: 'border-box', - display: 'flex', - flexDirection: 'column', - minHeight: 0, - }, - small: {}, - titleControls: { - alignItems: 'center', - display: 'flex', - flexFlow: 'row wrap', - minHeight: 48, - order: 3, - }, - toolbar: { - '&$small': { - '& $closeButton': { - order: 'unset', - }, - '& $titleControls': { - order: 'unset', - }, - }, - alignItems: 'flex-start', - background: theme.palette.shades?.light, - justifyContent: 'space-between', - minHeight: 'max-content', - paddingLeft: theme.spacing(2), - }, - vertical: { - }, - windowSideBarTitle: { - ...theme.typography.subtitle1, - alignSelf: 'center', - flexGrow: 1, - width: 160, - }, -}); - const enhance = compose( withRef(), withTranslation(), - withStyles(styles), withSize(), connect(mapStateToProps, mapDispatchToProps), withPlugins('CompanionWindow'), diff --git a/src/containers/CustomPanel.js b/src/containers/CustomPanel.js index 493ad146335bc1e55955512e721ad1bd83244ed4..3f83547018ed6f2b604d0bdb4d506556662133b8 100644 --- a/src/containers/CustomPanel.js +++ b/src/containers/CustomPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { CustomPanel } from '../components/CustomPanel'; @@ -11,17 +10,8 @@ import { CustomPanel } from '../components/CustomPanel'; const mapStateToProps = (state, { id, windowId }) => ({ }); -/** - * - * @param theme - * @returns {label: {paddingLeft: number}}} - */ -const styles = theme => ({ -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps), withPlugins('CustomPanel'), ); diff --git a/src/containers/ErrorContent.js b/src/containers/ErrorContent.js index ce3089f9d0f01b14b4d642e5194e2cf517ecd9e0..85443562090cbcdbf4501e01a7d765d561c44ef8 100644 --- a/src/containers/ErrorContent.js +++ b/src/containers/ErrorContent.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { ErrorContent } from '../components/ErrorContent'; import { @@ -23,29 +22,8 @@ const mapStateToProps = (state, { companionWindowId, windowId }) => ({ showJsError: getConfig(state).window.showJsError, }); -/** - * @param theme - * @returns {{typographyBody: {flexGrow: number, fontSize: number|string}, - * windowTopBarStyle: {minHeight: number, paddingLeft: number, backgroundColor: string}}} - */ -const styles = theme => ({ - alert: { - backgroundColor: theme.palette.error.main, - color: '#fff', - fontWeight: theme.typography.fontWeightMedium, - }, - details: { - '& pre': { - height: '100px', - overflowY: 'scroll', - }, - flexDirection: 'column', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps), withPlugins('ErrorContent'), ); diff --git a/src/containers/GalleryView.js b/src/containers/GalleryView.js index 6b916d755abb443155a84da0efcac4ccd453b082..9c54be5a4b3971d371f187fdafbb58490e461945 100644 --- a/src/containers/GalleryView.js +++ b/src/containers/GalleryView.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { GalleryView } from '../components/GalleryView'; import { getCanvases, getSequenceViewingDirection } from '../state/selectors'; @@ -17,24 +16,7 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** - * Styles to be passed to the withStyles HOC - */ -const styles = theme => ({ - galleryContainer: { - alignItems: 'flex-start', - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - overflowX: 'hidden', - overflowY: 'scroll', - padding: '50px 0 50px 20px', - width: '100%', - }, -}); - const enhance = compose( - withStyles(styles), connect(mapStateToProps), withPlugins('GalleryView'), // further HOC go here diff --git a/src/containers/GalleryViewThumbnail.js b/src/containers/GalleryViewThumbnail.js index 66d6a94a625c7f12db73a32d8e8d6fa2f29e16ca..c0306777ce9d9aecd58dfa13b733764dc95c5b51 100644 --- a/src/containers/GalleryViewThumbnail.js +++ b/src/containers/GalleryViewThumbnail.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import flatten from 'lodash/flatten'; -import { withStyles } from '@material-ui/core/styles'; import * as actions from '../state/actions'; import { GalleryViewThumbnail } from '../components/GalleryViewThumbnail'; import { @@ -12,62 +11,6 @@ import { getCompanionWindowsForContent, } from '../state/selectors'; -/** - * Styles to be passed to the withStyles HOC - */ -const styles = theme => ({ - annotationIcon: { - height: '1rem', - width: '1rem', - }, - annotationsChip: { - ...theme.typography.caption, - }, - avatar: { - backgroundColor: 'transparent', - }, - chips: { - opacity: 0.875, - position: 'absolute', - right: 0, - textAlign: 'right', - top: 0, - }, - galleryViewItem: { - '&$hasAnnotations': { - border: `2px solid ${theme.palette.action.selected}`, - }, - '&$selected,&$selected$hasAnnotations': { - border: `2px solid ${theme.palette.primary.main}`, - }, - '&:focus': { - outline: 'none', - }, - '&:hover': { - backgroundColor: theme.palette.action.hover, - }, - border: '2px solid transparent', - cursor: 'pointer', - display: 'inline-block', - margin: `${theme.spacing(1)}px ${theme.spacing(0.5)}px`, - maxHeight: props => props.config.height + 45, - minWidth: '60px', - overflow: 'hidden', - padding: theme.spacing(0.5), - position: 'relative', - width: 'min-content', - }, - hasAnnotations: {}, - searchChip: { - ...theme.typography.caption, - '&$selected $avatar': { - backgroundColor: theme.palette.highlights?.primary, - }, - marginTop: 2, - }, - selected: {}, -}); - /** */ const mapStateToProps = (state, { canvas, windowId }) => { const currentCanvas = getCurrentCanvas(state, { windowId }); @@ -115,7 +58,6 @@ const mapDispatchToProps = (dispatch, { canvas, id, windowId }) => ({ const enhance = compose( connect(mapStateToProps, mapDispatchToProps), - withStyles(styles), // further HOC go here ); diff --git a/src/containers/IIIFThumbnail.js b/src/containers/IIIFThumbnail.js index c596b508cd92fee25b69b5e09e198011182363e7..6631a8deb3d5e296210213072e71a68cc30439c4 100644 --- a/src/containers/IIIFThumbnail.js +++ b/src/containers/IIIFThumbnail.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getConfig, @@ -16,51 +15,7 @@ const mapStateToProps = (state) => ({ thumbnailsConfig: getConfig(state).thumbnails, }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - caption: { - lineHeight: '1.5em', - wordBreak: 'break-word', - }, - image: {}, - insideCaption: { - color: '#ffffff', - lineClamp: '1', - whiteSpace: 'nowrap', - }, - insideLabel: { - background: 'linear-gradient(to top, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.3) 70%, rgba(0,0,0,0) 100%)', - bottom: '5px', - boxSizing: 'border-box', - left: '0px', - padding: '4px', - position: 'absolute', - width: '100%', - }, - insideRoot: { - display: 'inline-block', - height: 'inherit', - position: 'relative', - }, - label: { - overflow: 'hidden', - textOverflow: 'ellipsis', - }, - outsideCaption: { - boxOrient: 'vertical', - display: '-webkit-box', - lineClamp: '2', - maxHeight: '3em', - }, - outsideLabel: {}, - outsideRoot: {}, - root: {}, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps), withPlugins('IIIFThumbnail'), diff --git a/src/containers/LayersPanel.js b/src/containers/LayersPanel.js index 16a1a9b01b5cec0d1005c357debb1fd72dcf801d..2d7352f4eb2ce54a4927221a3e2aef820a258e23 100644 --- a/src/containers/LayersPanel.js +++ b/src/containers/LayersPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { LayersPanel } from '../components/LayersPanel'; import { @@ -15,17 +14,8 @@ const mapStateToProps = (state, { id, windowId }) => ({ canvasIds: getVisibleCanvasIds(state, { windowId }), }); -/** - * - * @param theme - * @returns {label: {paddingLeft: number}}} - */ -const styles = theme => ({ -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps), withPlugins('LayersPanel'), ); diff --git a/src/containers/LocalePicker.js b/src/containers/LocalePicker.js index 509fef4e3648f8a7b4e4baaf5b0052e12dc76607..2edb53b784605e79a283efcb2a08838131297894 100644 --- a/src/containers/LocalePicker.js +++ b/src/containers/LocalePicker.js @@ -1,27 +1,9 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { LocalePicker } from '../components/LocalePicker'; -/** - * - * @param theme - * @returns {label: {paddingLeft: number}}} - */ -const styles = theme => ({ - select: { - '&:focus': { - backgroundColor: theme.palette.background.paper, - }, - }, - selectEmpty: { - backgroundColor: theme.palette.background.paper, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), ); export default enhance(LocalePicker); diff --git a/src/containers/ManifestForm.js b/src/containers/ManifestForm.js index e64b68d68c74d31e07ae6c7c3627b761b83a22a7..02a32621445f22f08e4137054d6b2895bd0e69fb 100644 --- a/src/containers/ManifestForm.js +++ b/src/containers/ManifestForm.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { ManifestForm } from '../components/ManifestForm'; @@ -12,24 +11,8 @@ import { ManifestForm } from '../components/ManifestForm'; * @private */ const mapDispatchToProps = { addResource: actions.addResource }; -/** - * - * @param theme - */ -const styles = theme => ({ - buttons: { - textAlign: 'right', - [theme.breakpoints.up('sm')]: { - textAlign: 'inherit', - }, - }, - input: { - ...theme.typography.body1, - }, -}); const enhance = compose( - withStyles(styles), withTranslation(), connect(null, mapDispatchToProps), withPlugins('ManifestForm'), diff --git a/src/containers/ManifestListItem.js b/src/containers/ManifestListItem.js index 6553a9cfa24a9e74766a40e28b1d66b3c7d87a15..55f5c43e90be8bbb35d0a048eeaef46d74a7ae43 100644 --- a/src/containers/ManifestListItem.js +++ b/src/containers/ManifestListItem.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { getManifest, @@ -50,56 +49,8 @@ const mapDispatchToProps = { fetchManifest: actions.fetchManifest, }; -/** - * - * @param theme - * @returns {{root: {}, label: {textAlign: string, textTransform: string}}} - */ -const styles = theme => ({ - active: {}, - buttonGrid: { - }, - label: { - textAlign: 'left', - textTransform: 'initial', - }, - logo: { - height: '2.5rem', - maxWidth: '100%', - objectFit: 'contain', - paddingRight: 8, - }, - placeholder: { - backgroundColor: theme.palette.grey[300], - }, - root: { - '&$active': { - borderLeft: `4px solid ${theme.palette.primary.main}`, - }, - '&:hover,&:focus-within': { - '&$active': { - borderLeft: `4px solid ${theme.palette.primary.main}`, - }, - backgroundColor: theme.palette.action.hover, - borderLeft: `4px solid ${theme.palette.action.hover}`, - }, - borderLeft: '4px solid transparent', - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(2), - [theme.breakpoints.up('sm')]: { - paddingLeft: theme.spacing(3), - paddingRight: theme.spacing(3), - }, - }, - thumbnail: { - maxWidth: '100%', - objectFit: 'contain', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('ManifestListItem'), ); diff --git a/src/containers/ManifestListItemError.js b/src/containers/ManifestListItemError.js index 001d98750b73b2916dded1f97fb205e82df2f433..40d9f5f14dd188eb03b0b691d6eae96cffee308f 100644 --- a/src/containers/ManifestListItemError.js +++ b/src/containers/ManifestListItemError.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { fetchManifest, removeResource } from '../state/actions'; import { ManifestListItemError } from '../components/ManifestListItemError'; @@ -12,26 +11,8 @@ const mapDispatchToProps = { onTryAgainClick: fetchManifest, }; -/** - * - * @param theme - * @returns {{manifestIdText: {wordBreak: string}, - * errorIcon: {color: string, width: string, height: string}}} - */ -const styles = theme => ({ - errorIcon: { - color: theme.palette.error.main, - height: '2rem', - width: '2rem', - }, - manifestIdText: { - wordBreak: 'break-all', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(null, mapDispatchToProps), withPlugins('ManifestListItemError'), ); diff --git a/src/containers/ManifestRelatedLinks.js b/src/containers/ManifestRelatedLinks.js index 6bcc356778bf8d8f6388d25c2e2698462ea1a119..a3f1edf9e0a702b8b94fe9f28b8e1317493a0cb3 100644 --- a/src/containers/ManifestRelatedLinks.js +++ b/src/containers/ManifestRelatedLinks.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getManifestHomepage, @@ -25,17 +24,7 @@ const mapStateToProps = (state, { id, windowId }) => ({ seeAlso: getManifestSeeAlso(state, { windowId }), }); -const styles = { - labelValueMetadata: { - '& dd': { - marginBottom: '.5em', - marginLeft: '0', - }, - }, -}; - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps), withPlugins('ManifestRelatedLinks'), diff --git a/src/containers/MinimalWindow.js b/src/containers/MinimalWindow.js index 5ed8aec777d6abe9e2abaa2eedf6c55ecca01abf..d4e57cb75b41a5db524aaef50172dada9a6563d4 100644 --- a/src/containers/MinimalWindow.js +++ b/src/containers/MinimalWindow.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { MinimalWindow } from '../components/MinimalWindow'; @@ -22,42 +21,8 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ removeWindow: () => dispatch(actions.removeWindow(windowId)), }); -/** - * @param theme - * @returns {{typographyBody: {flexGrow: number, fontSize: number|string}, - * windowTopBarStyle: {minHeight: number, paddingLeft: number, backgroundColor: string}}} - */ -const styles = theme => ({ - button: { - marginLeft: 'auto', - }, - title: { - ...theme.typography.h6, - flexGrow: 1, - paddingLeft: theme.spacing(0.5), - }, - window: { - backgroundColor: theme.palette.shades?.dark, - borderRadius: 0, - display: 'flex', - flexDirection: 'column', - height: '100%', - minHeight: 0, - overflow: 'hidden', - width: '100%', - }, - windowTopBarStyle: { - backgroundColor: theme.palette.shades?.main, - borderTop: '2px solid transparent', - minHeight: 32, - paddingLeft: theme.spacing(0.5), - paddingRight: theme.spacing(0.5), - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('MinimalWindow'), ); diff --git a/src/containers/MosaicRenderPreview.js b/src/containers/MosaicRenderPreview.js index 0103bab9c5beeba6658c2573ade4196ef5f2243b..149861f89f6eabdde49a83dd85592a80228a62e2 100644 --- a/src/containers/MosaicRenderPreview.js +++ b/src/containers/MosaicRenderPreview.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import { getManifestTitle } from '../state/selectors'; @@ -13,18 +12,7 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** - * - * @param theme - */ -const styles = theme => ({ - preview: { - ...theme.typography.h4, - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, null), withPlugins('MosaicRenderPreview'), diff --git a/src/containers/OpenSeadragonViewer.js b/src/containers/OpenSeadragonViewer.js index b5c59dff19b3cccda6d45e2430ffcf60487d4e80..adc01cf90c054e3d7eb0a28f26158bef22b89f5e 100644 --- a/src/containers/OpenSeadragonViewer.js +++ b/src/containers/OpenSeadragonViewer.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import flatten from 'lodash/flatten'; import { withPlugins } from '../extend/withPlugins'; import { OpenSeadragonViewer } from '../components/OpenSeadragonViewer'; @@ -55,16 +54,7 @@ const mapDispatchToProps = { updateViewport: actions.updateViewport, }; -const styles = { - osdContainer: { - cursor: 'grab', - flex: 1, - position: 'relative', - }, -}; - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('OpenSeadragonViewer'), diff --git a/src/containers/PrimaryWindow.js b/src/containers/PrimaryWindow.js index 336894235f31a71867a2e77da22347d9099c7909..1895e8053964c70d57bdb34f8316dc7704832f63 100644 --- a/src/containers/PrimaryWindow.js +++ b/src/containers/PrimaryWindow.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getManifestoInstance, getVisibleCanvasAudioResources, getVisibleCanvasVideoResources, getWindow, @@ -18,16 +17,7 @@ const mapStateToProps = (state, { windowId }) => { }; }; -const styles = { - primaryWindow: { - display: 'flex', - flex: 1, - position: 'relative', - }, -}; - const enhance = compose( - withStyles(styles), connect(mapStateToProps, null), withPlugins('PrimaryWindow'), ); diff --git a/src/containers/SanitizedHtml.js b/src/containers/SanitizedHtml.js index 932bfa0c0a650823e8c4d473d230fd749581b251..b6771d0d9e5585f2d35e7fd2ef7b7caaf468e334 100644 --- a/src/containers/SanitizedHtml.js +++ b/src/containers/SanitizedHtml.js @@ -1,16 +1,3 @@ -import { withStyles } from '@material-ui/core/styles'; import { SanitizedHtml } from '../components/SanitizedHtml'; -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - root: { - '& a': { - color: theme.palette.primary.main, - textDecoration: 'underline', - }, - }, -}); - -export default withStyles(styles)(SanitizedHtml); +export default SanitizedHtml; diff --git a/src/containers/ScrollIndicatedDialogContent.js b/src/containers/ScrollIndicatedDialogContent.js index 66ec29e1abc18cec67c6603ae5dbc848bec301fe..aad0df3d57d586b115f3664b4001dfe7f58ed383 100644 --- a/src/containers/ScrollIndicatedDialogContent.js +++ b/src/containers/ScrollIndicatedDialogContent.js @@ -1,29 +1,3 @@ -import { withStyles } from '@material-ui/core/styles'; import { ScrollIndicatedDialogContent } from '../components/ScrollIndicatedDialogContent'; -/** - * Styles for the withStyles HOC - */ -const styles = theme => ({ - shadowScrollDialog: { - /* Shadow covers */ - background: `linear-gradient(${theme.palette.background.paper} 30%, rgba(255, 255, 255, 0)), ` - + `linear-gradient(rgba(255, 255, 255, 0), ${theme.palette.background.paper} 70%) 0 100%, ` - // Shaddows - + 'radial-gradient(50% 0, farthest-side, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)), ' - + 'radial-gradient(50% 100%, farthest-side, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) 0 100%,', - /* Shadow covers */ - background: `linear-gradient(${theme.palette.background.paper} 30%, rgba(255, 255, 255, 0)), ` // eslint-disable-line no-dupe-keys - + `linear-gradient(rgba(255, 255, 255, 0), ${theme.palette.background.paper} 70%) 0 100%, ` - // Shaddows - + 'radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)), ' - + 'radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, .2), rgba(0, 0, 0, 0)) 0 100%;', - - backgroundAttachment: 'local, local, scroll, scroll', - backgroundRepeat: 'no-repeat', - backgroundSize: '100% 40px, 100% 40px, 100% 14px, 100% 14px', - overflowY: 'auto', - }, -}); - -export default withStyles(styles)(ScrollIndicatedDialogContent); +export default ScrollIndicatedDialogContent; diff --git a/src/containers/SearchHit.js b/src/containers/SearchHit.js index 009f44903e6e8f5f058248b27f98b1d9460911e1..7e67df152f62bde81582c712da441141f1590ecf 100644 --- a/src/containers/SearchHit.js +++ b/src/containers/SearchHit.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { SearchHit } from '../components/SearchHit'; import * as actions from '../state/actions'; @@ -70,60 +69,8 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ ), }); -/** */ -const styles = theme => ({ - adjacent: {}, - focused: {}, - hitCounter: { - ...theme.typography.subtitle2, - backgroundColor: theme.palette.hitCounter?.default, - height: 30, - marginRight: theme.spacing(1), - verticalAlign: 'inherit', - }, - inlineButton: { - '& span': { - lineHeight: '1.5em', - }, - margin: 0, - padding: 0, - textTransform: 'none', - }, - listItem: { - '&$adjacent': { - '& $hitCounter': { - backgroundColor: theme.palette.highlights?.secondary, - }, - '&$windowSelected': { - '& $hitCounter': { - backgroundColor: theme.palette.highlights?.primary, - }, - }, - }, - '&$windowSelected': { - '& $hitCounter': { - backgroundColor: theme.palette.highlights?.primary, - }, - '&$focused': { - '&:hover': { - backgroundColor: 'inherit', - }, - backgroundColor: 'inherit', - }, - }, - borderBottom: `0.5px solid ${theme.palette.divider}`, - paddingRight: 8, - }, - selected: {}, - subtitle: { - marginBottom: theme.spacing(1.5), - }, - windowSelected: {}, -}); - const enhance = compose( connect(mapStateToProps, mapDispatchToProps), - withStyles(styles), withTranslation(), withPlugins('SearchHit'), ); diff --git a/src/containers/SearchPanel.js b/src/containers/SearchPanel.js index 64461b48c8f2ae69f343890eeaeae1d9ce34e208..5a5f4aa4088eb554c56345381e82ec0d54583c2a 100644 --- a/src/containers/SearchPanel.js +++ b/src/containers/SearchPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import * as actions from '../state/actions'; import { withPlugins } from '../extend/withPlugins'; import { SearchPanel } from '../components/SearchPanel'; @@ -22,27 +21,8 @@ const mapDispatchToProps = (dispatch, props) => ({ removeSearch: () => dispatch(actions.removeSearch(props.windowId, props.id)), }); -/** -* Styles for withStyles HOC -*/ -const styles = theme => ({ - clearChip: { - marginLeft: theme.spacing(1), - }, - inlineButton: { - '& span': { - lineHeight: '1.5em', - }, - margin: theme.spacing(2), - padding: 0, - textAlign: 'inherit', - textTransform: 'none', - }, -}); - const enhance = compose( connect(mapStateToProps, mapDispatchToProps), - withStyles(styles), withTranslation(), withPlugins('SearchPanel'), ); diff --git a/src/containers/SearchPanelControls.js b/src/containers/SearchPanelControls.js index e81d46a4f3b48dedb66f93f4e2bc3034aeb77f12..e2f718ae07e613b059bc5d4ad2e206586be32b3c 100644 --- a/src/containers/SearchPanelControls.js +++ b/src/containers/SearchPanelControls.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { SearchPanelControls } from '../components/SearchPanelControls'; import * as actions from '../state/actions'; @@ -33,26 +32,8 @@ const mapDispatchToProps = { fetchSearch: actions.fetchSearch, }; -/** */ -const styles = theme => ({ - endAdornment: { - position: 'absolute', - right: 0, - }, - form: { - paddingBottom: theme.spacing(1), - paddingRight: theme.spacing(1.5), - width: '100%', - }, - searchProgress: { - position: 'absolute', - right: 0, - }, -}); - const enhance = compose( connect(mapStateToProps, mapDispatchToProps), - withStyles(styles), withTranslation(), withPlugins('SearchPanelControls'), ); diff --git a/src/containers/SearchPanelNavigation.js b/src/containers/SearchPanelNavigation.js index 614b2419209114973c256fc4029944724185b715..83b421ac3bcca2e24ca0e593390ecfca4eb56869 100644 --- a/src/containers/SearchPanelNavigation.js +++ b/src/containers/SearchPanelNavigation.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { SearchPanelNavigation } from '../components/SearchPanelNavigation'; import * as actions from '../state/actions'; @@ -37,17 +36,8 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ ), }); -/** */ -const styles = theme => ({ - body2: { - marginLeft: '-16px', - width: '100%', - }, -}); - const enhance = compose( connect(mapStateToProps, mapDispatchToProps), - withStyles(styles), withTranslation(), withPlugins('SearchPanelNavigation'), ); diff --git a/src/containers/SearchResults.js b/src/containers/SearchResults.js index 15e660d99a297bfdb9a13a57d4f19e48697c8097..0684e36b8ef5ea04d37945200b31e4484639c9fe 100644 --- a/src/containers/SearchResults.js +++ b/src/containers/SearchResults.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { SearchResults } from '../components/SearchResults'; import * as actions from '../state/actions'; @@ -33,26 +32,8 @@ const mapDispatchToProps = { fetchSearch: actions.fetchSearch, }; -/** */ -const styles = theme => ({ - moreButton: { - width: '100%', - }, - navigation: { - textTransform: 'none', - }, - noResults: { - ...theme.typography.h6, - padding: theme.spacing(2), - }, - toggleFocus: { - ...theme.typography.subtitle1, - }, -}); - const enhance = compose( connect(mapStateToProps, mapDispatchToProps), - withStyles(styles), withTranslation(), withPlugins('SearchResults'), ); diff --git a/src/containers/SelectCollection.js b/src/containers/SelectCollection.js index 446c32711f233e68c9ca4fe904e643c4e71d0ab2..e711f53708a42c3f8c395178f2c0dbca35b88e8a 100644 --- a/src/containers/SelectCollection.js +++ b/src/containers/SelectCollection.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import * as actions from '../state/actions'; import { withPlugins } from '../extend/withPlugins'; import { @@ -22,13 +21,9 @@ const mapStateToProps = (state, { windowId }) => { const mapDispatchToProps = { showCollectionDialog: actions.showCollectionDialog, }; -/** */ -const styles = (theme) => ({ -}); const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('SelectCollection'), ); diff --git a/src/containers/SidebarIndexItem.js b/src/containers/SidebarIndexItem.js index e58afff81c4fdd32cd41d211f51aff84590fed28..bc8a19001ea4d2ac41e743ecb4f338d9873506f7 100644 --- a/src/containers/SidebarIndexItem.js +++ b/src/containers/SidebarIndexItem.js @@ -1,21 +1,10 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { SidebarIndexItem } from '../components/SidebarIndexItem'; -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - label: { - paddingLeft: theme.spacing(1), - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(null, null), withPlugins('SidebarIndexItem'), diff --git a/src/containers/SidebarIndexList.js b/src/containers/SidebarIndexList.js index 866e7d61d7229e239182378c8cd3e075a5a85cfa..de46a8893914f51188a78541e0899a866524b0f5 100644 --- a/src/containers/SidebarIndexList.js +++ b/src/containers/SidebarIndexList.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { @@ -29,21 +28,7 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({ setCanvas: (...args) => dispatch(actions.setCanvas(...args)), }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - label: { - paddingLeft: theme.spacing(1), - }, - listItem: { - borderBottom: `0.5px solid ${theme.palette.divider}`, - paddingRight: theme.spacing(1), - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('SidebarIndexList'), diff --git a/src/containers/SidebarIndexTableOfContents.js b/src/containers/SidebarIndexTableOfContents.js index a626981f346b31775b7511a3c85d4065c0874c27..870f0c4087c0b5e0ae1ba55e59b5e048a3cc58cc 100644 --- a/src/containers/SidebarIndexTableOfContents.js +++ b/src/containers/SidebarIndexTableOfContents.js @@ -1,8 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; -import { alpha } from '@material-ui/core/styles/colorManipulator'; import { withPlugins } from '../extend/withPlugins'; import { SidebarIndexTableOfContents } from '../components/SidebarIndexTableOfContents'; import { @@ -34,45 +32,7 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({ toggleNode: nodeId => dispatch(actions.toggleNode(windowId, id, nodeId)), }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - content: { - alignItems: 'flex-start', - borderLeft: '1px solid transparent', - padding: '8px 16px 8px 0', - width: 'auto', - }, - group: { - borderLeft: `1px solid ${theme.palette.grey[300]}`, - }, - label: { - paddingLeft: 0, - }, // needed for pseudo $label class - root: { - flexGrow: 1, - }, - selected: {}, // needed for pseudo $selected class - treeItemRoot: { - '&:focus > $content': { - backgroundColor: theme.palette.action.selected, - }, - '&:hover > $content': { - backgroundColor: theme.palette.action.hover, - }, - '&:hover >$content $label, &:focus > $content $label, &$selected > $content $label, &$selected > $content $label:hover, &$selected:focus > $content $label': { - backgroundColor: 'transparent', - }, - }, - visibleNode: { - backgroundColor: alpha(theme.palette.highlights?.primary || theme.palette.action.selected, 0.35), - display: 'inline', - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('SidebarIndexTableOfContents'), diff --git a/src/containers/SidebarIndexThumbnail.js b/src/containers/SidebarIndexThumbnail.js index 9f582dc4647693cfb9d78d574648bd4169f066d1..74c831814fefcc36ec6e69a06643bce378d7ec89 100644 --- a/src/containers/SidebarIndexThumbnail.js +++ b/src/containers/SidebarIndexThumbnail.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { SidebarIndexThumbnail } from '../components/SidebarIndexThumbnail'; import { getConfig } from '../state/selectors'; @@ -15,17 +14,7 @@ const mapStateToProps = (state, { data }) => ({ ...(getConfig(state).canvasNavigation || {}), }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - label: { - paddingLeft: theme.spacing(1), - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, null), withPlugins('SidebarIndexThumbnail'), diff --git a/src/containers/ThumbnailCanvasGrouping.js b/src/containers/ThumbnailCanvasGrouping.js index 0804cee2d51e20fbf78c28f64f4d73cef0b9aa93..548c941ba941931b77e7915ca1549a43269c5211 100644 --- a/src/containers/ThumbnailCanvasGrouping.js +++ b/src/containers/ThumbnailCanvasGrouping.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { getCurrentCanvas } from '../state/selectors'; @@ -25,33 +24,8 @@ const mapStateToProps = (state, { data }) => ({ currentCanvasId: (getCurrentCanvas(state, { windowId: data.windowId }) || {}).id, }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - canvas: { - '&$currentCanvas': { - outline: `2px solid ${theme.palette.primary.main}`, - outlineOffset: '3px', - }, - '&:hover': { - outline: `9px solid ${theme.palette.action.hover}`, - outlineOffset: '-2px', - }, - boxSizing: 'border-box', - color: theme.palette.common.white, - cursor: 'pointer', - display: 'inline-block', - outline: 0, - whiteSpace: 'nowrap', - }, - currentCanvas: { - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('ThumbnailCanvasGrouping'), ); diff --git a/src/containers/ThumbnailNavigation.js b/src/containers/ThumbnailNavigation.js index e3be9a8f3363f1068771976c0d71929445d9e771..bf7c3490aaa6db191facdaad429aa0c14c5087b4 100644 --- a/src/containers/ThumbnailNavigation.js +++ b/src/containers/ThumbnailNavigation.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { ThumbnailNavigation } from '../components/ThumbnailNavigation'; @@ -40,20 +39,7 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ setPreviousCanvas: (...args) => dispatch(actions.setPreviousCanvas(windowId)), }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - thumbNavigation: { - '&:focus': { - boxShadow: 0, - outline: 0, - }, - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('ThumbnailNavigation'), diff --git a/src/containers/VideoViewer.js b/src/containers/VideoViewer.js index 70e8ebc0832ba985c42d185bb3ffc98e074b8993..3c868940a211a4b048006184fb0928f8416eab99 100644 --- a/src/containers/VideoViewer.js +++ b/src/containers/VideoViewer.js @@ -1,7 +1,6 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { VideoViewer } from '../components/VideoViewer'; import { getConfig, getVisibleCanvasCaptions, getVisibleCanvasVideoResources } from '../state/selectors'; @@ -15,22 +14,8 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** */ -const styles = () => ({ - container: { - alignItems: 'center', - display: 'flex', - width: '100%', - }, - video: { - maxHeight: '100%', - width: '100%', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, null), withPlugins('VideoViewer'), ); diff --git a/src/containers/ViewerInfo.js b/src/containers/ViewerInfo.js index 7e2714d9b7880b574dea33859bb272bd98445394..6757102f98ea7cf276f753ea852eb133e2938148 100644 --- a/src/containers/ViewerInfo.js +++ b/src/containers/ViewerInfo.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { ViewerInfo } from '../components/ViewerInfo'; import { @@ -29,20 +28,7 @@ const mapStateToProps = (state, props) => { }; }; -const styles = { - osdInfo: { - order: 2, - overflow: 'hidden', - paddingBottom: 3, - textOverflow: 'ellipsis', - unicodeBidi: 'plaintext', - whiteSpace: 'nowrap', - width: '100%', - }, -}; - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, null), withPlugins('ViewerInfo'), diff --git a/src/containers/ViewerNavigation.js b/src/containers/ViewerNavigation.js index 38fb8d4602b0e24e8095486f1c13aa6edaac7a33..3f142de4315e01ad9ad214fc58e0bfdcfc58111f 100644 --- a/src/containers/ViewerNavigation.js +++ b/src/containers/ViewerNavigation.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { @@ -28,14 +27,7 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ setPreviousCanvas: (...args) => dispatch(actions.setPreviousCanvas(windowId)), }); -const styles = { - osdNavigation: { - order: 1, - }, -}; - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('ViewerNavigation'), diff --git a/src/containers/Window.js b/src/containers/Window.js index a06801e2a445d6a9530b9ec6de882fc994c79984..36cd4e14e1a5a87a6fd43cf8ec78a0f16e15c1f5 100644 --- a/src/containers/Window.js +++ b/src/containers/Window.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; @@ -36,70 +35,8 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ focusWindow: () => dispatch(actions.focusWindow(windowId)), }); -/** - * @param theme - */ -const styles = theme => ({ - companionAreaBottom: { - display: 'flex', - flex: '0', - flexBasis: 'auto', - minHeight: 0, - }, - companionAreaRight: { - display: 'flex', - flex: '0 1 auto', - minHeight: 0, - }, - maximized: {}, - middle: { - display: 'flex', - flex: '1', - flexDirection: 'row', - minHeight: 0, - }, - middleLeft: { - display: 'flex', - flex: '1', - flexDirection: 'column', - minHeight: 0, - }, - primaryWindow: { - display: 'flex', - flex: '1', - height: '300px', - minHeight: 0, - position: 'relative', - }, - thumbnailArea: { - backgroundColor: theme.palette.shades?.dark, - }, - thumbnailAreaBottom: { - }, - thumbnailAreaRight: { - minWidth: 100, - }, - window: { - '&$maximized': { - left: 0, - position: 'absolute', - top: 0, - zIndex: theme.zIndex.modal - 1, - }, - backgroundColor: theme.palette.shades?.dark, - borderRadius: 0, - display: 'flex', - flexDirection: 'column', - height: '100%', - minHeight: 0, - overflow: 'hidden', - width: '100%', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('Window'), ); diff --git a/src/containers/WindowAuthenticationBar.js b/src/containers/WindowAuthenticationBar.js index f3b7f8733367a8b7f2adf4c1e35f74d49b3353f1..b38a244aa80c6a2c31568c07caff1133affde8ab 100644 --- a/src/containers/WindowAuthenticationBar.js +++ b/src/containers/WindowAuthenticationBar.js @@ -1,62 +1,10 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; -import { alpha } from '@material-ui/core/styles/colorManipulator'; import { withPlugins } from '../extend/withPlugins'; import { WindowAuthenticationBar } from '../components/WindowAuthenticationBar'; -/** - * @param theme - * @returns {{typographyBody: {flexGrow: number, fontSize: number|string}, - * windowTopBarStyle: {minHeight: number, paddingLeft: number, backgroundColor: string}}} - */ -const styles = theme => ({ - buttonInvert: { - '&:hover': { - backgroundColor: alpha(theme.palette.secondary.contrastText, 1 - theme.palette.action.hoverOpacity), - }, - backgroundColor: theme.palette.secondary.contrastText, - marginLeft: theme.spacing(5), - paddingBottom: 0, - paddingTop: 0, - }, - expanded: { - paddingLeft: theme.spacing(), - paddingRight: theme.spacing(), - }, - failure: { - backgroundColor: theme.palette.error.dark, - }, - fauxButton: { - marginLeft: theme.spacing(2.5), - }, - icon: { - marginRight: theme.spacing(1.5), - verticalAlign: 'text-bottom', - }, - label: { - lineHeight: 2.25, - }, - paper: { - backgroundColor: theme.palette.secondary.main, - color: theme.palette.secondary.contrastText, - cursor: 'pointer', - }, - topBar: { - '&:hover': { - backgroundColor: theme.palette.secondary.main, - }, - alignItems: 'center', - display: 'flex', - justifyContent: 'inherit', - padding: theme.spacing(1), - textTransform: 'none', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), withPlugins('WindowAuthenticationBar'), ); diff --git a/src/containers/WindowCanvasNavigationControls.js b/src/containers/WindowCanvasNavigationControls.js index f1f17053f8af45c4d492d70f51b93e49771e38d2..ca5088f34b6e32c34ef511435f01dd01143add70 100644 --- a/src/containers/WindowCanvasNavigationControls.js +++ b/src/containers/WindowCanvasNavigationControls.js @@ -1,45 +1,18 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withSize } from 'react-sizeme'; -import { withStyles } from '@material-ui/core'; -import { alpha } from '@material-ui/core/styles/colorManipulator'; import { withPlugins } from '../extend/withPlugins'; -import { getWorkspace } from '../state/selectors'; +import { getShowZoomControlsConfig, getWorkspace } from '../state/selectors'; import { WindowCanvasNavigationControls } from '../components/WindowCanvasNavigationControls'; /** */ const mapStateToProps = (state, { windowId }) => ({ + showZoomControls: getShowZoomControlsConfig(state), visible: getWorkspace(state).focusedWindowId === windowId, }); -/** - * - * @param theme - */ -const styles = theme => ({ - canvasNav: { - cursor: 'default', - display: 'flex', - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'center', - textAlign: 'center', - }, - canvasNavStacked: { - flexDirection: 'column', - }, - controls: { - backgroundColor: alpha(theme.palette.background.paper, 0.5), - bottom: 0, - position: 'absolute', - width: '100%', - zIndex: 50, - }, -}); - const enhance = compose( connect(mapStateToProps), - withStyles(styles), withSize(), withPlugins('WindowCanvasNavigationControls'), ); diff --git a/src/containers/WindowListButton.js b/src/containers/WindowListButton.js index 74506d9dbaec7e421ae6288d6eb93e64210aeaec..68e984e497f7addbed5b701fcc07ec7499cae9bf 100644 --- a/src/containers/WindowListButton.js +++ b/src/containers/WindowListButton.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getWindowIds, getWorkspace } from '../state/selectors'; import { WindowListButton } from '../components/WindowListButton'; @@ -12,26 +11,8 @@ const mapStateToProps = (state) => ({ windowCount: getWindowIds(state).length, }); -/** - * - * @param theme - * @returns {{background: {background: string}}} - */ -const styles = theme => ({ - badge: { - paddingLeft: 12, - }, - ctrlBtn: { - margin: theme.spacing(1), - }, - ctrlBtnSelected: { - backgroundColor: theme.palette.action.selected, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, null), withPlugins('WindowListButton'), ); diff --git a/src/containers/WindowSideBar.js b/src/containers/WindowSideBar.js index 012b2fd5d5cf273865948294150c6bb72c6d8043..022e34f310dbb64f9f64c5492c165314e84c7430 100644 --- a/src/containers/WindowSideBar.js +++ b/src/containers/WindowSideBar.js @@ -1,6 +1,5 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; -import { withStyles } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import { WindowSideBar } from '../components/WindowSideBar'; @@ -19,32 +18,7 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** - * - * @param theme - * @returns {{toolbar: CSSProperties | toolbar | {minHeight}, grow: {flexGrow: number}, - * drawer: {overflowX: string, left: number, flexShrink: number, width: number, height: string}}} - */ -const styles = theme => ({ - drawer: { - flexShrink: 0, - height: '100%', - order: -1000, - zIndex: theme.zIndex.appBar - 1, - }, - grow: { - flexGrow: 1, - }, - paper: { - borderInlineEnd: `1px solid ${theme.palette.divider}`, - overflowX: 'hidden', - width: 48, - }, - toolbar: theme.mixins.toolbar, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, null), withPlugins('WindowSideBar'), diff --git a/src/containers/WindowSideBarAnnotationsPanel.js b/src/containers/WindowSideBarAnnotationsPanel.js index 904cea2f93053bcbca8d2c0903931f934ccc6e9d..8370a0c375692dbbf729443fe0607ebab4de0621 100644 --- a/src/containers/WindowSideBarAnnotationsPanel.js +++ b/src/containers/WindowSideBarAnnotationsPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getVisibleCanvasIds, @@ -22,20 +21,8 @@ const mapStateToProps = (state, { windowId }) => ({ canvasIds: getVisibleCanvasIds(state, { windowId }), }); -/** */ -const styles = theme => ({ - section: { - borderBottom: `.5px solid ${theme.palette.section_divider}`, - paddingBottom: theme.spacing(1), - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(1), - paddingTop: theme.spacing(2), - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, null), withPlugins('WindowSideBarAnnotationsPanel'), // further HOC diff --git a/src/containers/WindowSideBarButtons.js b/src/containers/WindowSideBarButtons.js index 8449e9b6a28c6a0c2067618a8933da7fa888aa68..3b0511ae0fa9c694439f68416106ef173881b1d4 100644 --- a/src/containers/WindowSideBarButtons.js +++ b/src/containers/WindowSideBarButtons.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; @@ -65,49 +64,8 @@ const mapStateToProps = (state, { windowId }) => ({ sideBarPanel: ((getCompanionWindowsForPosition(state, { position: 'left', windowId }))[0] || {}).content, }); -/** */ -const style = theme => ({ - badge: { - backgroundColor: theme.palette.notification?.main, - }, - tab: { - '&:active': { - backgroundColor: theme.palette.action?.active, - }, - '&:focus': { - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - backgroundColor: theme.palette.action?.hover, - textDecoration: 'none', - // Reset on touch devices, it doesn't add specificity - }, - '&:hover': { - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - backgroundColor: theme.palette.action?.hover, - textDecoration: 'none', - // Reset on touch devices, it doesn't add specificity - }, - - borderRight: '2px solid transparent', - minWidth: 'auto', - }, - tabSelected: { - borderRight: `2px solid ${theme.palette.primary?.main}`, - }, - tabsFlexContainer: { - flexDirection: 'column', - }, - tabsIndicator: { - display: 'none', - }, -}); - const enhance = compose( withTranslation(), - withStyles(style), connect(mapStateToProps, mapDispatchToProps), withPlugins('WindowSideBarButtons'), ); diff --git a/src/containers/WindowSideBarCanvasPanel.js b/src/containers/WindowSideBarCanvasPanel.js index 24324d253865b996f88b608e36c67245c11a0f36..e6b4befc734f459a96201631e3d1be18cb0b94cb 100644 --- a/src/containers/WindowSideBarCanvasPanel.js +++ b/src/containers/WindowSideBarCanvasPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { WindowSideBarCanvasPanel } from '../components/WindowSideBarCanvasPanel'; @@ -53,37 +52,8 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({ ), }); -/** - * - * @param theme - */ -const styles = theme => ({ - break: { - flexBasis: '100%', - height: 0, - }, - collectionNavigationButton: { - textTransform: 'none', - }, - label: { - paddingLeft: theme.spacing(1), - }, - select: { - '&:focus': { - backgroundColor: theme.palette.background.paper, - }, - }, - selectEmpty: { - backgroundColor: theme.palette.background.paper, - }, - variantTab: { - minWidth: 'auto', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WindowSideBarCanvasPanel'), ); diff --git a/src/containers/WindowSideBarCollectionPanel.js b/src/containers/WindowSideBarCollectionPanel.js index 32b31a77b283f90756a568cffe44be7282f31d5b..738c11125a4d23e87226f674a85ee2423d71603c 100644 --- a/src/containers/WindowSideBarCollectionPanel.js +++ b/src/containers/WindowSideBarCollectionPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { @@ -56,22 +55,7 @@ const mapDispatchToProps = (dispatch, { id, windowId }) => ({ updateWindow: (...args) => dispatch(actions.updateWindow(windowId, ...args)), }); -/** - * Styles for withStyles HOC - */ -const styles = theme => ({ - label: { - paddingLeft: theme.spacing(1), - }, - menuItem: { - borderBottom: `0.5px solid ${theme.palette.divider}`, - paddingRight: theme.spacing(1), - whiteSpace: 'normal', - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), connect(mapStateToProps, mapDispatchToProps), withPlugins('WindowSideBarCollectionPanel'), diff --git a/src/containers/WindowSideBarInfoPanel.js b/src/containers/WindowSideBarInfoPanel.js index 9fc498ce117521c64e176a2559bebd8c8b40692d..19de86776b88a5277762ce3e4b8c74eb95cf9e86 100644 --- a/src/containers/WindowSideBarInfoPanel.js +++ b/src/containers/WindowSideBarInfoPanel.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { @@ -33,24 +32,8 @@ const mapDispatchToProps = (dispatch, { windowId, id }) => ({ setLocale: locale => dispatch(actions.updateCompanionWindow(windowId, id, { locale })), }); -/** - * - * @param theme - * @returns {label: {paddingLeft: number}}} - */ -const styles = theme => ({ - section: { - borderBottom: `.5px solid ${theme.palette.section_divider}`, - paddingBottom: theme.spacing(1), - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(1), - paddingTop: theme.spacing(2), - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WindowSideBarInfoPanel'), ); diff --git a/src/containers/WindowThumbnailSettings.js b/src/containers/WindowThumbnailSettings.js index 24878457a780b19e3e280e0e289179636d0fb28b..d773247feef8d32d57fcaa1835dba73c3eb9dff3 100644 --- a/src/containers/WindowThumbnailSettings.js +++ b/src/containers/WindowThumbnailSettings.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { getThumbnailNavigationPosition, getThemeDirection } from '../state/selectors'; @@ -26,22 +25,7 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** */ -const styles = theme => ({ - label: { - borderBottom: '2px solid transparent', - }, - MenuItem: { - display: 'inline-block', - }, - selectedLabel: { - borderBottom: `2px solid ${theme.palette.secondary.main}`, - color: theme.palette.secondary.main, - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(null, { withRef: true }), connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true }), withPlugins('WindowThumbnailSettings'), diff --git a/src/containers/WindowTopBar.js b/src/containers/WindowTopBar.js index 27dd9b83248c59267293ebb9b86512ef3b8a13e1..ae9fc34b90eef656931403890f734f980993ac6b 100644 --- a/src/containers/WindowTopBar.js +++ b/src/containers/WindowTopBar.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { getWindowConfig, isFocused } from '../state/selectors'; @@ -35,31 +34,8 @@ const mapDispatchToProps = (dispatch, { windowId }) => ({ toggleWindowSideBar: () => dispatch(actions.toggleWindowSideBar(windowId)), }); -/** - * @param theme - * @returns {{typographyBody: {flexGrow: number, fontSize: number|string}, - * windowTopBarStyle: {minHeight: number, paddingLeft: number, backgroundColor: string}}} - */ -const styles = theme => ({ - focused: {}, - windowTopBarStyle: { - '&$focused': { - borderTop: `2px solid ${theme.palette.primary.main}`, - }, - backgroundColor: theme.palette.shades?.main, - borderTop: '2px solid transparent', - minHeight: 32, - paddingLeft: theme.spacing(0.5), - paddingRight: theme.spacing(0.5), - }, - windowTopBarStyleDraggable: { - cursor: 'move', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WindowTopBar'), ); diff --git a/src/containers/WindowTopBarPluginArea.js b/src/containers/WindowTopBarPluginArea.js index 4bcdc88f5ae95da56b721b52d856012ded348c0a..cf05b64af3e923718c58de9d28f952c2e8147d50 100644 --- a/src/containers/WindowTopBarPluginArea.js +++ b/src/containers/WindowTopBarPluginArea.js @@ -1,17 +1,11 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { WindowTopBarPluginArea } from '../components/WindowTopBarPluginArea'; -/** - */ -const styles = {}; - const enhance = compose( withTranslation(), - withStyles(styles), connect(null, null), withPlugins('WindowTopBarPluginArea'), ); diff --git a/src/containers/WindowTopBarPluginMenu.js b/src/containers/WindowTopBarPluginMenu.js index 510d7a35de8418cb7c831e145e9d5178cc0092e2..4900528e9f5cd7ca363f978ced229ed8c46bff41 100644 --- a/src/containers/WindowTopBarPluginMenu.js +++ b/src/containers/WindowTopBarPluginMenu.js @@ -1,25 +1,12 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { WindowTopBarPluginMenu } from '../components/WindowTopBarPluginMenu'; import { withWorkspaceContext } from '../contexts/WorkspaceContext'; -/** - * - * @param theme - * @returns {{ctrlBtn: {margin: (number|string)}}} - */ -const styles = theme => ({ - ctrlBtnSelected: { - backgroundColor: theme.palette.action.selected, - }, -}); - const enhance = compose( withTranslation(), withWorkspaceContext, - withStyles(styles), withPlugins('WindowTopBarPluginMenu'), ); diff --git a/src/containers/WindowTopBarTitle.js b/src/containers/WindowTopBarTitle.js index 5cb51b8274b7ffc7b1bca026c5b5fe5cfea0207f..fbdf31288be412e6bddec49981aa5a6a0a4838d3 100644 --- a/src/containers/WindowTopBarTitle.js +++ b/src/containers/WindowTopBarTitle.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { getManifestStatus, getManifestTitle, getWindowConfig } from '../state/selectors'; import { WindowTopBarTitle } from '../components/WindowTopBarTitle'; @@ -14,20 +13,8 @@ const mapStateToProps = (state, { windowId }) => ({ manifestTitle: getManifestTitle(state, { windowId }), }); -/** - * @param theme - */ -const styles = theme => ({ - title: { - ...theme.typography.h6, - flexGrow: 1, - paddingLeft: theme.spacing(0.5), - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, null), withPlugins('WindowTopBarTitle'), ); diff --git a/src/containers/WindowTopMenuButton.js b/src/containers/WindowTopMenuButton.js index 54d863e2b688ca032ac5ed7af9e093e176b7fc18..8a114a8dea2d3d6f7c2b455ab27f820b8f97653d 100644 --- a/src/containers/WindowTopMenuButton.js +++ b/src/containers/WindowTopMenuButton.js @@ -1,23 +1,10 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { WindowTopMenuButton } from '../components/WindowTopMenuButton'; -/** - * - * @param theme - * @returns {{ctrlBtn: {margin: (number|string)}}} - */ -const styles = theme => ({ - ctrlBtnSelected: { - backgroundColor: theme.palette.action.selected, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), withPlugins('WindowTopMenuButton'), ); diff --git a/src/containers/WindowViewSettings.js b/src/containers/WindowViewSettings.js index 6fc417f284b1f5b585c85ec369ddbd31946ece8a..3e34e5949b1ede7323c50648a8699cd3f111dfe7 100644 --- a/src/containers/WindowViewSettings.js +++ b/src/containers/WindowViewSettings.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { getAllowedWindowViewTypes, getWindowViewType } from '../state/selectors'; @@ -26,22 +25,7 @@ const mapStateToProps = (state, { windowId }) => ( } ); -/** */ -const styles = theme => ({ - label: { - borderBottom: '2px solid transparent', - }, - MenuItem: { - display: 'inline-block', - }, - selectedLabel: { - borderBottom: `2px solid ${theme.palette.secondary.main}`, - color: theme.palette.secondary.main, - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(null, { withRef: true }), connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true }), withPlugins('WindowViewSettings'), diff --git a/src/containers/Workspace.js b/src/containers/Workspace.js index 6eac59013b39931e67ce89e965cb58bfa3678c7b..f9288e8d94c750a2e8bcafb881abbbea7779bf55 100644 --- a/src/containers/Workspace.js +++ b/src/containers/Workspace.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { Workspace } from '../components/Workspace'; import { @@ -18,7 +17,6 @@ import * as actions from '../state/actions'; const mapStateToProps = state => ( { allowNewWindows: getConfig(state).workspace.allowNewWindows, - isWorkspaceControlPanelVisible: getConfig(state).workspaceControlPanel.enabled, maximizedWindowIds: getMaximizedWindowsIds(state), windowIds: getWindowIds(state), workspaceId: getWorkspace(state).id, @@ -35,35 +33,8 @@ const mapDispatchToProps = { addWindow: actions.addWindow, }; -/** - * @param theme - */ -const styles = theme => ({ - workspaceViewport: { - bottom: 0, - left: 0, - margin: 0, - overflow: 'hidden', - position: 'absolute', - right: 0, - top: 0, - }, - workspaceWithControlPanel: { - paddingTop: 74, - }, - // injection order matters here - // eslint-disable-next-line sort-keys - '@media (min-width: 600px)': { - workspaceWithControlPanel: { - paddingLeft: 68, - paddingTop: 0, - }, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('Workspace'), // further HOC go here diff --git a/src/containers/WorkspaceAdd.js b/src/containers/WorkspaceAdd.js index 25f8903234c26197b3b0746936db93a93ef8eda6..0c0883945d1b04b1e8ea07aa1e822f5486ac15b9 100644 --- a/src/containers/WorkspaceAdd.js +++ b/src/containers/WorkspaceAdd.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { WorkspaceAdd } from '../components/WorkspaceAdd'; @@ -24,73 +23,8 @@ const mapDispatchToProps = { setWorkspaceAddVisibility: actions.setWorkspaceAddVisibility, }; -/** - * - * @param theme - * @returns {{typographyBody: {flexGrow: number, fontSize: string}, - * form: {paddingBottom: number, paddingTop: number, marginTop: number}, - * fab: {bottom: number, position: string, right: number}, - * menuButton: {marginRight: number, marginLeft: number}}} - */ -const styles = theme => ({ - displayNone: { - display: 'none', - }, - fab: { - bottom: theme.spacing(2), - position: 'absolute', - right: theme.spacing(2), - }, - form: { - left: '0', - marginTop: 48, - paddingBottom: theme.spacing(2), - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(2), - [theme.breakpoints.up('sm')]: { - paddingLeft: theme.spacing(3), - paddingRight: theme.spacing(3), - }, - paddingTop: theme.spacing(2), - right: '0', - }, - list: { - margin: '16px', - }, - menuButton: { - marginLeft: -12, - marginRight: 20, - }, - paper: { - borderTop: '0', - left: '0', - [theme.breakpoints.up('sm')]: { - left: '65px', - }, - }, - typographyBody: { - flexGrow: 1, - }, - workspaceAdd: { - boxSizing: 'border-box', - height: '100%', - overflowX: 'hidden', - overflowY: 'auto', - paddingTop: 68, - }, - // injection order matters - // eslint-disable-next-line sort-keys - '@media (min-width: 600px)': { - workspaceAdd: { - paddingLeft: 68, - paddingTop: 0, - }, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WorkspaceAdd'), ); diff --git a/src/containers/WorkspaceAddButton.js b/src/containers/WorkspaceAddButton.js index 427676b481fa75442a6f9ae9eba09eba199e7151..cec63fcb4a2e3b12701ff83936e7a44349af6e4f 100644 --- a/src/containers/WorkspaceAddButton.js +++ b/src/containers/WorkspaceAddButton.js @@ -1,13 +1,37 @@ import { connect } from 'react-redux'; import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; -import withWidth from '@material-ui/core/withWidth'; +import { useTheme } from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import { getWindowIds, getWorkspace } from '../state/selectors'; import { WorkspaceAddButton } from '../components/WorkspaceAddButton'; +/** + * Be careful using this hook. It only works because the number of + * breakpoints in theme is static. It will break once you change the number of + * breakpoints. See https://legacy.reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level + */ +function useWidth() { + const theme = useTheme(); + const keys = [...theme.breakpoints.keys].reverse(); + return ( + keys.reduce((output, key) => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const matches = useMediaQuery(theme.breakpoints.up(key)); + return !output && matches ? key : output; + }, null) || 'xs' + ); +} + +/** + * withWidth + * @memberof WorkspaceControlPanel + * @private + */ +const withWidth = () => (WrappedComponent) => (props) => <WrappedComponent {...props} width={useWidth()} />; + /** * mapStateToProps - to hook up connect * @memberof WorkspaceControlPanel @@ -32,30 +56,8 @@ const mapStateToProps = (state, { width }) => { */ const mapDispatchToProps = { setWorkspaceAddVisibility: actions.setWorkspaceAddVisibility }; -/** - * - * @param theme - * @returns {{ctrlBtn: {margin: (number|string)}}} - */ -const styles = theme => ({ - fab: { - margin: theme.spacing(1), - }, - fabPrimary: { - '&:focus': { - backgroundColor: theme.palette.primary.dark, - }, - }, - fabSecondary: { - '&:focus': { - backgroundColor: theme.palette.secondary.dark, - }, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), withWidth({ initialWidth: 'xs' }), connect(mapStateToProps, mapDispatchToProps), withPlugins('WorkspaceAddButton'), diff --git a/src/containers/WorkspaceArea.js b/src/containers/WorkspaceArea.js index c2655978c98e9a934c485eccbf5984192b2aa31a..f58fa21317a454ecbeba92513635bb76dc634b74 100644 --- a/src/containers/WorkspaceArea.js +++ b/src/containers/WorkspaceArea.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles, lighten, darken } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceArea } from '../components/WorkspaceArea'; import { getConfig, getWindowIds, getWorkspace } from '../state/selectors'; @@ -20,30 +19,8 @@ const mapStateToProps = state => ( } ); -/** - * - * @param theme - * @returns {{background: {background: string}}} - */ -const styles = (theme) => { - const getBackgroundColor = theme.palette.type === 'light' ? darken : lighten; - - return { - viewer: { - background: getBackgroundColor(theme.palette.shades.light, 0.1), - bottom: 0, - left: 0, - overflow: 'hidden', - position: 'absolute', - right: 0, - top: 0, - }, - }; -}; - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps), withPlugins('WorkspaceArea'), ); diff --git a/src/containers/WorkspaceControlPanel.js b/src/containers/WorkspaceControlPanel.js index 88f482e91d3a8cb92d78770c855eae6e7bda8907..e23bb417a950d24c6d7cb7bd6ec5bd690758e92b 100644 --- a/src/containers/WorkspaceControlPanel.js +++ b/src/containers/WorkspaceControlPanel.js @@ -1,69 +1,10 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceControlPanel } from '../components/WorkspaceControlPanel'; -/** - * - * @param theme - * @returns {{ctrlBtn: {margin: (number|string)}, - * drawer: {overflowX: string, height: string}}} - */ -const styles = theme => ({ - branding: { - display: 'flex', - position: 'absolute', - [theme.breakpoints.up('xs')]: { - display: 'none', - }, - [theme.breakpoints.up('sm')]: { - bottom: 0, - display: 'block', - float: 'none', - right: 'auto', - width: '100%', - }, - right: 0, - }, - ctrlBtn: { - margin: theme.spacing(1), - }, - drawer: { - overflowX: 'hidden', - }, - root: { - height: 64, - [theme.breakpoints.up('sm')]: { - height: '100%', - left: 0, - right: 'auto', - width: 64, - }, - }, - toolbar: { - display: 'flex', - justifyContent: 'space-between', - [theme.breakpoints.up('sm')]: { - flexDirection: 'column', - justifyContent: 'flex-start', - minHeight: 0, - }, - }, - wide: { - width: 'auto', - }, - workspaceButtons: { - [theme.breakpoints.up('sm')]: { - display: 'flex', - flexDirection: 'column', - }, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), withPlugins('WorkspaceControlPanel'), // further HOC go here ); diff --git a/src/containers/WorkspaceControlPanelButtons.js b/src/containers/WorkspaceControlPanelButtons.js index eaef08bd775196782632a0bb6022759aa41b3d57..9d500357b4190a08d0a7b3a1a278fc067c06beb2 100644 --- a/src/containers/WorkspaceControlPanelButtons.js +++ b/src/containers/WorkspaceControlPanelButtons.js @@ -1,21 +1,8 @@ import { compose } from 'redux'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceControlPanelButtons } from '../components/WorkspaceControlPanelButtons'; -/** - * - * @param theme - * @returns {{ctrlBtn: {margin: (number|string)}}} - */ -const styles = theme => ({ - ctrlBtn: { - margin: theme.spacing(1), - }, -}); - const enhance = compose( - withStyles(styles), withPlugins('WorkspaceControlPanelButtons'), ); diff --git a/src/containers/WorkspaceElastic.js b/src/containers/WorkspaceElastic.js index 9badc4614bbc259c42c3f64d6ca1654b37343fcc..5cc72f575697d02c3a32bdb5ddb7394ad1ffd505 100644 --- a/src/containers/WorkspaceElastic.js +++ b/src/containers/WorkspaceElastic.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; import WorkspaceElastic from '../components/WorkspaceElastic'; @@ -44,22 +43,7 @@ const mapDispatchToProps = (dispatch, props) => ({ }, }); -const styles = { - workspace: { - boxSizing: 'border-box', - margin: 0, - position: 'absolute', - transitionDuration: '.7s', - // order matters - // eslint-disable-next-line sort-keys - '& .react-draggable-dragging': { - transitionDuration: 'unset', - }, - }, -}; - const enhance = compose( - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WorkspaceElastic'), // further HOC go here diff --git a/src/containers/WorkspaceElasticWindow.js b/src/containers/WorkspaceElasticWindow.js index 1c071396f719be6aafabf396f8611b0bbab612c2..7e6b5ccac2ff2756781fd1a66208580554442e78 100644 --- a/src/containers/WorkspaceElasticWindow.js +++ b/src/containers/WorkspaceElasticWindow.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core'; import * as actions from '../state/actions'; import WorkspaceElasticWindow from '../components/WorkspaceElasticWindow'; import { @@ -36,17 +35,7 @@ const mapDispatchToProps = (dispatch, props) => ({ }, }); -/** - * @param theme - */ -const styles = theme => ({ - focused: { - zIndex: theme.zIndex.modal - 1, - }, -}); - const enhance = compose( - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), // further HOC go here ); diff --git a/src/containers/WorkspaceExport.js b/src/containers/WorkspaceExport.js index e1ac982c10aa03c0cff9451dd1c3fbb63bac98ab..069a52542815f19231609bdfe7e0f81997efbff7 100644 --- a/src/containers/WorkspaceExport.js +++ b/src/containers/WorkspaceExport.js @@ -1,6 +1,5 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; import { withTranslation } from 'react-i18next'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceExport } from '../components/WorkspaceExport'; @@ -17,18 +16,8 @@ const mapStateToProps = state => ({ exportableState: getExportableState(state), }); -/** - * Styles for the withStyles HOC - */ -const styles = theme => ({ - accordionTitle: { - padding: 0, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, {}), withPlugins('WorkspaceExport'), ); diff --git a/src/containers/WorkspaceImport.js b/src/containers/WorkspaceImport.js index 01c22d154776e0dff666ef3a28af1df9f5daaf1a..c529bca89a68af3d2f688ed4f4617670cf141076 100644 --- a/src/containers/WorkspaceImport.js +++ b/src/containers/WorkspaceImport.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceImport } from '../components/WorkspaceImport'; import * as actions from '../state/actions'; @@ -16,22 +15,8 @@ const mapDispatchToProps = { importConfig: actions.importMiradorState, }; -/** */ -const styles = theme => ({ - cancelBtn: { - color: theme.palette.text.primary, - }, - textField: { - width: '100%', - }, - textInput: { - fontFamily: 'monospace', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(null, mapDispatchToProps), withPlugins('WorkspaceImport'), ); diff --git a/src/containers/WorkspaceMenuButton.js b/src/containers/WorkspaceMenuButton.js index 16e10d569baeeb4c63666db50e14fddf26cbeca5..b5f2518eb255db4cf81d47c6ab1b68d9ed602931 100644 --- a/src/containers/WorkspaceMenuButton.js +++ b/src/containers/WorkspaceMenuButton.js @@ -1,26 +1,10 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceMenuButton } from '../components/WorkspaceMenuButton'; -/** - * - * @param theme - * @returns {{ctrlBtn: {margin: (number|string)}}} - */ -const styles = theme => ({ - ctrlBtn: { - margin: theme.spacing(1), - }, - ctrlBtnSelected: { - backgroundColor: theme.palette.action.selected, - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), withPlugins('WorkspaceMenuButton'), // further HOC ); diff --git a/src/containers/WorkspaceMosaic.js b/src/containers/WorkspaceMosaic.js index b66e2f4aa67fd4741712f7d9e131b052e693b9f6..4ab6529d059266edca949428ed7c0ff7a6c2b509 100644 --- a/src/containers/WorkspaceMosaic.js +++ b/src/containers/WorkspaceMosaic.js @@ -1,11 +1,9 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { getWorkspace } from '../state/selectors'; import * as actions from '../state/actions'; import { WorkspaceMosaic } from '../components/WorkspaceMosaic'; -import globalReactMosaicStyles from '../styles/react-mosaic-component'; /** * mapStateToProps - to hook up connect @@ -27,26 +25,7 @@ const mapStateToProps = state => ( */ const mapDispatchToProps = { updateWorkspaceMosaicLayout: actions.updateWorkspaceMosaicLayout }; -const styles = { - root: { - '& .mosaic-preview': { - boxShadow: 'none', - }, - '& .mosaic-tile': { - boxShadow: '0 1px 3px 0 rgba(0, 0, 0, .2), 0 1px 1px 0 rgba(0, 0, 0, .2), 0 2px 1px -1px rgba(0, 0, 0, .2)', - }, - '& .mosaic-window': { - boxShadow: 'none', - }, - '& .mosaic-window-toolbar': { - display: 'none !important', - }, - }, - ...globalReactMosaicStyles, -}; - const enhance = compose( - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WorkspaceMosaic'), // further HOC go here diff --git a/src/containers/WorkspaceOptionsButton.js b/src/containers/WorkspaceOptionsButton.js index f17b51310f56018393b23b6593ff87da902f2857..5768c4cd2aae646aef2e586106f357cd3856fd59 100644 --- a/src/containers/WorkspaceOptionsButton.js +++ b/src/containers/WorkspaceOptionsButton.js @@ -1,24 +1,9 @@ import { compose } from 'redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core/styles'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceOptionsButton } from '../components/WorkspaceOptionsButton'; -/** - * - * @param theme - */ -const styles = theme => ({ - ctrlBtn: { - margin: theme.spacing(1), - }, - ctrlBtnSelected: { - backgroundColor: theme.palette.action.selected, - }, -}); - const enhance = compose( - withStyles(styles), withTranslation(), withPlugins('WorkspaceOptionsButton'), ); diff --git a/src/containers/WorkspaceSelectionDialog.js b/src/containers/WorkspaceSelectionDialog.js index d14881af77bf8f0a0a894d50cf82b821910c037f..3305e5321672755ccd59391bc0d61d330e5b17a5 100644 --- a/src/containers/WorkspaceSelectionDialog.js +++ b/src/containers/WorkspaceSelectionDialog.js @@ -1,7 +1,6 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import { WorkspaceSelectionDialog } from '../components/WorkspaceSelectionDialog'; import * as actions from '../state/actions'; @@ -23,60 +22,8 @@ const mapDispatchToProps = { */ const mapStateToProps = state => ({ workspaceType: getWorkspaceType(state) }); -/** */ -const styles = theme => ({ - card: { - backgroundColor: 'transparent', - borderRadius: '0', - boxShadow: '0 0 transparent', - display: 'flex', - }, - content: { - flex: '1 0 auto', - }, - details: { - display: 'flex', - flexDirection: 'column', - }, - headline: { - paddingBottom: '6px', - }, - list: { - '&active': { - outline: 'none', - }, - '&focus': { - outline: 'none', - }, - outline: 'none', - }, - media: { - flex: '0 0 120px', - height: '90px', - }, - menuItem: { - height: 'auto', - overflow: 'auto', - whiteSpace: 'inherit', - }, - root: { - '&:last-child': { - paddingBottom: '12px', - }, - paddingBottom: 0, - paddingTop: 0, - textAlign: 'left', - }, - svgIcon: { - flexShrink: 0, - height: '90px', - width: '120px', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('WorkspaceSelectionDialog'), ); diff --git a/src/containers/ZoomControls.js b/src/containers/ZoomControls.js index 512ce860cbef74cb3534084c3d8ba9d0e06db0bc..1cc85d545509720db14d9af2c07a189a5e10b7a6 100644 --- a/src/containers/ZoomControls.js +++ b/src/containers/ZoomControls.js @@ -1,10 +1,9 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { withTranslation } from 'react-i18next'; -import { withStyles } from '@material-ui/core'; import { withPlugins } from '../extend/withPlugins'; import * as actions from '../state/actions'; -import { getShowZoomControlsConfig, getViewer } from '../state/selectors'; +import { getViewer } from '../state/selectors'; import { ZoomControls } from '../components/ZoomControls'; /** @@ -14,7 +13,6 @@ import { ZoomControls } from '../components/ZoomControls'; */ const mapStateToProps = (state, { windowId }) => ( { - showZoomControls: getShowZoomControlsConfig(state), viewer: getViewer(state, { windowId }), } ); @@ -26,33 +24,8 @@ const mapStateToProps = (state, { windowId }) => ( */ const mapDispatchToProps = { updateViewport: actions.updateViewport }; -/** - * - * @param theme - * @returns {{zoom_controls: {position: string, right: number}, - * ListItem: {paddingBottom: number, paddingTop: number}}} - */ -const styles = theme => ({ - divider: { - borderRight: '1px solid #808080', - display: 'inline-block', - height: '24px', - margin: '12px 6px', - }, - ListItem: { - paddingBottom: 0, - paddingTop: 0, - }, - zoom_controls: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - }, -}); - const enhance = compose( withTranslation(), - withStyles(styles), connect(mapStateToProps, mapDispatchToProps), withPlugins('ZoomControls'), ); diff --git a/src/extend/pluginMapping.js b/src/extend/pluginMapping.js index efc449252abff501d89a04062516592fe7c2dd9b..7ddeefd6bb9c6e88f9cb1134abb0f66dffef05c6 100644 --- a/src/extend/pluginMapping.js +++ b/src/extend/pluginMapping.js @@ -18,7 +18,7 @@ import CompanionWindowRegistry from '../lib/CompanionWindowRegistry'; */ export function createTargetToPluginMapping(plugins) { return plugins.reduce((map, plugin) => ( - update(map, [plugin.target, plugin.mode], x => [...x || [], plugin]) + update(map, [plugin.target, plugin.mode], x => [...(x || []), plugin]) ), {}); } diff --git a/src/state/createStore.js b/src/state/createStore.js index 9467f322c5535aab6d73abcd48d0aaed5b3dc9a1..c115859260c6db7cd5398b22b308bf9572cf5b32 100644 --- a/src/state/createStore.js +++ b/src/state/createStore.js @@ -6,7 +6,7 @@ import thunkMiddleware from 'redux-thunk'; import createSagaMiddleware from 'redux-saga'; import { combineReducers, createStore, applyMiddleware } from 'redux'; -import { composeWithDevTools } from 'redux-devtools-extension'; +import { composeWithDevTools } from '@redux-devtools/extension'; import createRootReducer from './reducers/rootReducer'; import getRootSaga from './sagas'; import settings from '../config/settings'; diff --git a/src/styles/react-mosaic-component.js b/src/styles/react-mosaic-component.js index 23d46a4fb2c67f5531ab4c4e4a2fc01c3c464110..2f6885ea1208517f1943c303366875083a3d4bfd 100644 --- a/src/styles/react-mosaic-component.js +++ b/src/styles/react-mosaic-component.js @@ -1,285 +1,283 @@ // Generated using jss convert node_modules/react-mosaic-component/react-mosaic-component.css > src/styles/react-mosaic-component.js // .mosaic-blueprint-theme styles have also been removed const globalReactMosaicStyles = { - '@global': { - '.mosaic': { - height: '100%', - width: '100%', - }, - '.mosaic, .mosaic > *': { - boxSizing: 'border-box', - }, - '.mosaic .mosaic-zero-state': { - position: 'absolute', - top: 6, - right: 6, - bottom: 6, - left: 6, - width: 'auto', - height: 'auto', - zIndex: '1', - }, - '.mosaic-root': { - position: 'absolute', - top: 3, - right: 3, - bottom: 3, - left: 3, - }, - '.mosaic-split': { - position: 'absolute', - zIndex: '1', - touchAction: 'none', - }, - '.mosaic-split:hover': { - background: 'black', - }, - '.mosaic-split .mosaic-split-line': { - position: 'absolute', - }, - '.mosaic-split.-row': { - marginLeft: -3, - width: 6, - cursor: 'ew-resize', - }, - '.mosaic-split.-row .mosaic-split-line': { - top: '0', - bottom: '0', - left: 3, - right: 3, - }, - '.mosaic-split.-column': { - marginTop: -3, - height: 6, - cursor: 'ns-resize', - }, - '.mosaic-split.-column .mosaic-split-line': { - top: 3, - bottom: 3, - left: '0', - right: '0', - }, - '.mosaic-tile': { - position: 'absolute', - margin: 3, - }, - '.mosaic-tile > *': { - height: '100%', - width: '100%', - }, - '.mosaic-drop-target': { - position: 'relative', - }, - '.mosaic-drop-target.drop-target-hover .drop-target-container': { - display: 'block', - }, - '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.left': { - right: 'calc(100% - 10px )', - }, - '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.right': { - left: 'calc(100% - 10px )', - }, - '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.bottom': { - top: 'calc(100% - 10px )', - }, - '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.top': { - bottom: 'calc(100% - 10px )', - }, - '.mosaic-drop-target .drop-target-container': { - position: 'absolute', - top: '0', - right: '0', - bottom: '0', - left: '0', - display: 'none', - }, - '.mosaic-drop-target .drop-target-container.-dragging': { - display: 'block', - }, - '.mosaic-drop-target .drop-target-container .drop-target': { - position: 'absolute', - top: '0', - right: '0', - bottom: '0', - left: '0', - background: 'rgba(0, 0, 0, 0.2)', - border: '2px solid black', - opacity: '0', - zIndex: '5', - }, - '.mosaic-drop-target .drop-target-container .drop-target.left': { - right: 'calc(100% - 30% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.right': { - left: 'calc(100% - 30% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.bottom': { - top: 'calc(100% - 30% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.top': { - bottom: 'calc(100% - 30% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover': { - opacity: '1', - }, - '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.left': { - right: 'calc(100% - 50% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.right': { - left: 'calc(100% - 50% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.bottom': { - top: 'calc(100% - 50% )', - }, - '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.top': { - bottom: 'calc(100% - 50% )', - }, - '.mosaic-window, .mosaic-preview': { - position: 'relative', - display: 'flex', - fallbacks: [ - { - display: '-webkit-box', - }, - ], - webkitBoxOrient: 'vertical', - webkitBoxDirection: 'normal', - flexDirection: 'column', - overflow: 'hidden', - boxShadow: '0 0 1px rgba(0, 0, 0, 0.2)', - }, - '.mosaic-window .mosaic-window-toolbar, .mosaic-preview .mosaic-window-toolbar': { - zIndex: '4', - display: 'flex', - fallbacks: [ - { - display: '-webkit-box', - }, - ], - webkitBoxPack: 'justify', - justifyContent: 'space-between', - webkitBoxAlign: 'center', - alignItems: 'center', - flexShrink: '0', - height: 30, - background: 'white', - boxShadow: '0 1px 1px rgba(0, 0, 0, 0.2)', - }, - '.mosaic-window .mosaic-window-toolbar.draggable, .mosaic-preview .mosaic-window-toolbar.draggable': { - cursor: 'move', - }, - '.mosaic-window .mosaic-window-title, .mosaic-preview .mosaic-window-title': { - paddingLeft: 15, - webkitBoxFlex: '1', - flex: '1', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', - overflow: 'hidden', - minHeight: 18, - }, - '.mosaic-window .mosaic-window-controls, .mosaic-preview .mosaic-window-controls': { - display: 'flex', - fallbacks: [ - { - display: '-webkit-box', - }, - ], - height: '100%', - }, - '.mosaic-window .mosaic-window-controls .separator, .mosaic-preview .mosaic-window-controls .separator': { - height: 20, - borderLeft: '1px solid black', - margin: '5px 4px', - }, - '.mosaic-window .mosaic-window-body, .mosaic-preview .mosaic-window-body': { - position: 'relative', - webkitBoxFlex: '1', - flex: '1', - height: '0', - background: 'white', - zIndex: '1', - overflow: 'hidden', - }, - '.mosaic-window .mosaic-window-additional-actions-bar, .mosaic-preview .mosaic-window-additional-actions-bar': { - position: 'absolute', - top: 30, - right: '0', - bottom: 'initial', - left: '0', - height: '0', - overflow: 'hidden', - background: 'white', - webkitBoxPack: 'end', - justifyContent: 'flex-end', - display: 'flex', - fallbacks: [ - { - display: '-webkit-box', - }, - ], - zIndex: '3', - }, - '.mosaic-window .mosaic-window-additional-actions-bar .bp3-button, .mosaic-preview .mosaic-window-additional-actions-bar .bp3-button': { - margin: '0', - }, - '.mosaic-window .mosaic-window-additional-actions-bar .bp3-button:after, .mosaic-preview .mosaic-window-additional-actions-bar .bp3-button:after': { - display: 'none', - }, - '.mosaic-window .mosaic-window-body-overlay, .mosaic-preview .mosaic-window-body-overlay': { - position: 'absolute', - top: '0', - right: '0', - bottom: '0', - left: '0', - opacity: '0', - background: 'white', - display: 'none', - zIndex: '2', - }, - '.mosaic-window.additional-controls-open .mosaic-window-additional-actions-bar, .mosaic-preview.additional-controls-open .mosaic-window-additional-actions-bar': { - height: 30, - }, - '.mosaic-window.additional-controls-open .mosaic-window-body-overlay, .mosaic-preview.additional-controls-open .mosaic-window-body-overlay': { - display: 'block', - }, - '.mosaic-window .mosaic-preview, .mosaic-preview .mosaic-preview': { - height: '100%', - width: '100%', - position: 'absolute', - zIndex: '0', - border: '1px solid black', - maxHeight: 400, - }, - '.mosaic-window .mosaic-preview .mosaic-window-body, .mosaic-preview .mosaic-preview .mosaic-window-body': { - display: 'flex', - fallbacks: [ - { - display: '-webkit-box', - }, - ], - webkitBoxOrient: 'vertical', - webkitBoxDirection: 'normal', - flexDirection: 'column', - webkitBoxAlign: 'center', - alignItems: 'center', - webkitBoxPack: 'center', - justifyContent: 'center', - }, - '.mosaic-window .mosaic-preview h4, .mosaic-preview .mosaic-preview h4': { - marginBottom: 10, - }, - '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.close-button:before': { - content: '\'Close\'', - }, - '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.split-button:before': { - content: '\'Split\'', - }, - '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.replace-button:before': { - content: '\'Replace\'', - }, - '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.expand-button:before': { - content: '\'Expand\'', - }, + '.mosaic': { + height: '100%', + width: '100%', + }, + '.mosaic, .mosaic > *': { + boxSizing: 'border-box', + }, + '.mosaic .mosaic-zero-state': { + position: 'absolute', + top: 6, + right: 6, + bottom: 6, + left: 6, + width: 'auto', + height: 'auto', + zIndex: '1', + }, + '.mosaic-root': { + position: 'absolute', + top: 3, + right: 3, + bottom: 3, + left: 3, + }, + '.mosaic-split': { + position: 'absolute', + zIndex: '1', + touchAction: 'none', + }, + '.mosaic-split:hover': { + background: 'black', + }, + '.mosaic-split .mosaic-split-line': { + position: 'absolute', + }, + '.mosaic-split.-row': { + marginLeft: -3, + width: 6, + cursor: 'ew-resize', + }, + '.mosaic-split.-row .mosaic-split-line': { + top: '0', + bottom: '0', + left: 3, + right: 3, + }, + '.mosaic-split.-column': { + marginTop: -3, + height: 6, + cursor: 'ns-resize', + }, + '.mosaic-split.-column .mosaic-split-line': { + top: 3, + bottom: 3, + left: '0', + right: '0', + }, + '.mosaic-tile': { + position: 'absolute', + margin: 3, + }, + '.mosaic-tile > *': { + height: '100%', + width: '100%', + }, + '.mosaic-drop-target': { + position: 'relative', + }, + '.mosaic-drop-target.drop-target-hover .drop-target-container': { + display: 'block', + }, + '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.left': { + right: 'calc(100% - 10px )', + }, + '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.right': { + left: 'calc(100% - 10px )', + }, + '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.bottom': { + top: 'calc(100% - 10px )', + }, + '.mosaic-drop-target.mosaic > .drop-target-container .drop-target.top': { + bottom: 'calc(100% - 10px )', + }, + '.mosaic-drop-target .drop-target-container': { + position: 'absolute', + top: '0', + right: '0', + bottom: '0', + left: '0', + display: 'none', + }, + '.mosaic-drop-target .drop-target-container.-dragging': { + display: 'block', + }, + '.mosaic-drop-target .drop-target-container .drop-target': { + position: 'absolute', + top: '0', + right: '0', + bottom: '0', + left: '0', + background: 'rgba(0, 0, 0, 0.2)', + border: '2px solid black', + opacity: '0', + zIndex: '5', + }, + '.mosaic-drop-target .drop-target-container .drop-target.left': { + right: 'calc(100% - 30% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.right': { + left: 'calc(100% - 30% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.bottom': { + top: 'calc(100% - 30% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.top': { + bottom: 'calc(100% - 30% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover': { + opacity: '1', + }, + '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.left': { + right: 'calc(100% - 50% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.right': { + left: 'calc(100% - 50% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.bottom': { + top: 'calc(100% - 50% )', + }, + '.mosaic-drop-target .drop-target-container .drop-target.drop-target-hover.top': { + bottom: 'calc(100% - 50% )', + }, + '.mosaic-window, .mosaic-preview': { + position: 'relative', + display: 'flex', + fallbacks: [ + { + display: '-webkit-box', + }, + ], + webkitBoxOrient: 'vertical', + webkitBoxDirection: 'normal', + flexDirection: 'column', + overflow: 'hidden', + boxShadow: '0 0 1px rgba(0, 0, 0, 0.2)', + }, + '.mosaic-window .mosaic-window-toolbar, .mosaic-preview .mosaic-window-toolbar': { + zIndex: '4', + display: 'flex', + fallbacks: [ + { + display: '-webkit-box', + }, + ], + webkitBoxPack: 'justify', + justifyContent: 'space-between', + webkitBoxAlign: 'center', + alignItems: 'center', + flexShrink: '0', + height: 30, + background: 'white', + boxShadow: '0 1px 1px rgba(0, 0, 0, 0.2)', + }, + '.mosaic-window .mosaic-window-toolbar.draggable, .mosaic-preview .mosaic-window-toolbar.draggable': { + cursor: 'move', + }, + '.mosaic-window .mosaic-window-title, .mosaic-preview .mosaic-window-title': { + paddingLeft: 15, + webkitBoxFlex: '1', + flex: '1', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap', + overflow: 'hidden', + minHeight: 18, + }, + '.mosaic-window .mosaic-window-controls, .mosaic-preview .mosaic-window-controls': { + display: 'flex', + fallbacks: [ + { + display: '-webkit-box', + }, + ], + height: '100%', + }, + '.mosaic-window .mosaic-window-controls .separator, .mosaic-preview .mosaic-window-controls .separator': { + height: 20, + borderLeft: '1px solid black', + margin: '5px 4px', + }, + '.mosaic-window .mosaic-window-body, .mosaic-preview .mosaic-window-body': { + position: 'relative', + webkitBoxFlex: '1', + flex: '1', + height: '0', + background: 'white', + zIndex: '1', + overflow: 'hidden', + }, + '.mosaic-window .mosaic-window-additional-actions-bar, .mosaic-preview .mosaic-window-additional-actions-bar': { + position: 'absolute', + top: 30, + right: '0', + bottom: 'initial', + left: '0', + height: '0', + overflow: 'hidden', + background: 'white', + webkitBoxPack: 'end', + justifyContent: 'flex-end', + display: 'flex', + fallbacks: [ + { + display: '-webkit-box', + }, + ], + zIndex: '3', + }, + '.mosaic-window .mosaic-window-additional-actions-bar .bp3-button, .mosaic-preview .mosaic-window-additional-actions-bar .bp3-button': { + margin: '0', + }, + '.mosaic-window .mosaic-window-additional-actions-bar .bp3-button:after, .mosaic-preview .mosaic-window-additional-actions-bar .bp3-button:after': { + display: 'none', + }, + '.mosaic-window .mosaic-window-body-overlay, .mosaic-preview .mosaic-window-body-overlay': { + position: 'absolute', + top: '0', + right: '0', + bottom: '0', + left: '0', + opacity: '0', + background: 'white', + display: 'none', + zIndex: '2', + }, + '.mosaic-window.additional-controls-open .mosaic-window-additional-actions-bar, .mosaic-preview.additional-controls-open .mosaic-window-additional-actions-bar': { + height: 30, + }, + '.mosaic-window.additional-controls-open .mosaic-window-body-overlay, .mosaic-preview.additional-controls-open .mosaic-window-body-overlay': { + display: 'block', + }, + '.mosaic-window .mosaic-preview, .mosaic-preview .mosaic-preview': { + height: '100%', + width: '100%', + position: 'absolute', + zIndex: '0', + border: '1px solid black', + maxHeight: 400, + }, + '.mosaic-window .mosaic-preview .mosaic-window-body, .mosaic-preview .mosaic-preview .mosaic-window-body': { + display: 'flex', + fallbacks: [ + { + display: '-webkit-box', + }, + ], + webkitBoxOrient: 'vertical', + webkitBoxDirection: 'normal', + flexDirection: 'column', + webkitBoxAlign: 'center', + alignItems: 'center', + webkitBoxPack: 'center', + justifyContent: 'center', + }, + '.mosaic-window .mosaic-preview h4, .mosaic-preview .mosaic-preview h4': { + marginBottom: 10, + }, + '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.close-button:before': { + content: '\'Close\'', + }, + '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.split-button:before': { + content: '\'Split\'', + }, + '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.replace-button:before': { + content: '\'Replace\'', + }, + '.mosaic:not(.mosaic-blueprint-theme) .mosaic-default-control.expand-button:before': { + content: '\'Expand\'', }, };