Skip to content

Commit

Permalink
Asset Image Sidebar (#138)
Browse files Browse the repository at this point in the history
* added basic sidebar component with manager

* moved the detail tab to assets folder, added form, added a punch of css

* Apply eslint-fixer changes

* build files

* Automatic frontend build

* fixed token name typo

* added focal-point icon

* added sidebar button

* build files

* Apply eslint-fixer changes

* added download button to form

* build files

* removed transition

* build files

* fixed display bug if thers more than one entry

* added `hashPriority: low` to styles

* build files

* Automatic frontend build

---------

Co-authored-by: Corepex <[email protected]>
  • Loading branch information
Corepex and Corepex authored Mar 26, 2024
1 parent 0a09064 commit 85ab8df
Show file tree
Hide file tree
Showing 23 changed files with 531 additions and 20 deletions.
5 changes: 5 additions & 0 deletions assets/js/src/assets/icons/core/focal-point.inline.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion assets/js/src/components/icon/icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ const icons = {
'file-x-03': React.lazy(async () => await import('@Pimcore/assets/icons/core/file-x-03.inline.svg')),
'presentation-chart-01': React.lazy(async () => await import('@Pimcore/assets/icons/core/presentation-chart-01.inline.svg')),
'video-recorder': React.lazy(async () => await import('@Pimcore/assets/icons/core/video-recorder.inline.svg')),
'image-01': React.lazy(async () => await import('@Pimcore/assets/icons/core/image-01.inline.svg'))
'image-01': React.lazy(async () => await import('@Pimcore/assets/icons/core/image-01.inline.svg')),
'focal-point': React.lazy(async () => await import('@Pimcore/assets/icons/core/focal-point.inline.svg'))
}

export interface IconProps {
Expand Down
48 changes: 48 additions & 0 deletions assets/js/src/components/sidebar/sidebar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { type Meta } from '@storybook/react'
import { Sidebar } from '@Pimcore/components/sidebar/sidebar'
import { AssetEditorSidebarDetailsTab } from '@Pimcore/modules/asset/types/image/sidebar/tabs/details/details'
import { Icon } from '@Pimcore/components/icon/icon'
import React from 'react'

const config: Meta = {
title: 'Pimcore studio/UI/Sidebar',
component: (args) => {
return (
<div style={{ display: 'flex', height: '50vh' }}>
<Sidebar entries={args.entries} buttons={args.buttons} />
</div>
)
},
parameters: {
layout: 'centered'
},
tags: ['autodocs'],
argTypes: {
entries: {
table: {
disable: true
}
}
}
}

export default config

export const _default = {
args: {
entries: [
{
key: 'details',
icon: <Icon name={'view-details'} options={{ width: '16px', height: '16px' }}/>,
component: <AssetEditorSidebarDetailsTab/>
}
],
buttons: [
{
key: 'focal-point',
icon: <Icon name={'focal-point'} options={{ width: '16px', height: '16px' }}/>,
onClick: () => { console.log('focal-point button clicked') }
}
]
}
}
81 changes: 81 additions & 0 deletions assets/js/src/components/sidebar/sidebar.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { createStyles } from 'antd-style'

export const useStyle = createStyles(({ token, css }) => {
return {
sidebar: css`
display: flex;
.sidebar__navigation {
display: flex;
width: 45px;
padding: 4px 8px;
flex-direction: column;
align-items: center;
flex-shrink: 0;
align-self: stretch;
border-right: 1px solid rgba(0, 0, 0, 0.08);
border-left: 1px solid rgba(0, 0, 0, 0.08);
justify-content: space-between;
color: ${token.colorIconSidebar};
.sidebar__navigation__tabs,
.sidebar__navigation__buttons {
.pimcore-icon {
flex-shrink: 0;
color: ${token.colorIconSidebar};
&:hover {
color: ${token.colorIconHover};
cursor: pointer;
}
}
}
.sidebar__navigation__tabs {
.entry {
display: flex;
width: 45px;
padding: ${token.paddingXS}px ${token.paddingXXS}px;
justify-content: center;
align-items: center;
.pimcore-icon {
flex-shrink: 0;
color: ${token.colorIconSidebar};
&:hover {
color: ${token.colorIconHover};
cursor: pointer;
}
}
&.active {
border-right: 2px solid ${token.colorPrimaryActive};
.pimcore-icon {
color: ${token.colorPrimaryActive}
}
}
}
}
}
.sidebar__content {
padding: ${token.paddingXS}px ${token.paddingSM}px;
width: 250px;
.tab {
display: none;
&.active {
display: block;
}
}
&:not(.expanded) {
display: none;
}
}
`
}
}, { hashPriority: 'low' })
86 changes: 86 additions & 0 deletions assets/js/src/components/sidebar/sidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useStyle } from './sidebar.styles'
import React, { useState } from 'react'
import { type ISidebarButton, type ISidebarEntry } from '@Pimcore/modules/sidebar/SidebarManager'

interface SidebarProps {
entries: ISidebarEntry[]
buttons?: ISidebarButton[]
}

export const Sidebar = ({ entries, buttons = [] }: SidebarProps): React.JSX.Element => {
const { styles } = useStyle()
const preparedEntries = entries.map((entry) => {
// TODO: do we need any type of translated label here?
return {
...entry,
label: 'TRANSLATED_LABEL'
}
})
const preparedButtons = buttons?.map((button) => {
return {
...button,
label: 'TRANSLATED_LABEL'
}
})
const [activeTab, setActiveTab] = useState<string>('')

function handleSidebarClick (key: string): void {
if (key === activeTab) {
setActiveTab('')
return
}

setActiveTab(key)
}

return (
<div className={styles.sidebar}>
<div className={'sidebar__navigation'}>
<div className={'sidebar__navigation__tabs'}>
{
preparedEntries.map((entry) => {
return (
<div
key={entry.key}
className={'entry ' + (entry.key === activeTab ? 'active' : '')}
onClick={() => {
handleSidebarClick(entry.key)
}}
>
{entry.icon}
</div>
)
})
}
</div>
<div className={'sidebar__navigation__buttons'}>
{
preparedButtons.map((button) => {
return (
<div
key={button.key}
onClick={button.onClick}
>
{button.icon}
</div>
)
})
}
</div>
</div>

<div className={'sidebar__content ' + (activeTab !== '' ? 'expanded' : '')}>
{preparedEntries.map((entry, index) => {
return (
<div
key={entry.key}
className={'tab ' + (entry.key === activeTab ? 'active' : '')}
>
{entry.component}
</div>
)
})}
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { SidebarManager } from '@Pimcore/modules/sidebar/SidebarManager'

export class AssetEditorSidebarManager extends SidebarManager {}
18 changes: 18 additions & 0 deletions assets/js/src/modules/asset/types/image/sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AssetEditorSidebarManager } from '@Pimcore/modules/asset/types/image/sidebar/AssetEditorSidebarManager'
import { Icon } from '@Pimcore/components/icon/icon'
import React from 'react'
import { AssetEditorSidebarDetailsTab } from '@Pimcore/modules/asset/types/image/sidebar/tabs/details/details'

export const sidebarManager = new AssetEditorSidebarManager()

sidebarManager.registerEntry({
key: 'details',
icon: <Icon name={'view-details'} options={{ width: '16px', height: '16px' }}/>,
component: <AssetEditorSidebarDetailsTab/>
})

sidebarManager.registerButton({
key: 'focal-point',
icon: <Icon name={'focal-point'} options={{ width: '16px', height: '16px' }}/>,
onClick: () => { console.log('focal-point button clicked') }
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { createStyles } from 'antd-style'

export const useStyle = createStyles(({ token, css }) => {
return {
sidebarContentEntry: css`
.sidebar__content-label {
color: ${token.colorPrimaryActive};
line-height: 20px;
font-weight: 600;
margin: 0;
padding-bottom: ${token.paddingXS}px;
&:not(:first-of-type) {
padding-top: ${token.paddingXS}px;
}
}
`,

sidebarContentDimensions: css`
display: flex;
flex-direction: column;
align-items: flex-start;
align-self: stretch;
.entry-content__dimensions-label {
display: flex;
padding-bottom: ${token.paddingXXS};
gap: ${token.marginMD}px;
align-items: center;
gap: ${token.marginXXS};
align-self: stretch;
p {
margin: 0
}
}
.entry-content__dimensions-content {
color: ${token.colorTextDescription};
display: flex;
padding-bottom: ${token.paddingXXS};
gap: ${token.marginMD}px;
align-items: center;
gap: ${token.marginXXS};
align-self: stretch;
p {
margin: 0;
line-height: 22px;
}
}
`,

sidebarContentDownload: css`
.entry-content__download-content-thumbnail {
display: flex;
align-items: center;
gap: ${token.paddingXXS}px;
padding-bottom: ${token.paddingSM}px;
.ant-select {
flex: 1
}
}
.entry-content__download-content-custom {
.ant-form-item {
margin-bottom: 0;
}
.entry-content__download-content-custom__dimensions {
display: flex;
gap: ${token.marginSM}px;
padding-bottom: ${token.paddingSM}px;
}
.entry-content__download-content-custom__others {
display: flex;
gap: ${token.paddingXS}px;
flex-direction: column;
padding-bottom: ${token.paddingSM}px;
> div {
display: flex;
gap: ${token.marginSM}px;
>.ant-form-item {
flex: 1
}
}
}
.entry-content__download-content-custom__button {
padding: ${token.paddingXS}px 0;
}
}
`
}
}, { hashPriority: 'low' })
Loading

0 comments on commit 85ab8df

Please sign in to comment.