mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 21:19:18 +00:00
Work on sidebar share more popup
This commit is contained in:
29
ui/package-lock.json
generated
29
ui/package-lock.json
generated
@@ -13,6 +13,7 @@
|
||||
"@babel/core": "^7.14.0",
|
||||
"@babel/preset-react": "^7.13.13",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@headlessui/react": "^1.2.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@vitejs/plugin-react-refresh": "^1.3.3",
|
||||
"autoprefixer": "^10.2.5",
|
||||
@@ -22,6 +23,7 @@
|
||||
"babel-plugin-i18next-extract": "^0.8.3",
|
||||
"babel-plugin-styled-components": "^1.12.0",
|
||||
"babel-plugin-transform-semantic-ui-react-imports": "^1.4.1",
|
||||
"classnames": "^2.3.1",
|
||||
"connect-history-api-fallback": "^1.6.0",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"esbuild": "^0.11.20",
|
||||
@@ -1689,6 +1691,17 @@
|
||||
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.0.tgz",
|
||||
"integrity": "sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg=="
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.2.0.tgz",
|
||||
"integrity": "sha512-19DkLz8gDgbi+WvkoTzi9vs0NK9TJf94vbYhMzB4LYJo03Kili0gmvXT9CiKZoxXZ7YAvy/b1U1oQKEnjWrqxw==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||
@@ -4493,6 +4506,11 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||
},
|
||||
"node_modules/clean-stack": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||
@@ -15007,6 +15025,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.0.tgz",
|
||||
"integrity": "sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg=="
|
||||
},
|
||||
"@headlessui/react": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.2.0.tgz",
|
||||
"integrity": "sha512-19DkLz8gDgbi+WvkoTzi9vs0NK9TJf94vbYhMzB4LYJo03Kili0gmvXT9CiKZoxXZ7YAvy/b1U1oQKEnjWrqxw==",
|
||||
"requires": {}
|
||||
},
|
||||
"@istanbuljs/load-nyc-config": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
|
||||
@@ -17179,6 +17203,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"classnames": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
|
||||
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
|
||||
},
|
||||
"clean-stack": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"@babel/core": "^7.14.0",
|
||||
"@babel/preset-react": "^7.13.13",
|
||||
"@babel/preset-typescript": "^7.13.0",
|
||||
"@headlessui/react": "^1.2.0",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@vitejs/plugin-react-refresh": "^1.3.3",
|
||||
"autoprefixer": "^10.2.5",
|
||||
@@ -22,6 +23,7 @@
|
||||
"babel-plugin-i18next-extract": "^0.8.3",
|
||||
"babel-plugin-styled-components": "^1.12.0",
|
||||
"babel-plugin-transform-semantic-ui-react-imports": "^1.4.1",
|
||||
"classnames": "^2.3.1",
|
||||
"connect-history-api-fallback": "^1.6.0",
|
||||
"copy-to-clipboard": "^3.3.1",
|
||||
"esbuild": "^0.11.20",
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react'
|
||||
import { useMutation, useQuery, gql, useLazyQuery } from '@apollo/client'
|
||||
import copy from 'copy-to-clipboard'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import {
|
||||
sidebareDeleteShare,
|
||||
sidebareDeleteShareVariables,
|
||||
@@ -31,6 +32,9 @@ import { ReactComponent as CopyIcon } from './icons/shareCopyIcon.svg'
|
||||
import { ReactComponent as DeleteIcon } from './icons/shareDeleteIcon.svg'
|
||||
import { ReactComponent as MoreIcon } from './icons/shareMoreIcon.svg'
|
||||
import { ReactComponent as AddIcon } from './icons/shareAddIcon.svg'
|
||||
import Checkbox from '../../primitives/form/Checkbox'
|
||||
import { TextField } from '../../primitives/form/Input'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const SHARE_PHOTO_QUERY = gql`
|
||||
query sidebarGetPhotoShares($id: ID!) {
|
||||
@@ -236,6 +240,49 @@ const DELETE_SHARE_MUTATION = gql`
|
||||
// )
|
||||
// }
|
||||
|
||||
const ArrowPopoverPanel = styled.div.attrs({
|
||||
className:
|
||||
'absolute right-6 -top-3 bg-white rounded shadow-md border border-gray-200 w-[260px]',
|
||||
})`
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
right: -7px;
|
||||
width: 8px;
|
||||
height: 14px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 14'%3E%3Cpolyline stroke-width='1' stroke='%23E2E2E2' fill='%23FFFFFF' points='1 0 7 7 1 14'%3E%3C/polyline%3E%3C/svg%3E");
|
||||
}
|
||||
`
|
||||
|
||||
const MorePopover = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Popover className="relative">
|
||||
<Popover.Button
|
||||
className="align-middle p-1 ml-2"
|
||||
title={t('sidebar.sharing.more', 'More')}
|
||||
>
|
||||
<MoreIcon />
|
||||
</Popover.Button>
|
||||
|
||||
<Popover.Panel>
|
||||
<ArrowPopoverPanel>
|
||||
<div className="px-4 py-2">
|
||||
<Checkbox label="Password protected" />
|
||||
<TextField sizeVariant="small" className="mt-2" />
|
||||
</div>
|
||||
<div className="px-4 py-2 border-t border-gray-200 mt-2 mb-2">
|
||||
<Checkbox label="Expiration date" />
|
||||
<TextField sizeVariant="small" className="mt-2" />
|
||||
</div>
|
||||
</ArrowPopoverPanel>
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
type SidebarShareAlbumProps = {
|
||||
id: string
|
||||
}
|
||||
@@ -364,7 +411,7 @@ const SidebarShare = ({
|
||||
|
||||
const optionsRows = shares.map(share => (
|
||||
<tr key={share.token} className="border-gray-100 border-b border-t">
|
||||
<td className="pl-4 w-full py-2">
|
||||
<td className="pl-4 py-2 w-full">
|
||||
<span className="text-[#585858] mr-2">
|
||||
<LinkIcon className="inline-block mr-2" />
|
||||
<span className="text-xs uppercase font-bold">
|
||||
@@ -373,9 +420,9 @@ const SidebarShare = ({
|
||||
</span>
|
||||
<span className="text-sm">{share.token}</span>
|
||||
</td>
|
||||
<td className="pr-4 py-2 whitespace-nowrap text-[#5C6A7F] flex">
|
||||
<td className="pr-6 py-2 whitespace-nowrap text-[#5C6A7F] flex">
|
||||
<button
|
||||
className="align-middle p-1 ml-1"
|
||||
className="align-middle p-1 ml-2"
|
||||
title={t('sidebar.sharing.copy_link', 'Copy Link')}
|
||||
onClick={() => {
|
||||
copy(`${location.origin}/share/${share.token}`)
|
||||
@@ -387,17 +434,13 @@ const SidebarShare = ({
|
||||
onClick={() => {
|
||||
deleteShare({ variables: { token: share.token } })
|
||||
}}
|
||||
className="align-middle p-1 ml-1"
|
||||
className="align-middle p-1 ml-2 hover:text-red-600 focus:text-red-600"
|
||||
title={t('sidebar.sharing.delete', 'Delete')}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</button>
|
||||
<button
|
||||
className="align-middle p-1 ml-1"
|
||||
title={t('sidebar.sharing.more', 'More')}
|
||||
>
|
||||
<MoreIcon />
|
||||
</button>
|
||||
<MorePopover />
|
||||
|
||||
{/* <ShareItemMoreDropdown share={share} id={id} isPhoto={isPhoto} /> */}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -405,8 +448,8 @@ const SidebarShare = ({
|
||||
|
||||
if (optionsRows.length == 0) {
|
||||
optionsRows.push(
|
||||
<tr key="no-shares">
|
||||
<td colSpan={2}>
|
||||
<tr key="no-shares" className="border-gray-100 border-b border-t">
|
||||
<td colSpan={2} className="pl-4 py-2 italic text-gray-600">
|
||||
{t('sidebar.sharing.no_shares_found', 'No shares found')}
|
||||
</td>
|
||||
</tr>
|
||||
@@ -419,26 +462,28 @@ const SidebarShare = ({
|
||||
{t('sidebar.sharing.title', 'Sharing options')}
|
||||
</SidebarSectionTitle>
|
||||
<div>
|
||||
<table className="border-collapse">
|
||||
{optionsRows}
|
||||
<tr className="text-left border-gray-100 border-b border-t">
|
||||
<td colSpan={2} className="pl-4 py-2">
|
||||
<button
|
||||
className="text-[#4ABF3C] font-bold uppercase text-xs"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
shareItem({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<AddIcon className="inline-block mr-2" />
|
||||
<span>{t('sidebar.sharing.add_share', 'Add shares')}</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<table className="border-collapse w-full">
|
||||
<tbody>{optionsRows}</tbody>
|
||||
<tfoot>
|
||||
<tr className="text-left border-gray-100 border-b border-t">
|
||||
<td colSpan={2} className="pl-4 py-2">
|
||||
<button
|
||||
className="text-[#4ABF3C] font-bold uppercase text-xs"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
shareItem({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<AddIcon className="inline-block mr-2" />
|
||||
<span>{t('sidebar.sharing.add_share', 'Add shares')}</span>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</SidebarSection>
|
||||
|
||||
@@ -38,11 +38,15 @@ export const SidebarProvider = ({ children }: SidebarProviderProps) => {
|
||||
pinned: boolean
|
||||
}>({
|
||||
content: null,
|
||||
pinned: false,
|
||||
pinned: true,
|
||||
})
|
||||
|
||||
const updateSidebar = (content: React.ReactNode | null) => {
|
||||
setState(state => ({ ...state, content }))
|
||||
if (content) {
|
||||
setState(state => ({ ...state, content }))
|
||||
} else {
|
||||
setState(state => ({ ...state, content: null, pinned: false }))
|
||||
}
|
||||
}
|
||||
|
||||
const setPinned = (pinned: boolean) => {
|
||||
@@ -88,7 +92,7 @@ export const Sidebar = () => {
|
||||
className={`fixed top-[72px] bg-white bottom-0 w-full overflow-y-auto transform transition-transform motion-reduce:transition-none ${
|
||||
content == null && !pinned ? 'translate-x-full' : 'translate-x-0'
|
||||
} ${
|
||||
pinned ? 'mt-[62px]' : 'lg:shadow-separator'
|
||||
pinned ? 'lg:mt-[62px]' : 'lg:shadow-separator'
|
||||
} lg:w-[420px] lg:right-0 lg:top-0 lg:z-40`}
|
||||
>
|
||||
{content}
|
||||
|
||||
@@ -16,13 +16,19 @@ const SidebarHeader = ({ title }: SidebarHeaderProps) => {
|
||||
|
||||
return (
|
||||
<div className="m-2 flex items-center gap-2">
|
||||
{!pinned && (
|
||||
<button title="Close sidebar" onClick={() => updateSidebar(null)}>
|
||||
<CloseIcon className="m-2" />
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className={`${pinned ? 'lg:hidden' : ''}`}
|
||||
title="Close sidebar"
|
||||
onClick={() => updateSidebar(null)}
|
||||
>
|
||||
<CloseIcon className="m-2" />
|
||||
</button>
|
||||
<span className="flex-grow -mt-1">{title}</span>
|
||||
<button title="Pin sidebar" onClick={() => setPinned(!pinned)}>
|
||||
<button
|
||||
className="hidden lg:block"
|
||||
title="Pin sidebar"
|
||||
onClick={() => setPinned(!pinned)}
|
||||
>
|
||||
<PinIcon className="m-2" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="12px" height="12px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg width="12px" height="14px" viewBox="0 0 12 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path stroke="none" stroke-width="1" fill="none" d="M5.01241676,3.99743718 L5.2123926,4.0051107 C6.08060614,4.06729045 6.87900923,4.50349496 7.40038252,5.20051071 C7.56578589,5.42163587 7.52061445,5.73497915 7.29948929,5.90038252 C7.07836413,6.06578589 6.76502085,6.02061445 6.59961748,5.79948929 C6.25203528,5.33481212 5.71976656,5.04400912 5.14095753,5.00255596 C4.62002941,4.96524811 4.10763194,5.1332244 3.71124008,5.46642286 L3.58355339,5.58355339 L2.0896441,7.0773559 C1.33169968,7.86211367 1.34253936,9.10952545 2.11400695,9.88099305 C2.84487099,10.6118571 4.00289823,10.6600523 4.78475751,10.0238225 L4.91144661,9.91144661 L5.76644661,9.05644661 C5.96170876,8.86118446 6.27829124,8.86118446 6.47355339,9.05644661 C6.64711974,9.23001296 6.66640489,9.49943736 6.53140884,9.6943055 L6.47355339,9.76355339 L5.6123559,10.6246441 C4.43521924,11.7615607 2.56410157,11.7453012 1.40690017,10.5880998 C0.295986831,9.47718649 0.236565536,7.70832791 1.24484904,6.52165836 L1.37644661,6.37644661 L2.87638252,4.87651071 C3.44442608,4.30826116 4.21444071,3.99361146 5.01241676,3.99743718 Z M10.5930998,1.41190017 C11.7040132,2.52281351 11.7634345,4.29167209 10.755151,5.47834164 L10.6235534,5.62355339 L9.12361748,7.12348929 C8.50823696,7.73909297 7.65582094,8.05706905 6.7876074,7.9948893 C5.91939386,7.93270955 5.12099077,7.49650504 4.59961748,6.79948929 C4.43421411,6.57836413 4.47938555,6.26502085 4.70051071,6.09961748 C4.92163587,5.93421411 5.23497915,5.97938555 5.40038252,6.20051071 C5.74796472,6.66518788 6.28023344,6.95599088 6.85904247,6.99744404 C7.37997059,7.03475189 7.89236806,6.8667756 8.28875992,6.53357714 L8.41644661,6.41644661 L9.9103559,4.9226441 C10.6683003,4.13788633 10.6574606,2.89047455 9.88599305,2.11900695 C9.15512901,1.38814291 7.99710177,1.3399477 7.21436478,1.97705267 L7.08752112,2.08958265 L6.22752112,2.94458265 C6.03169053,3.13927469 5.71510939,3.13835172 5.52041735,2.94252112 C5.34735776,2.76844949 5.32885818,2.49897001 5.46442178,2.30449627 L5.52247888,2.23541735 L6.3876441,1.3753559 C7.56478076,0.238439269 9.43589843,0.254698775 10.5930998,1.41190017 Z" fill="currentColor" fill-rule="nonzero"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="10px" height="2px" viewBox="0 0 10 2" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<svg width="10px" height="14px" viewBox="0 0 10 2" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<path stroke="none" stroke-width="1" fill="none" d="M0.646446609,0.646446609 C1.13807119,0.5 1.26307119,0.555964406 1.35355339,0.646446609 C1.44403559,0.736928813 1.5,0.861928813 1.5,1 C1.5,1.13807119 1.44403559,1.26307119 1.35355339,1.35355339 C1.26307119,1.44403559 1.13807119,1.5 1,1.5 C0.861928813,1.5 0.736928813,1.44403559 0.646446609,1.35355339 C0.555964406,1.26307119 0.5,1.13807119 0.5,1 C0.5,0.861928813 0.555964406,0.736928813 0.646446609,0.646446609 Z M4.64644661,0.646446609 C5.13807119,0.5 5.26307119,0.555964406 5.35355339,0.646446609 C5.44403559,0.736928813 5.5,0.861928813 5.5,1 C5.5,1.13807119 5.44403559,1.26307119 5.35355339,1.35355339 C5.26307119,1.44403559 5.13807119,1.5 5,1.5 C4.86192881,1.5 4.73692881,1.44403559 4.64644661,1.35355339 C4.55596441,1.26307119 4.5,1.13807119 4.5,1 C4.5,0.861928813 4.55596441,0.736928813 4.64644661,0.646446609 Z M8.64644661,0.646446609 C9.13807119,0.5 9.26307119,0.555964406 9.35355339,0.646446609 C9.44403559,0.736928813 9.5,0.861928813 9.5,1 C9.5,1.13807119 9.44403559,1.26307119 9.35355339,1.35355339 C9.26307119,1.44403559 9.13807119,1.5 9,1.5 C8.86192881,1.5 8.73692881,1.44403559 8.64644661,1.35355339 C8.55596441,1.26307119 8.5,1.13807119 8.5,1 C8.5,0.861928813 8.55596441,0.736928813 8.64644661,0.646446609 Z" stroke="currentColor"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -1,15 +1,20 @@
|
||||
import React, { forwardRef } from 'react'
|
||||
import classNames, { Argument as ClassNamesArg } from 'classnames'
|
||||
|
||||
type TextFieldProps = {
|
||||
label?: string
|
||||
error?: string
|
||||
} & React.InputHTMLAttributes<HTMLInputElement>
|
||||
className?: ClassNamesArg
|
||||
sizeVariant?: 'default' | 'small'
|
||||
} & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'className'>
|
||||
|
||||
export const TextField = forwardRef(
|
||||
(
|
||||
{ label, error, className, ...inputProps }: TextFieldProps,
|
||||
{ label, error, className, sizeVariant, ...inputProps }: TextFieldProps,
|
||||
ref: React.ForwardedRef<HTMLInputElement>
|
||||
) => {
|
||||
sizeVariant = sizeVariant ?? 'default'
|
||||
|
||||
let variant = 'bg-white border-gray-200 focus:border-blue-400'
|
||||
if (error)
|
||||
variant =
|
||||
@@ -17,7 +22,11 @@ export const TextField = forwardRef(
|
||||
|
||||
const input = (
|
||||
<input
|
||||
className={`block border rounded-md h-10 w-full focus:ring-2 focus:outline-none px-2 ${variant}`}
|
||||
className={classNames(
|
||||
'block border rounded-md w-full focus:ring-2 focus:outline-none px-2',
|
||||
variant,
|
||||
sizeVariant == 'default' ? 'py-2' : 'py-1'
|
||||
)}
|
||||
{...inputProps}
|
||||
ref={ref}
|
||||
/>
|
||||
@@ -28,7 +37,7 @@ export const TextField = forwardRef(
|
||||
|
||||
if (label) {
|
||||
return (
|
||||
<label className={`block my-4 ${className}`}>
|
||||
<label className={classNames('block', className)}>
|
||||
<span className="block text-xs uppercase font-semibold mb-1">
|
||||
{label}
|
||||
</span>
|
||||
@@ -39,7 +48,9 @@ export const TextField = forwardRef(
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<div
|
||||
className={classNames(className, sizeVariant == 'small' && 'text-sm')}
|
||||
>
|
||||
{input}
|
||||
{errorElm}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user