Skip to content

Commit

Permalink
Merge pull request #658 from commercelayer/add-useInputResourceGroupO…
Browse files Browse the repository at this point in the history
…verlay

Add the `useInputResourceGroupOverlay` hook
  • Loading branch information
marcomontalbano committed May 24, 2024
2 parents e293eb7 + f256aea commit ee382ce
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 130 deletions.
3 changes: 2 additions & 1 deletion packages/app-elements/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export {
useCoreApi,
useCoreSdkProvider
} from '#providers/CoreSdkProvider'
export { createApp, type ClAppKey, type ClAppProps } from '#providers/createApp'
export { ErrorBoundary } from '#providers/ErrorBoundary'
export { GTMProvider, useTagManager } from '#providers/GTMProvider'
export {
Expand All @@ -71,6 +70,7 @@ export {
type TokenProviderRolePermissions,
type TokenProviderTokenApplicationKind
} from '#providers/TokenProvider'
export { createApp, type ClAppKey, type ClAppProps } from '#providers/createApp'

// Atoms
export { A, type AProps } from '#ui/atoms/A'
Expand Down Expand Up @@ -233,6 +233,7 @@ export { InputReadonly, type InputReadonlyProps } from '#ui/forms/InputReadonly'
export {
HookedInputResourceGroup,
InputResourceGroup,
useInputResourceGroupOverlay,
type HookedInputResourceGroupProps,
type InputResourceGroupProps
} from '#ui/forms/InputResourceGroup'
Expand Down
1 change: 1 addition & 0 deletions packages/app-elements/src/ui/atoms/Icon/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const iconMapping = {
cloudArrowUp: phosphor.CloudArrowUp,
creditCard: phosphor.CreditCard,
currencyEur: phosphor.CurrencyEur,
dotsSixVertical: phosphor.DotsSixVertical,
dotsThree: phosphor.DotsThree,
download: phosphor.Download,
envelopeSimple: phosphor.EnvelopeSimple,
Expand Down
150 changes: 94 additions & 56 deletions packages/app-elements/src/ui/forms/InputResourceGroup/FullList.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useOverlay } from '#hooks/useOverlay'
import { AvatarLetter } from '#ui/atoms/AvatarLetter'
import { Card } from '#ui/atoms/Card'
import { SkeletonTemplate } from '#ui/atoms/SkeletonTemplate'
import { Spacer } from '#ui/atoms/Spacer'
import { Text } from '#ui/atoms/Text'
import { SearchBar } from '#ui/composite/SearchBar'
import { InputCheckboxGroupItem } from '#ui/forms/InputCheckboxGroup/InputCheckboxGroupItem'
import { InputWrapper } from '#ui/internals/InputWrapper'
import { type OverlayProps } from '#ui/internals/Overlay'
import { ResourceList } from '#ui/resources/ResourceList'
import { type ListableResourceType, type QueryFilter } from '@commercelayer/sdk'
import isEmpty from 'lodash/isEmpty'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { InputCheckboxGroupItem } from '../InputCheckboxGroup/InputCheckboxGroupItem'
import {
computeLabelWithSelected,
prepareCheckboxItemOrMock,
Expand Down Expand Up @@ -67,14 +67,14 @@ export interface FullListProps {
* It will be automatically computed with the number of selected items.
*/
title: string
/**
* Total count of pre-fetched items.
*/
totalCount?: number
/**
* Show icon in checkbox selectors
*/
showCheckboxIcon?: boolean
/**
* Hide selected items
*/
hideSelected?: boolean
}

export function FullList({
Expand All @@ -86,12 +86,15 @@ export function FullList({
searchBy,
sortBy,
title,
totalCount,
showCheckboxIcon = true
showCheckboxIcon = true,
hideSelected = false
}: FullListProps): JSX.Element {
const { values, toggleValue } = useToggleCheckboxValues(defaultValues)
const [filters, setFilters] = useState<QueryFilter>({})
const [search, setSearch] = useState<string>('')

const initialValues = useMemo(() => defaultValues, [])

const selectedCount = values.length

useEffect(() => {
Expand Down Expand Up @@ -127,54 +130,89 @@ export function FullList({
</Spacer>
)}

<SkeletonTemplate isLoading={totalCount == null || totalCount === 0}>
<InputWrapper
fieldset
label={computeLabelWithSelected({
label: title,
selectedCount,
totalCount
})}
>
<Card gap='1' overflow='hidden'>
<ResourceList
type={resource}
emptyState={<div>No items found</div>}
query={{
pageSize: 25,
filters,
sort: {
[sortBy.attribute]: sortBy.direction
<SkeletonTemplate>
<ResourceList
type={resource}
title={(totalCount) => (
<Text weight='semibold'>
{computeLabelWithSelected({
label: title,
selectedCount: hideSelected
? selectedCount - initialValues.length
: selectedCount,
totalCount:
hideSelected && totalCount != null
? totalCount - initialValues.length
: totalCount
})}
</Text>
)}
variant='boxed'
emptyState={<div>No items found</div>}
query={{
pageSize: 25,
filters,
sort: {
[sortBy.attribute]: sortBy.direction
}
}}
ItemTemplate={({ isLoading, resource }) => {
const item = prepareCheckboxItemOrMock({
resource,
isLoading,
fieldForLabel,
fieldForValue
})

if (hideSelected && initialValues.includes(item.value)) {
return null
}

return (
<InputCheckboxGroupItem
isLoading={isLoading}
checked={values.includes(item.value)}
onChange={() => {
toggleValue(item.value)
}}
icon={
showCheckboxIcon ? (
<AvatarLetter text={item.label} />
) : undefined
}
}}
ItemTemplate={({ isLoading, resource }) => {
const item = prepareCheckboxItemOrMock({
resource,
isLoading,
fieldForLabel,
fieldForValue
})
return (
<InputCheckboxGroupItem
isLoading={isLoading}
checked={values.includes(item.value)}
onChange={() => {
toggleValue(item.value)
}}
icon={
showCheckboxIcon ? (
<AvatarLetter text={item.label} />
) : undefined
}
content={<Text weight='semibold'>{item.label}</Text>}
value={item.value}
/>
)
}}
/>
</Card>
</InputWrapper>
content={<Text weight='semibold'>{item.label}</Text>}
value={item.value}
/>
)
}}
/>
</SkeletonTemplate>
</div>
)
}

// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export const useInputResourceGroupOverlay = () => {
const { Overlay, close, open } = useOverlay()

const InputResourceGroupOverlay = useCallback(
({
footer,
backgroundColor,
...props
}: FullListProps & Omit<OverlayProps, 'children'>) => (
<Overlay backgroundColor={backgroundColor} footer={footer}>
<div className='pt-5'>
<FullList {...props} />
</div>
</Overlay>
),
[Overlay]
)

return {
InputResourceGroupOverlay,
openInputResourceGroupOverlay: open,
closeInputResourceGroupOverlay: close
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { formatResourceName } from '#helpers/resources'
import { useOverlay } from '#hooks/useOverlay'
import { useCoreApi } from '#providers/CoreSdkProvider'
import { AvatarLetter } from '#ui/atoms/AvatarLetter'
import { Button } from '#ui/atoms/Button'
Expand All @@ -16,7 +15,11 @@ import {
} from '@commercelayer/sdk'
import uniqBy from 'lodash/uniqBy'
import { useEffect, useState } from 'react'
import { FullList, type FullListProps, type SortBy } from './FullList'
import {
useInputResourceGroupOverlay,
type FullListProps,
type SortBy
} from './FullList'
import {
computeLabelWithSelected,
prepareCheckboxItemOrMock,
Expand Down Expand Up @@ -61,10 +64,9 @@ export const InputResourceGroup: React.FC<InputResourceGroupProps> = ({
filters = {},
hideWhenSingleItem,
showCheckboxIcon = true,
hideSelected = false,
title
}) => {
const { Overlay, close, open } = useOverlay()

const { values, toggleValue, setValues } =
useToggleCheckboxValues(defaultValues)
const selectedCount = values.length
Expand All @@ -90,6 +92,12 @@ export const InputResourceGroup: React.FC<InputResourceGroupProps> = ({
[values]
)

const {
InputResourceGroupOverlay,
closeInputResourceGroupOverlay,
openInputResourceGroupOverlay
} = useInputResourceGroupOverlay()

const isEmptyList = !isLoading && list.length === 0
const isHidden =
hideWhenSingleItem === true &&
Expand Down Expand Up @@ -136,7 +144,7 @@ export const InputResourceGroup: React.FC<InputResourceGroupProps> = ({
<button
type='button'
onClick={() => {
open()
openInputResourceGroupOverlay()
}}
>
<Text variant='primary' weight='bold'>
Expand All @@ -149,35 +157,30 @@ export const InputResourceGroup: React.FC<InputResourceGroupProps> = ({
</button>
</Spacer>
) : null}
<Overlay
<InputResourceGroupOverlay
footer={
<Button
fullWidth
type='button'
onClick={() => {
close()
closeInputResourceGroupOverlay()
setSelectedValuesForPreview(values)
}}
>
Apply
</Button>
}
>
<div className='pt-5'>
<FullList
defaultValues={values}
fieldForLabel={fieldForLabel}
fieldForValue={fieldForValue}
onChange={setValues}
resource={resource}
searchBy={searchBy}
sortBy={sortBy}
title={title}
totalCount={totalCount}
showCheckboxIcon={showCheckboxIcon}
/>
</div>
</Overlay>
defaultValues={values}
fieldForLabel={fieldForLabel}
fieldForValue={fieldForValue}
onChange={setValues}
resource={resource}
searchBy={searchBy}
sortBy={sortBy}
title={title}
hideSelected={hideSelected}
showCheckboxIcon={showCheckboxIcon}
/>
</InputWrapper>
</SkeletonTemplate>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { useInputResourceGroupOverlay } from './FullList'
export {
HookedInputResourceGroup,
type HookedInputResourceGroupProps
Expand Down
Loading

0 comments on commit ee382ce

Please sign in to comment.