Skip to content

Commit

Permalink
dnd
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperkristensen committed Nov 18, 2024
1 parent 0bd2aac commit c714d53
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 183 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import {
closestCenter,
defaultDropAnimationSideEffects,
DndContext,
DragEndEvent,
DragOverlay,
DragStartEvent,
DropAnimation,
KeyboardSensor,
PointerSensor,
UniqueIdentifier,
Expand All @@ -17,9 +20,8 @@ import {
useSortable,
} from "@dnd-kit/sortable"
import { CSS } from "@dnd-kit/utilities"
import { CheckMini, Spinner, ThumbnailBadge } from "@medusajs/icons"
import { clx, Tooltip } from "@medusajs/ui"
import { AnimatePresence, motion } from "framer-motion"
import { ThumbnailBadge } from "@medusajs/icons"
import { Checkbox, clx, Tooltip } from "@medusajs/ui"
import { useCallback, useState } from "react"
import { useTranslation } from "react-i18next"

Expand All @@ -32,14 +34,24 @@ interface MediaView {

interface MediaGridProps {
media: MediaView[]
setMedia: (media: MediaView[]) => void
onSwapPositions: (callback: (items: MediaView[]) => MediaView[]) => void
selection: Record<string, boolean>
onCheckedChange: (id: string) => (value: boolean) => void
}

const dropAnimationConfig: DropAnimation = {
sideEffects: defaultDropAnimationSideEffects({
styles: {
active: {
opacity: "0.4",
},
},
}),
}

export const MediaGrid = ({
media,
setMedia,
onSwapPositions,
selection,
onCheckedChange,
}: MediaGridProps) => {
Expand All @@ -60,10 +72,10 @@ export const MediaGrid = ({
setActiveId(null)
const { active, over } = event

if (active.id !== over.id) {
setItems((items) => {
const oldIndex = items.indexOf(active.id)
const newIndex = items.indexOf(over.id)
if (active.id !== over?.id) {
onSwapPositions((items) => {
const oldIndex = items.findIndex((item) => item.field_id === active.id)
const newIndex = items.findIndex((item) => item.field_id === over?.id)

return arrayMove(items, oldIndex, newIndex)
})
Expand All @@ -79,7 +91,10 @@ export const MediaGrid = ({
>
<div className="bg-ui-bg-subtle size-full overflow-auto">
<div className="grid h-fit auto-rows-auto grid-cols-4 gap-6 p-6">
<SortableContext items={media} strategy={rectSortingStrategy}>
<SortableContext
items={media.map((m) => m.field_id)}
strategy={rectSortingStrategy}
>
{media.map((m) => {
return (
<MediaGridItem
Expand All @@ -91,6 +106,16 @@ export const MediaGrid = ({
)
})}
</SortableContext>
<DragOverlay dropAnimation={dropAnimationConfig}>
{activeId ? (
<MediaGridItemOverlay
media={media.find((m) => m.field_id === activeId)!}
checked={
!!selection[media.find((m) => m.field_id === activeId)!.id!]
}
/>
) : null}
</DragOverlay>
</div>
</div>
</DndContext>
Expand All @@ -108,13 +133,15 @@ const MediaGridItem = ({
checked,
onCheckedChange,
}: MediaGridItemProps) => {
const [isLoading, setIsLoading] = useState(true)

const { t } = useTranslation()

const handleToggle = useCallback(() => {
onCheckedChange(!checked)
}, [checked, onCheckedChange])
const handleToggle = useCallback(
(value: boolean) => {
console.log("value", value)
onCheckedChange(value)
},
[onCheckedChange]
)

const {
attributes,
Expand All @@ -135,8 +162,6 @@ const MediaGridItem = ({
className="shadow-elevation-card-rest hover:shadow-elevation-card-hover focus-visible:shadow-borders-focus bg-ui-bg-subtle-hover group relative aspect-square h-auto max-w-full overflow-hidden rounded-lg outline-none"
style={style}
ref={setNodeRef}
{...attributes}
{...listeners}
>
{media.isThumbnail && (
<div className="absolute left-2 top-2">
Expand All @@ -146,50 +171,57 @@ const MediaGridItem = ({
</div>
)}
<div
className={clx(
"transition-fg absolute right-2 top-2 opacity-0 group-focus-within:opacity-100 group-hover:opacity-100 group-focus:opacity-100",
{
"opacity-100": checked,
}
)}
className="absolute inset-0 outline-none"
{...attributes}
{...listeners}
/>
<div
className={clx("transition-fg absolute right-2 top-2 opacity-0", {
"group-focus-within:opacity-100 group-hover:opacity-100 group-focus:opacity-100":
!isDragging && !checked,
"opacity-100": checked,
})}
>
<div
className={clx(
"group relative inline-flex h-4 w-4 items-center justify-center outline-none "
)}
>
<div
className={clx(
"text-ui-fg-on-inverted bg-ui-bg-component shadow-borders-base [&_path]:shadow-details-contrast-on-bg-interactive group-disabled:text-ui-fg-disabled group-disabled:!bg-ui-bg-disabled group-disabled:!shadow-borders-base transition-fg h-[14px] w-[14px] rounded-[3px]",
{
"bg-ui-bg-interactive group-hover:bg-ui-bg-interactive shadow-borders-interactive-with-shadow":
checked,
}
)}
>
{checked && (
<div className="absolute inset-0">
<CheckMini />
</div>
)}
</div>
<Checkbox
onClick={(e) => {
e.stopPropagation()
}}
checked={checked}
onCheckedChange={handleToggle}
/>
</div>
<img
src={media.url}
alt=""
className="size-full object-cover object-center"
/>
</div>
)
}

export const MediaGridItemOverlay = ({
media,
checked,
}: {
media: MediaView
checked: boolean
}) => {
return (
<div className="shadow-elevation-card-rest hover:shadow-elevation-card-hover focus-visible:shadow-borders-focus bg-ui-bg-subtle-hover group relative aspect-square h-auto max-w-full overflow-hidden rounded-lg outline-none">
{media.isThumbnail && (
<div className="absolute left-2 top-2">
<ThumbnailBadge />
</div>
)}
<div
className={clx("transition-fg absolute right-2 top-2 opacity-0", {
"opacity-100": checked,
})}
>
<Checkbox checked={checked} />
</div>
<AnimatePresence>
{isLoading && (
<motion.div
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0, transition: { duration: 0.5 } }}
className="bg-ui-bg-subtle-hover absolute inset-0 flex items-center justify-center"
>
<Spinner className="text-ui-fg-subtle animate-spin" />
</motion.div>
)}
</AnimatePresence>
<img
src={media.url}
onLoad={() => setIsLoading(false)}
alt=""
className="size-full object-cover object-center"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => {
setSelection({})
}

const onSwapPositions = (
callback: (items: typeof fields) => typeof fields
) => {
const newFields = callback(fields)

newFields.forEach((field, index) => {
update(index, field)
})
}

const selectionCount = Object.keys(selection).length

return (
Expand All @@ -162,6 +172,7 @@ export const EditProductMediaForm = ({ product }: ProductMediaViewProps) => {
media={fields}
onCheckedChange={handleCheckedChange}
selection={selection}
onSwapPositions={onSwapPositions}
/>
<div className="bg-ui-bg-base overflow-auto border-b px-6 py-4 lg:border-b-0 lg:border-l">
<UploadMediaFormItem form={form} append={append} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import {
ProductType,
} from "@models"

import { UpdateProductInput } from "@types"
import {
MockEventBusService,
moduleIntegrationTestRunner,
} from "@medusajs/test-utils"
import { UpdateProductInput } from "@types"
import {
buildProductAndRelationsData,
createCollections,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,67 +974,6 @@
}
}
},
{
"columns": {
"product_id": {
"name": "product_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"image_id": {
"name": "image_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"rank": {
"name": "rank",
"type": "integer",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "integer"
}
},
"name": "product_images",
"schema": "public",
"indexes": [
{
"keyName": "product_images_pkey",
"columnNames": ["product_id", "image_id"],
"composite": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"product_images_product_id_foreign": {
"constraintName": "product_images_product_id_foreign",
"columnNames": ["product_id"],
"localTableName": "public.product_images",
"referencedColumnNames": ["id"],
"referencedTableName": "public.product",
"deleteRule": "cascade"
},
"product_images_image_id_foreign": {
"constraintName": "product_images_image_id_foreign",
"columnNames": ["image_id"],
"localTableName": "public.product_images",
"referencedColumnNames": ["id"],
"referencedTableName": "public.image",
"deleteRule": "cascade"
}
}
},
{
"columns": {
"product_id": {
Expand Down
31 changes: 0 additions & 31 deletions packages/modules/product/src/migrations/Migration20241115140922.ts

This file was deleted.

8 changes: 5 additions & 3 deletions packages/modules/product/src/models/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
export { default as Product } from "./product"
export { default as ProductCategory } from "./product-category"
export { default as ProductCollection } from "./product-collection"
export { default as Image } from "./product-image"
export { default as ProductImageProduct } from "./product-image-product"
export { default as ProductOption } from "./product-option"
export { default as ProductOptionValue } from "./product-option-value"
export { default as ProductTag } from "./product-tag"
export { default as ProductType } from "./product-type"
export { default as ProductVariant } from "./product-variant"
export { default as ProductOption } from "./product-option"
export { default as ProductOptionValue } from "./product-option-value"
export { default as Image } from "./product-image"

17 changes: 0 additions & 17 deletions packages/modules/product/src/models/product-image-product.ts

This file was deleted.

Loading

0 comments on commit c714d53

Please sign in to comment.