Skip to content

Commit

Permalink
Merge pull request #1967 from roberdm49/TP-2066-grid-image-picker
Browse files Browse the repository at this point in the history
TP-2066: Grid image picker
  • Loading branch information
aguescribano87 authored Nov 1, 2024
2 parents 5ffbf74 + fcc7f7e commit ce9acef
Show file tree
Hide file tree
Showing 32 changed files with 1,005 additions and 13 deletions.
File renamed without changes.
2 changes: 1 addition & 1 deletion docs/iframe.html
Original file line number Diff line number Diff line change
Expand Up @@ -345,4 +345,4 @@



window['STORIES'] = [];</script><script src="runtime~main.c541cfd3.iframe.bundle.js"></script><script src="vendors~main.c2101b29.iframe.bundle.js"></script><script src="main.0c936991.iframe.bundle.js"></script></body></html>
window['STORIES'] = [];</script><script src="runtime~main.e88fcb0c.iframe.bundle.js"></script><script src="vendors~main.98038cc7.iframe.bundle.js"></script><script src="main.87c86a8d.iframe.bundle.js"></script></body></html>
3 changes: 0 additions & 3 deletions docs/main.0c936991.iframe.bundle.js

This file was deleted.

1 change: 0 additions & 1 deletion docs/main.0c936991.iframe.bundle.js.map

This file was deleted.

3 changes: 3 additions & 0 deletions docs/main.87c86a8d.iframe.bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/main.87c86a8d.iframe.bundle.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions docs/vendors~main.98038cc7.iframe.bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/vendors~main.98038cc7.iframe.bundle.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions docs/vendors~main.c2101b29.iframe.bundle.js

This file was deleted.

1 change: 0 additions & 1 deletion docs/vendors~main.c2101b29.iframe.bundle.js.map

This file was deleted.

3 changes: 3 additions & 0 deletions packages/gridImagePicker/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.spec.js
*.story.js
src
41 changes: 41 additions & 0 deletions packages/gridImagePicker/hooks/useAspectRatio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useEffect, useState } from 'react'

function useAspectRatio (src) {
const [data, setData] = useState({
aspectRatio: 0,
loading: true,
error: false,
})

useEffect(() => {
const fetchAspectRatio = async (src) => {
try {
setData({ aspectRatio: 0, loading: true, error: false })

const img = new window.Image()
img.src = src

img.onload = () => {
const aspectRatio = img.width / img.height
setData({ aspectRatio, loading: false, error: false })
}

img.onerror = (error) => {
console.error(error)
setData({ aspectRatio: 0, loading: false, error: true })
}
} catch (error) {
console.error(error)
setData({ aspectRatio: 0, loading: false, error: true })
}
}

if (src) {
fetchAspectRatio(src)
}
}, [src])

return [data.aspectRatio, data.loading, data.error]
}

export default useAspectRatio
109 changes: 109 additions & 0 deletions packages/gridImagePicker/hooks/useGridImagePicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { useCallback, useEffect, useState } from 'react'
import { arrayMove } from '@dnd-kit/sortable'
import { isItemClickable } from '../utils/manageItem'
import {
getItemsInitialState,
getUpdatedItemsWithDecrement,
getUpdatedItemsWithIncrement,
refreshItemsPosition,
fillCheckedItems,
getSelectAllItems,
getDeselectAllItems,
} from '../utils/manageListOfItems'
import '../styles/GridImagePicker.css'

function useGridImagePicker ({
listOfSrc,
maxSelectablePreferenceByUser,
maxSizeInMB,
minAspectRatio,
maxAspectRatio,
onChange,
}) {
const [isDraggingActive, setIsDraggingActive] = useState(false)
const [items, setItems] = useState(() => getItemsInitialState(listOfSrc))
const maxSelectable = Math.min(
maxSelectablePreferenceByUser,
items.filter(item => !item.sizeError && !item.aspectRatioError && !item.fetchError).length
)
const numberOfCheckedItems = items.filter(item => item.checked).length
const isMaxSelectableReached = numberOfCheckedItems >= maxSelectable
const itemsAreReady = items.every(item => !item.loading)

const config = { maxSizeInMB, minAspectRatio, maxAspectRatio, maxSelectable }
const status = { isDraggingActive, isMaxSelectableReached, itemsAreReady, numberOfCheckedItems }

useEffect(() => {
if (!onChange) return

onChange(items)
}, [items])

useEffect(() => {
if (!itemsAreReady) return

const updatedItems = fillCheckedItems(items, maxSelectable)
setItems(updatedItems)
}, [itemsAreReady])

const handleClickItem = useCallback((targetItem) => {
const isUnclickable = !isItemClickable(targetItem, isMaxSelectableReached)
if (isUnclickable) return

const updatedItems = targetItem.checked
? getUpdatedItemsWithDecrement(items, targetItem)
: getUpdatedItemsWithIncrement(items, targetItem)

const refreshedItems = refreshItemsPosition(updatedItems)
setItems(refreshedItems)
}, [isMaxSelectableReached, items])

const handleSelectAll = useCallback(() => {
const newItems = getSelectAllItems(items, maxSelectable)
const refreshedItems = refreshItemsPosition(newItems)
setItems(refreshedItems)
}, [items, maxSelectable])

const handleDeselectAll = useCallback(() => {
const newItems = getDeselectAllItems(items)
setItems(newItems)
}, [items])

const handleUpdateItem = useCallback(({ id, ...restOfKeys }) => {
const newItems = items.map(item => item.id === id
? { ...item, ...restOfKeys }
: item
)
setItems(newItems)
}, [items])

const handleDragStart = () => {
setIsDraggingActive(true)
}

const handleDragEnd = ({ active, over }) => {
setIsDraggingActive(false)
if (active.id === over.id) return

const oldIndex = items.findIndex(item => item.id === active.id)
const newIndex = items.findIndex(item => item.id === over.id)
const reorderedItems = arrayMove(items, oldIndex, newIndex)
const refreshedItems = refreshItemsPosition(reorderedItems)

setItems(refreshedItems)
}

return {
attributes: { items, config, status },
methods: {
handleClickItem,
handleUpdateItem,
handleDeselectAll,
handleSelectAll,
handleDragStart,
handleDragEnd,
},
}
}

export default useGridImagePicker
34 changes: 34 additions & 0 deletions packages/gridImagePicker/hooks/useSize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useEffect, useState } from 'react'

function useSize (src) {
const [data, setData] = useState({
size: 0,
loading: true,
error: false,
})

useEffect(() => {
const fetchImageSize = async (src) => {
try {
setData({ size: 0, loading: true, error: false })
const response = await window.fetch(src)
const blob = await response.blob()

const sizeInMegaBytes = (blob.size / 1024) / 1024

setData({ size: sizeInMegaBytes, loading: false, error: false })
} catch (error) {
console.error(error)
setData({ size: 0, loading: false, error: true })
}
}

if (src) {
fetchImageSize(src)
}
}, [src])

return [data.size, data.loading, data.error]
}

export default useSize
24 changes: 24 additions & 0 deletions packages/gridImagePicker/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@oneloop/gridimagepicker",
"version": "1.0.0",
"main": "lib/index.js",
"module": "src/index.js",
"files": [
"lib"
],
"dependencies": {
"@oneloop/box": "^1.2.285",
"@oneloop/icons": "^1.2.309",
"@dnd-kit/core": "^6.0.8",
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/utilities": "^3.2.2"
},
"peerDependencies": {
"react": "^16.8.0"
},
"publishConfig": {
"access": "public"
},
"gitHead": "d65c6a2618eac0470ecc4f462e8905e0aaf1bd9d"
}
39 changes: 39 additions & 0 deletions packages/gridImagePicker/src/GridImagePicker.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react'
import { GridImagePicker } from '.'

// 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKUlEQVR4nO3NMQEAAAjDMED5pGMCvlRA00nqs3m9AwAAAAAAAAAAgMMWvj0BwDVKG0sAAAAASUVORK5CYII=',
// 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAAAgCAIAAAAt/+nTAAAAQElEQVR4nO3PQQ0AIBDAMED5SUcEj4ZkVbDtmVk/OzrgVQNaA1oDWgNaA1oDWgNaA1oDWgNaA1oDWgNaA1oD2gVLaQHAY0QxtQAAAABJRU5ErkJggg==',
// 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAACACAIAAADlBPxNAAAASUlEQVR4nO3NMQEAAAjDMED5pKMCrlRA00nqsjm9AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4ABYzLwKAkfv2VAAAAABJRU5ErkJggg=='

const INSTAGRAM_RECOMMENDED_MIN_ASPECT_RATIO = 4 / 5
const INSTAGRAM_RECOMMENDED_MAX_ASPECT_RATIO = 1.91 / 1

export default {
component: GridImagePicker,
title: 'GridImagePicker',
}

export const normal = () => (
<GridImagePicker
minAspectRatio={INSTAGRAM_RECOMMENDED_MIN_ASPECT_RATIO}
maxAspectRatio={INSTAGRAM_RECOMMENDED_MAX_ASPECT_RATIO}
listOfSrc={[
'https://images.pexels.com/photos/39866/entrepreneur-startup-start-up-man-39866.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcREoRGyXmHy_6aIgXYqWHdOT3KjfmnuSyxypw&s',
'https://img-cdn.pixlr.com/image-generator/history/65bb506dcb310754719cf81f/ede935de-1138-4f66-8ed7-44bd16efc709/medium.webp',
'https://images.pexels.com/photos/1054666/pexels-photo-1054666.jpeg?cs=srgb&dl=pexels-hsapir-1054666.jpg&fm=jpg',
'https://letsenhance.io/static/8f5e523ee6b2479e26ecc91b9c25261e/1015f/MainAfter.jpg',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRQYq7qbO4AdXp0gXMxvGmnVSc-8OlsMSGrNw&s',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQq10gYXsQjI1uLya6MjHpGkLIX5kzVNQYOaQ&s',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTwB8-oiew0oNWLIPqQGPhcNJOYEzkaPKAbXw&s',
'https://b.thumbs.redditmedia.com/s-QXg7UUFth82jkXneJB5ahQMdyCsIhG0eAL-iFBRlU.jpg',
'https://media.vandal.net/i/640x360/10-2023/17/202310171652270_2.jpg',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRb3Nakk8OPx919ZVYgOxBq_FFapYcAEVQdBQ&s',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQHKxBCtsEIetdYRldyKjADBJ88W_gk_Sh3qg&s',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTGbUyUaLlMBdDIiUzN-v1yaBhAk24zzVaRNg&s',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRuj-4zb0fZ8bRiVZ3JhkNcm3iU26Oj42HwVg&s',
'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRBExwuuCqBEPFxceL1tTggoj8XaYvxr0X2hA&s',
'https://cdn.britannica.com/05/187505-131-7E335F18/Benedict-Cumberbatch-2014.jpg',
]}
/>
)
Loading

0 comments on commit ce9acef

Please sign in to comment.