mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-07-29 00:38:44 +00:00
Work on settings user table
This commit is contained in:
@@ -32,7 +32,6 @@ const ScannerSection = () => {
|
||||
)}
|
||||
</InputLabelDescription>
|
||||
<Button
|
||||
className="my-2"
|
||||
onClick={() => {
|
||||
startScanner()
|
||||
}}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Checkbox, Input, Table } from 'semantic-ui-react'
|
||||
import Checkbox from '../../../primitives/form/Checkbox'
|
||||
import { TextField, Button, ButtonGroup } from '../../../primitives/form/Input'
|
||||
import { TableRow, TableCell } from '../../../primitives/Table'
|
||||
|
||||
const CREATE_USER_MUTATION = gql`
|
||||
mutation createUser($username: String!, $admin: Boolean!) {
|
||||
@@ -88,16 +90,16 @@ const AddUserRow = ({ setShow, show, onUserAdded }: AddUserRowProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<TextField
|
||||
placeholder={t('login_page.field.username', 'Username')}
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<TextField
|
||||
placeholder={t(
|
||||
'login_page.initial_setup.field.photo_path.placeholder',
|
||||
'/path/to/photos'
|
||||
@@ -105,29 +107,28 @@ const AddUserRow = ({ setShow, show, onUserAdded }: AddUserRowProps) => {
|
||||
value={state.rootPath}
|
||||
onChange={e => updateInput(e, 'rootPath')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
toggle
|
||||
label="Admin"
|
||||
checked={state.admin}
|
||||
onChange={(e, data) => {
|
||||
onChange={e => {
|
||||
setState({
|
||||
...state,
|
||||
admin: data.checked || false,
|
||||
admin: e.target.checked || false,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<Button negative onClick={() => setShow(false)}>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<ButtonGroup>
|
||||
<Button variant="negative" onClick={() => setShow(false)}>
|
||||
{t('general.action.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
positive
|
||||
variant="positive"
|
||||
onClick={() => {
|
||||
createUser({
|
||||
variables: {
|
||||
@@ -139,9 +140,9 @@ const AddUserRow = ({ setShow, show, onUserAdded }: AddUserRowProps) => {
|
||||
>
|
||||
{t('settings.users.add_user.submit', 'Add user')}
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</ButtonGroup>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Button, Checkbox, Input, Table } from 'semantic-ui-react'
|
||||
import { EditRootPaths } from './EditUserRowRootPaths'
|
||||
import { UserRowChildProps } from './UserRow'
|
||||
import { TableRow, TableCell } from '../../../primitives/Table'
|
||||
import { TextField } from '../../../primitives/form/Input'
|
||||
import { Button, ButtonGroup } from '../../../primitives/form/Input'
|
||||
import Checkbox from '../../../primitives/form/Checkbox'
|
||||
|
||||
const EditUserRow = ({
|
||||
user,
|
||||
@@ -24,34 +27,34 @@ const EditUserRow = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Input
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<TextField
|
||||
style={{ width: '100%' }}
|
||||
placeholder={user.username}
|
||||
value={state.username}
|
||||
onChange={e => updateInput(e, 'username')}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<EditRootPaths user={user} />
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Checkbox
|
||||
toggle
|
||||
label="Admin"
|
||||
checked={state.admin}
|
||||
onChange={(_, data) => {
|
||||
onChange={e => {
|
||||
setState(state => ({
|
||||
...state,
|
||||
admin: data.checked || false,
|
||||
admin: e.target.checked || false,
|
||||
}))
|
||||
}}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
negative
|
||||
variant="negative"
|
||||
onClick={() =>
|
||||
setState(state => ({
|
||||
...state,
|
||||
@@ -62,9 +65,8 @@ const EditUserRow = ({
|
||||
{t('general.action.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
loading={updateUserLoading}
|
||||
disabled={updateUserLoading}
|
||||
positive
|
||||
variant="positive"
|
||||
onClick={() =>
|
||||
updateUser({
|
||||
variables: {
|
||||
@@ -77,9 +79,9 @@ const EditUserRow = ({
|
||||
>
|
||||
{t('general.action.save', 'Save')}
|
||||
</Button>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</ButtonGroup>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import React, { useState } from 'react'
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import { Button, Icon, Input } from 'semantic-ui-react'
|
||||
import styled from 'styled-components'
|
||||
import { USERS_QUERY } from './UsersTable'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { USER_ADD_ROOT_PATH_MUTATION } from './AddUserRow'
|
||||
@@ -14,6 +12,7 @@ import {
|
||||
settingsUsersQuery_user_rootAlbums,
|
||||
} from './__generated__/settingsUsersQuery'
|
||||
import { userAddRootPath } from './__generated__/userAddRootPath'
|
||||
import { Button, TextField } from '../../../primitives/form/Input'
|
||||
|
||||
const USER_REMOVE_ALBUM_PATH_MUTATION = gql`
|
||||
mutation userRemoveAlbumPathMutation($userId: ID!, $albumId: ID!) {
|
||||
@@ -23,12 +22,6 @@ const USER_REMOVE_ALBUM_PATH_MUTATION = gql`
|
||||
}
|
||||
`
|
||||
|
||||
const RootPathListItem = styled.li`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
type EditRootPathProps = {
|
||||
album: settingsUsersQuery_user_rootAlbums
|
||||
user: settingsUsersQuery_user
|
||||
@@ -48,10 +41,10 @@ const EditRootPath = ({ album, user }: EditRootPathProps) => {
|
||||
})
|
||||
|
||||
return (
|
||||
<RootPathListItem>
|
||||
<li className="flex justify-between">
|
||||
<span>{album.filePath}</span>
|
||||
<Button
|
||||
negative
|
||||
variant="negative"
|
||||
disabled={loading}
|
||||
onClick={() =>
|
||||
removeAlbumPath({
|
||||
@@ -62,18 +55,12 @@ const EditRootPath = ({ album, user }: EditRootPathProps) => {
|
||||
})
|
||||
}
|
||||
>
|
||||
<Icon name="remove" />
|
||||
{t('general.action.remove', 'Remove')}
|
||||
</Button>
|
||||
</RootPathListItem>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
const NewRootPathInput = styled(Input)`
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
`
|
||||
|
||||
type EditNewRootPathProps = {
|
||||
userID: string
|
||||
}
|
||||
@@ -93,39 +80,33 @@ const EditNewRootPath = ({ userID }: EditNewRootPathProps) => {
|
||||
)
|
||||
|
||||
return (
|
||||
<li>
|
||||
<NewRootPathInput
|
||||
style={{ width: '100%' }}
|
||||
<li className="flex gap-1 mt-2">
|
||||
<TextField
|
||||
value={value}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setValue(e.target.value)
|
||||
}
|
||||
disabled={loading}
|
||||
action={{
|
||||
positive: true,
|
||||
icon: 'add',
|
||||
content: t('general.action.add', 'Add'),
|
||||
onClick: () => {
|
||||
setValue('')
|
||||
addRootPath({
|
||||
variables: {
|
||||
id: userID,
|
||||
rootPath: value,
|
||||
},
|
||||
})
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="positive"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
setValue('')
|
||||
addRootPath({
|
||||
variables: {
|
||||
id: userID,
|
||||
rootPath: value,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
{t('general.action.add', 'Add')}
|
||||
</Button>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
const RootPathList = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
`
|
||||
|
||||
type EditRootPathsProps = {
|
||||
user: settingsUsersQuery_user
|
||||
}
|
||||
@@ -136,9 +117,9 @@ export const EditRootPaths = ({ user }: EditRootPathsProps) => {
|
||||
))
|
||||
|
||||
return (
|
||||
<RootPathList>
|
||||
<ul>
|
||||
{editRows}
|
||||
<EditNewRootPath userID={user.id} />
|
||||
</RootPathList>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import { Table, Loader, Button, Icon } from 'semantic-ui-react'
|
||||
import { Loader, Icon } from 'semantic-ui-react'
|
||||
import {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
} from '../../../primitives/Table'
|
||||
import { useQuery, gql } from '@apollo/client'
|
||||
import UserRow from './UserRow'
|
||||
import AddUserRow from './AddUserRow'
|
||||
import { SectionTitle } from '../SettingsPage'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { settingsUsersQuery } from './__generated__/settingsUsersQuery'
|
||||
import { Button } from '../../../primitives/form/Input'
|
||||
|
||||
export const USERS_QUERY = gql`
|
||||
query settingsUsersQuery {
|
||||
@@ -26,9 +35,8 @@ const UsersTable = () => {
|
||||
const { t } = useTranslation()
|
||||
const [showAddUser, setShowAddUser] = useState(false)
|
||||
|
||||
const { loading, error, data, refetch } = useQuery<settingsUsersQuery>(
|
||||
USERS_QUERY
|
||||
)
|
||||
const { loading, error, data, refetch } =
|
||||
useQuery<settingsUsersQuery>(USERS_QUERY)
|
||||
|
||||
if (error) {
|
||||
return <div>{`Users table error: ${error.message}`}</div>
|
||||
@@ -45,25 +53,28 @@ const UsersTable = () => {
|
||||
<div>
|
||||
<SectionTitle>{t('settings.users.title', 'Users')}</SectionTitle>
|
||||
<Loader active={loading} />
|
||||
<Table celled>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>
|
||||
<Table className="w-full max-w-6xl">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell>
|
||||
{t('settings.users.table.column_names.username', 'Username')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
{t('settings.users.table.column_names.photo_path', 'Photo path')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
{t('settings.users.table.column_names.admin', 'Admin')}
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell>
|
||||
{t(
|
||||
'settings.users.table.column_names.capabilities',
|
||||
'Capabilities'
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
<TableHeaderCell className="w-0 whitespace-nowrap">
|
||||
{t('settings.users.table.column_names.action', 'Action')}
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
|
||||
<Table.Body>
|
||||
<TableBody>
|
||||
{userRows}
|
||||
<AddUserRow
|
||||
show={showAddUser}
|
||||
@@ -73,23 +84,23 @@ const UsersTable = () => {
|
||||
refetch()
|
||||
}}
|
||||
/>
|
||||
</Table.Body>
|
||||
</TableBody>
|
||||
|
||||
<Table.Footer>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell colSpan="4">
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableHeaderCell colSpan={4} className="text-right">
|
||||
<Button
|
||||
positive
|
||||
className="bg-white"
|
||||
variant="positive"
|
||||
disabled={showAddUser}
|
||||
floated="right"
|
||||
onClick={() => setShowAddUser(true)}
|
||||
>
|
||||
<Icon name="add" />
|
||||
{t('settings.users.table.new_user', 'New user')}
|
||||
</Button>
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Footer>
|
||||
</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import React from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Button, Icon, Table, Modal } from 'semantic-ui-react'
|
||||
import styled from 'styled-components'
|
||||
import { Icon, Modal } from 'semantic-ui-react'
|
||||
import Checkbox from '../../../primitives/form/Checkbox'
|
||||
import { Button } from '../../../primitives/form/Input'
|
||||
import { TableCell, TableRow } from '../../../primitives/Table'
|
||||
import ChangePasswordModal from './UserChangePassword'
|
||||
import { UserRowChildProps } from './UserRow'
|
||||
|
||||
const PathList = styled.ul`
|
||||
margin: 0;
|
||||
padding: 0 0 0 12px;
|
||||
list-style: none;
|
||||
`
|
||||
|
||||
const ViewUserRow = ({
|
||||
user,
|
||||
// state,
|
||||
@@ -25,22 +21,22 @@ const ViewUserRow = ({
|
||||
}: UserRowChildProps) => {
|
||||
const { t } = useTranslation()
|
||||
const paths = (
|
||||
<PathList>
|
||||
<ul>
|
||||
{user.rootAlbums.map(album => (
|
||||
<li key={album.id}>{album.filePath}</li>
|
||||
))}
|
||||
</PathList>
|
||||
</ul>
|
||||
)
|
||||
|
||||
return (
|
||||
<Table.Row>
|
||||
<Table.Cell>{user.username}</Table.Cell>
|
||||
<Table.Cell>{paths}</Table.Cell>
|
||||
<Table.Cell>
|
||||
{user.admin ? <Icon name="checkmark" size="large" /> : null}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button.Group>
|
||||
<TableRow>
|
||||
<TableCell>{user.username}</TableCell>
|
||||
<TableCell>{paths}</TableCell>
|
||||
<TableCell>
|
||||
<Checkbox label="Admin" disabled checked={user.admin} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex gap-1">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setState(state => {
|
||||
@@ -50,7 +46,6 @@ const ViewUserRow = ({
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Icon name="edit" />
|
||||
{t('settings.users.table.row.action.edit', 'Edit')}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -73,7 +68,7 @@ const ViewUserRow = ({
|
||||
onClose={() => setChangePassword(false)}
|
||||
/>
|
||||
<Button
|
||||
negative
|
||||
variant="negative"
|
||||
onClick={() => {
|
||||
setConfirmDelete(true)
|
||||
}}
|
||||
@@ -102,7 +97,7 @@ const ViewUserRow = ({
|
||||
{t('general.action.cancel', 'Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
negative
|
||||
variant="negative"
|
||||
onClick={() => {
|
||||
setConfirmDelete(false)
|
||||
deleteUser({
|
||||
@@ -120,9 +115,9 @@ const ViewUserRow = ({
|
||||
</Button>
|
||||
</Modal.Actions>
|
||||
</Modal>
|
||||
</Button.Group>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@ const SidebarShare = ({
|
||||
<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"
|
||||
className="text-green-500 font-bold uppercase text-xs"
|
||||
disabled={loading}
|
||||
onClick={() => {
|
||||
shareItem({
|
||||
|
||||
@@ -39,7 +39,7 @@ export const SidebarProvider = ({ children }: SidebarProviderProps) => {
|
||||
pinned: boolean
|
||||
}>({
|
||||
content: null,
|
||||
pinned: true,
|
||||
pinned: false,
|
||||
})
|
||||
|
||||
const updateSidebar = (content: React.ReactNode | null) => {
|
||||
|
||||
41
ui/src/primitives/Table.tsx
Normal file
41
ui/src/primitives/Table.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const Table = styled.table.attrs({
|
||||
className: 'border border-separate rounded' as string,
|
||||
})`
|
||||
border-spacing: 0;
|
||||
|
||||
& td:not(:last-child),
|
||||
& th:not(:last-child) {
|
||||
border-right: 1px solid;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
& tr:first-child td {
|
||||
border-top: 1px solid;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
& td {
|
||||
border-bottom: 1px solid;
|
||||
border-color: inherit;
|
||||
}
|
||||
`
|
||||
|
||||
export const TableHeader = styled.thead.attrs({
|
||||
className: 'text-left',
|
||||
})``
|
||||
|
||||
export const TableBody = styled.tbody.attrs({ className: '' })``
|
||||
|
||||
export const TableFooter = styled.tfoot.attrs({ className: '' })``
|
||||
|
||||
export const TableRow = styled.tr.attrs({ className: '' })``
|
||||
|
||||
export const TableCell = styled.td.attrs({
|
||||
className: 'py-2 px-2 align-top',
|
||||
})``
|
||||
|
||||
export const TableHeaderCell = styled.th.attrs({
|
||||
className: 'bg-gray-50 py-2 px-2 align-top font-semibold' as string,
|
||||
})``
|
||||
@@ -29,13 +29,29 @@ const CheckboxLabelWrapper = styled.label`
|
||||
& input[type='checkbox']:checked + span::before {
|
||||
background-color: #0072ff;
|
||||
border-color: #109dff;
|
||||
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='7px' height='4px' viewBox='0 0 7 4' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EPath 5%3C/title%3E%3Cg id='Page-1' stroke='none' stroke-width='1.5' fill='none' fill-rule='evenodd'%3E%3Cpolyline id='Path-5' stroke='%23FFFFFF' points='1 1.93487039 2.77174339 3.70661378 6.47835718 2.27373675e-13'%3E%3C/polyline%3E%3C/g%3E%3C/svg%3E");
|
||||
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='7px' height='4px' viewBox='0 0 7 4' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EPath 5%3C/title%3E%3Cg stroke='none' stroke-width='1.5' fill='none' fill-rule='evenodd'%3E%3Cpolyline stroke='white' points='1 1.93487039 2.77174339 3.70661378 6.47835718 2.27373675e-13'%3E%3C/polyline%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
& input[type='checkbox']:focus + span::before {
|
||||
outline: 2px solid #86c6ff;
|
||||
border-color: #109dff;
|
||||
}
|
||||
|
||||
& input[type='checkbox']:disabled + span::before {
|
||||
border-color: #ccc;
|
||||
background-color: #fefefe;
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
& input[type='checkbox']:disabled:checked + span::before {
|
||||
background-color: #eee;
|
||||
background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='7px' height='4px' viewBox='0 0 7 4' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3EPath 5%3C/title%3E%3Cg stroke='none' stroke-width='1.5' fill='none' fill-rule='evenodd'%3E%3Cpolyline stroke='%23444444' points='1 1.93487039 2.77174339 3.70661378 6.47835718 2.27373675e-13'%3E%3C/polyline%3E%3C/g%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
& input[type='checkbox']:disabled + span {
|
||||
color: #555;
|
||||
cursor: initial;
|
||||
}
|
||||
`
|
||||
|
||||
type CheckboxProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
||||
|
||||
@@ -3,6 +3,7 @@ import classNames, { Argument as ClassNamesArg } from 'classnames'
|
||||
|
||||
import { ReactComponent as ActionArrowIcon } from './icons/textboxActionArrow.svg'
|
||||
import { ReactComponent as LoadingSpinnerIcon } from './icons/textboxLoadingSpinner.svg'
|
||||
import styled from 'styled-components'
|
||||
|
||||
type TextFieldProps = {
|
||||
label?: string
|
||||
@@ -121,15 +122,26 @@ export const TextField = forwardRef(
|
||||
}
|
||||
)
|
||||
|
||||
const buttonStyles =
|
||||
'bg-gray-50 px-6 py-0.5 rounded border border-gray-200 focus:outline-none focus:border-blue-300 text-[#222] hover:bg-gray-100'
|
||||
type ButtonProps = {
|
||||
variant?: 'negative' | 'positive' | 'default'
|
||||
}
|
||||
|
||||
const buttonStyles = ({ variant }: ButtonProps) =>
|
||||
classNames(
|
||||
'bg-gray-50 px-6 py-0.5 rounded border border-gray-200 focus:outline-none focus:border-blue-300 text-[#222] hover:bg-gray-100 whitespace-nowrap',
|
||||
variant == 'negative' &&
|
||||
'text-red-600 hover:bg-red-600 hover:border-red-700 hover:text-white transition-colors focus:border-red-600 focus:hover:border-red-700',
|
||||
variant == 'positive' &&
|
||||
'text-green-600 hover:bg-green-600 hover:border-green-700 hover:text-white transition-colors focus:border-green-600 focus:hover:border-green-700'
|
||||
)
|
||||
|
||||
export const Submit = ({
|
||||
className,
|
||||
variant,
|
||||
...props
|
||||
}: React.InputHTMLAttributes<HTMLInputElement>) => (
|
||||
}: ButtonProps & React.ButtonHTMLAttributes<HTMLInputElement>) => (
|
||||
<input
|
||||
className={classNames(buttonStyles, className)}
|
||||
className={classNames(buttonStyles({ variant }), className)}
|
||||
type="submit"
|
||||
{...props}
|
||||
/>
|
||||
@@ -137,10 +149,16 @@ export const Submit = ({
|
||||
|
||||
export const Button = ({
|
||||
children,
|
||||
variant,
|
||||
className,
|
||||
...props
|
||||
}: React.ButtonHTMLAttributes<HTMLButtonElement>) => (
|
||||
<button className={classNames(buttonStyles, className)} {...props}>
|
||||
}: ButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>) => (
|
||||
<button
|
||||
className={classNames(buttonStyles({ variant }), className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
|
||||
export const ButtonGroup = styled.div.attrs({ className: 'flex gap-1' })``
|
||||
|
||||
@@ -10,6 +10,20 @@ module.exports = {
|
||||
boxShadow: {
|
||||
separator: '0 0 4px 0 rgba(0, 0, 0, 0.1)',
|
||||
},
|
||||
colors: {
|
||||
green: {
|
||||
50: '#f0ffee',
|
||||
100: '#dcffd8',
|
||||
200: '#b6fdb3',
|
||||
300: '#7cf587',
|
||||
400: '#56e263',
|
||||
500: '#4abf3c',
|
||||
600: '#30a23e',
|
||||
700: '#168332',
|
||||
800: '#006624',
|
||||
900: '#00541d',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
|
||||
Reference in New Issue
Block a user