diff --git a/.pnp.cjs b/.pnp.cjs index 247d5d1f..ac7f3e3d 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -3006,10 +3006,6 @@ const RAW_RUNTIME_STATE = ["nanoid", "npm:5.0.4"],\ ["rdndmb-html5-to-touch", "npm:8.0.3"],\ ["react", "npm:18.2.0"],\ - ["react-dnd", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:16.0.1"],\ - ["react-dnd-html5-backend", "npm:16.0.1"],\ - ["react-dnd-multi-backend", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:8.0.3"],\ - ["react-dnd-touch-backend", "npm:16.0.1"],\ ["react-dom", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:18.2.0"],\ ["react-hot-toast", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:2.4.1"],\ ["swiper", "npm:11.0.7"],\ @@ -3045,6 +3041,137 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ + ["@dnd-kit/accessibility", [\ + ["npm:3.1.0", {\ + "packageLocation": "./.yarn/cache/@dnd-kit-accessibility-npm-3.1.0-c746ff31d6-4f9d24e801.zip/node_modules/@dnd-kit/accessibility/",\ + "packageDependencies": [\ + ["@dnd-kit/accessibility", "npm:3.1.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:6bf0421413cf10005f0e036c5f66c498340579f89c81403e024ace4de57497d9986177f4ebaa4711134cf1b8244d8a9d60fcb63b1f424ef415fe226687b0cf9b#npm:3.1.0", {\ + "packageLocation": "./.yarn/__virtual__/@dnd-kit-accessibility-virtual-d39a470f5f/0/cache/@dnd-kit-accessibility-npm-3.1.0-c746ff31d6-4f9d24e801.zip/node_modules/@dnd-kit/accessibility/",\ + "packageDependencies": [\ + ["@dnd-kit/accessibility", "virtual:6bf0421413cf10005f0e036c5f66c498340579f89c81403e024ace4de57497d9986177f4ebaa4711134cf1b8244d8a9d60fcb63b1f424ef415fe226687b0cf9b#npm:3.1.0"],\ + ["@types/react", "npm:18.2.48"],\ + ["react", "npm:18.2.0"],\ + ["tslib", "npm:2.6.2"]\ + ],\ + "packagePeers": [\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@dnd-kit/core", [\ + ["npm:6.1.0", {\ + "packageLocation": "./.yarn/cache/@dnd-kit-core-npm-6.1.0-13c1618df7-c793eb97cb.zip/node_modules/@dnd-kit/core/",\ + "packageDependencies": [\ + ["@dnd-kit/core", "npm:6.1.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:6.1.0", {\ + "packageLocation": "./.yarn/__virtual__/@dnd-kit-core-virtual-6bf0421413/0/cache/@dnd-kit-core-npm-6.1.0-13c1618df7-c793eb97cb.zip/node_modules/@dnd-kit/core/",\ + "packageDependencies": [\ + ["@dnd-kit/core", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:6.1.0"],\ + ["@dnd-kit/accessibility", "virtual:6bf0421413cf10005f0e036c5f66c498340579f89c81403e024ace4de57497d9986177f4ebaa4711134cf1b8244d8a9d60fcb63b1f424ef415fe226687b0cf9b#npm:3.1.0"],\ + ["@dnd-kit/utilities", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.2.2"],\ + ["@types/react", "npm:18.2.48"],\ + ["@types/react-dom", "npm:18.2.18"],\ + ["react", "npm:18.2.0"],\ + ["react-dom", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:18.2.0"],\ + ["tslib", "npm:2.6.2"]\ + ],\ + "packagePeers": [\ + "@types/react-dom",\ + "@types/react",\ + "react-dom",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@dnd-kit/modifiers", [\ + ["npm:7.0.0", {\ + "packageLocation": "./.yarn/cache/@dnd-kit-modifiers-npm-7.0.0-a682febbbe-542e1d2b61.zip/node_modules/@dnd-kit/modifiers/",\ + "packageDependencies": [\ + ["@dnd-kit/modifiers", "npm:7.0.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:7.0.0", {\ + "packageLocation": "./.yarn/__virtual__/@dnd-kit-modifiers-virtual-3427504d72/0/cache/@dnd-kit-modifiers-npm-7.0.0-a682febbbe-542e1d2b61.zip/node_modules/@dnd-kit/modifiers/",\ + "packageDependencies": [\ + ["@dnd-kit/modifiers", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:7.0.0"],\ + ["@dnd-kit/core", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:6.1.0"],\ + ["@dnd-kit/utilities", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.2.2"],\ + ["@types/dnd-kit__core", null],\ + ["@types/react", "npm:18.2.48"],\ + ["react", "npm:18.2.0"],\ + ["tslib", "npm:2.6.2"]\ + ],\ + "packagePeers": [\ + "@dnd-kit/core",\ + "@types/dnd-kit__core",\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@dnd-kit/sortable", [\ + ["npm:8.0.0", {\ + "packageLocation": "./.yarn/cache/@dnd-kit-sortable-npm-8.0.0-2d428bbda3-a6066c652b.zip/node_modules/@dnd-kit/sortable/",\ + "packageDependencies": [\ + ["@dnd-kit/sortable", "npm:8.0.0"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:8.0.0", {\ + "packageLocation": "./.yarn/__virtual__/@dnd-kit-sortable-virtual-13c752f3a0/0/cache/@dnd-kit-sortable-npm-8.0.0-2d428bbda3-a6066c652b.zip/node_modules/@dnd-kit/sortable/",\ + "packageDependencies": [\ + ["@dnd-kit/sortable", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:8.0.0"],\ + ["@dnd-kit/core", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:6.1.0"],\ + ["@dnd-kit/utilities", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.2.2"],\ + ["@types/dnd-kit__core", null],\ + ["@types/react", "npm:18.2.48"],\ + ["react", "npm:18.2.0"],\ + ["tslib", "npm:2.6.2"]\ + ],\ + "packagePeers": [\ + "@dnd-kit/core",\ + "@types/dnd-kit__core",\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ + ["@dnd-kit/utilities", [\ + ["npm:3.2.2", {\ + "packageLocation": "./.yarn/cache/@dnd-kit-utilities-npm-3.2.2-3fe8307947-9aa90526f3.zip/node_modules/@dnd-kit/utilities/",\ + "packageDependencies": [\ + ["@dnd-kit/utilities", "npm:3.2.2"]\ + ],\ + "linkType": "SOFT"\ + }],\ + ["virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.2.2", {\ + "packageLocation": "./.yarn/__virtual__/@dnd-kit-utilities-virtual-a3a66ba2b4/0/cache/@dnd-kit-utilities-npm-3.2.2-3fe8307947-9aa90526f3.zip/node_modules/@dnd-kit/utilities/",\ + "packageDependencies": [\ + ["@dnd-kit/utilities", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.2.2"],\ + ["@types/react", "npm:18.2.48"],\ + ["react", "npm:18.2.0"],\ + ["tslib", "npm:2.6.2"]\ + ],\ + "packagePeers": [\ + "@types/react",\ + "react"\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["@emotion/babel-plugin", [\ ["npm:11.11.0", {\ "packageLocation": "./.yarn/cache/@emotion-babel-plugin-npm-11.11.0-c1dcc4c884-89cbb6ec0e.zip/node_modules/@emotion/babel-plugin/",\ @@ -5677,15 +5804,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["@react-dnd/shallowequal", [\ - ["npm:4.0.2", {\ - "packageLocation": "./.yarn/cache/@react-dnd-shallowequal-npm-4.0.2-f944714335-9a352fd176.zip/node_modules/@react-dnd/shallowequal/",\ - "packageDependencies": [\ - ["@react-dnd/shallowequal", "npm:4.0.2"]\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["@react-pdf/fns", [\ ["npm:2.2.1", {\ "packageLocation": "./.yarn/cache/@react-pdf-fns-npm-2.2.1-77536ed89f-457bdff57e.zip/node_modules/@react-pdf/fns/",\ @@ -8573,6 +8691,10 @@ const RAW_RUNTIME_STATE = ["@boolti/icon", "workspace:packages/icon"],\ ["@boolti/typescript-config", "workspace:packages/config-typescript"],\ ["@boolti/ui", "workspace:packages/ui"],\ + ["@dnd-kit/core", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:6.1.0"],\ + ["@dnd-kit/modifiers", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:7.0.0"],\ + ["@dnd-kit/sortable", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:8.0.0"],\ + ["@dnd-kit/utilities", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.2.2"],\ ["@emotion/babel-plugin", "npm:11.11.0"],\ ["@emotion/react", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:11.11.3"],\ ["@emotion/styled", "virtual:85869d3eba7afdb6f94c001c9503942ddc4354e881daf63c24e9d58366ea9f25c6bac2df65ae0f5266c54cd36fe68f0d9568da3a1ab62446405c98ac852f4431#npm:11.11.0"],\ @@ -8592,8 +8714,6 @@ const RAW_RUNTIME_STATE = ["qrcode.react", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.1.0"],\ ["react", "npm:18.2.0"],\ ["react-daum-postcode", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:3.1.3"],\ - ["react-dnd", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:16.0.1"],\ - ["react-dnd-html5-backend", "npm:16.0.1"],\ ["react-dom", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:18.2.0"],\ ["react-dropzone", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:14.2.3"],\ ["react-hook-form", "virtual:9845906954fdbefbb879db24fa8772d77a945dca59f459806df47a5b67245d4bc6502880b373cca7201062c81bea9f13f699f52de2004c037e79dbdbd5d97fb3#npm:7.50.0"],\ @@ -16314,37 +16434,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["react-dnd", [\ - ["npm:16.0.1", {\ - "packageLocation": "./.yarn/cache/react-dnd-npm-16.0.1-974f047d7b-d069435750.zip/node_modules/react-dnd/",\ - "packageDependencies": [\ - ["react-dnd", "npm:16.0.1"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:16.0.1", {\ - "packageLocation": "./.yarn/__virtual__/react-dnd-virtual-42d9d58644/0/cache/react-dnd-npm-16.0.1-974f047d7b-d069435750.zip/node_modules/react-dnd/",\ - "packageDependencies": [\ - ["react-dnd", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:16.0.1"],\ - ["@react-dnd/invariant", "npm:4.0.2"],\ - ["@react-dnd/shallowequal", "npm:4.0.2"],\ - ["@types/hoist-non-react-statics", null],\ - ["@types/node", null],\ - ["@types/react", "npm:18.2.48"],\ - ["dnd-core", "npm:16.0.1"],\ - ["fast-deep-equal", "npm:3.1.3"],\ - ["hoist-non-react-statics", "npm:3.3.2"],\ - ["react", "npm:18.2.0"]\ - ],\ - "packagePeers": [\ - "@types/hoist-non-react-statics",\ - "@types/node",\ - "@types/react",\ - "react"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["react-dnd-html5-backend", [\ ["npm:16.0.1", {\ "packageLocation": "./.yarn/cache/react-dnd-html5-backend-npm-16.0.1-754940d855-6e4b632a11.zip/node_modules/react-dnd-html5-backend/",\ @@ -16355,68 +16444,6 @@ const RAW_RUNTIME_STATE = "linkType": "HARD"\ }]\ ]],\ - ["react-dnd-multi-backend", [\ - ["npm:8.0.3", {\ - "packageLocation": "./.yarn/cache/react-dnd-multi-backend-npm-8.0.3-6fb78e4cb8-9a6842cdf2.zip/node_modules/react-dnd-multi-backend/",\ - "packageDependencies": [\ - ["react-dnd-multi-backend", "npm:8.0.3"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:8.0.3", {\ - "packageLocation": "./.yarn/__virtual__/react-dnd-multi-backend-virtual-008794a211/0/cache/react-dnd-multi-backend-npm-8.0.3-6fb78e4cb8-9a6842cdf2.zip/node_modules/react-dnd-multi-backend/",\ - "packageDependencies": [\ - ["react-dnd-multi-backend", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:8.0.3"],\ - ["@types/dnd-core", null],\ - ["@types/react", "npm:18.2.48"],\ - ["@types/react-dnd", null],\ - ["@types/react-dom", "npm:18.2.18"],\ - ["dnd-core", null],\ - ["dnd-multi-backend", "virtual:1586002902c50f74c93bfc4432d5e9af4b44f1b1eb4b98750b8199eba8f53742e35a69aa3f476fc38f5ef4b29df79e58cd9bc0e0b0fe33fa0debed3793886ce3#npm:8.0.3"],\ - ["react", "npm:18.2.0"],\ - ["react-dnd", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:16.0.1"],\ - ["react-dnd-preview", "virtual:008794a211e0a134262fd7402818c5dec9b64ef6af463a2d4fc2130b03414955ff8c5a754770d2c797ea02e410f3409167cfc59cf1c1817d320877423dca6497#npm:8.0.3"],\ - ["react-dom", "virtual:de80dc576383b2386358abc0e9fe49c00e3397fe355a0337462b73ab3115c2e557eb85784ee0fe776394cc11dd020b4e84dbbd75acf72ee6d54415d82d21f5c5#npm:18.2.0"]\ - ],\ - "packagePeers": [\ - "@types/dnd-core",\ - "@types/react-dnd",\ - "@types/react-dom",\ - "@types/react",\ - "dnd-core",\ - "react-dnd",\ - "react-dom",\ - "react"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ - ["react-dnd-preview", [\ - ["npm:8.0.3", {\ - "packageLocation": "./.yarn/cache/react-dnd-preview-npm-8.0.3-73bdadc57c-f010d04a38.zip/node_modules/react-dnd-preview/",\ - "packageDependencies": [\ - ["react-dnd-preview", "npm:8.0.3"]\ - ],\ - "linkType": "SOFT"\ - }],\ - ["virtual:008794a211e0a134262fd7402818c5dec9b64ef6af463a2d4fc2130b03414955ff8c5a754770d2c797ea02e410f3409167cfc59cf1c1817d320877423dca6497#npm:8.0.3", {\ - "packageLocation": "./.yarn/__virtual__/react-dnd-preview-virtual-7bf065d0d7/0/cache/react-dnd-preview-npm-8.0.3-73bdadc57c-f010d04a38.zip/node_modules/react-dnd-preview/",\ - "packageDependencies": [\ - ["react-dnd-preview", "virtual:008794a211e0a134262fd7402818c5dec9b64ef6af463a2d4fc2130b03414955ff8c5a754770d2c797ea02e410f3409167cfc59cf1c1817d320877423dca6497#npm:8.0.3"],\ - ["@types/react", "npm:18.2.48"],\ - ["@types/react-dnd", null],\ - ["react", "npm:18.2.0"],\ - ["react-dnd", "virtual:9ef42ff9c873460955cc48cd9b15127324f3d1f83a4bea8e6327df0101bb993bef095b175f8d10a3f0d23ee47f702ca3ef7272cba815f708e8609d03d84b96a2#npm:16.0.1"]\ - ],\ - "packagePeers": [\ - "@types/react-dnd",\ - "@types/react",\ - "react-dnd",\ - "react"\ - ],\ - "linkType": "HARD"\ - }]\ - ]],\ ["react-dnd-touch-backend", [\ ["npm:16.0.1", {\ "packageLocation": "./.yarn/cache/react-dnd-touch-backend-npm-16.0.1-2b96ba84be-8809614693.zip/node_modules/react-dnd-touch-backend/",\ diff --git a/apps/admin/package.json b/apps/admin/package.json index f49ff77f..cc43cfd1 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -15,6 +15,10 @@ "@boolti/api": "*", "@boolti/icon": "*", "@boolti/ui": "*", + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/modifiers": "^7.0.0", + "@dnd-kit/sortable": "^8.0.0", + "@dnd-kit/utilities": "^3.2.2", "@emotion/react": "^11.11.3", "@emotion/styled": "^11.11.0", "@react-pdf/renderer": "^3.4.4", @@ -29,8 +33,6 @@ "qrcode.react": "^3.1.0", "react": "^18.2.0", "react-daum-postcode": "^3.1.3", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", "react-hook-form": "^7.50.0", diff --git a/apps/admin/src/components/ShowCastInfo/ShowCastInfo.styles.ts b/apps/admin/src/components/ShowCastInfo/ShowCastInfo.styles.ts index 0cee2948..357e8a21 100644 --- a/apps/admin/src/components/ShowCastInfo/ShowCastInfo.styles.ts +++ b/apps/admin/src/components/ShowCastInfo/ShowCastInfo.styles.ts @@ -1,8 +1,9 @@ -import { Button, mq_lg } from '@boolti/ui'; +import { mq_lg } from '@boolti/ui'; import styled from '@emotion/styled'; import { m } from 'framer-motion'; const Container = styled.div` + position: relative; border-radius: 8px; background: ${({ theme }) => theme.palette.grey.w}; box-shadow: 0px 8px 14px 0px ${({ theme }) => theme.palette.shadow}; @@ -36,7 +37,7 @@ const Handle = styled.button` align-items: center; justify-content: center; color: ${({ theme }) => theme.palette.grey.g40}; - cursor: move; + cursor: grab; user-select: none; user-zoom: none; ` @@ -46,9 +47,7 @@ const Name = styled.span` ${({ theme }) => theme.typo.sh2}; `; -const EditButton = styled(Button)` - padding: 13px 18px; - +const EditButtonWrapper = styled.div` span { display: none; margin-left: 8px; @@ -150,7 +149,7 @@ export default { Name, Cast, CollapseButton, - EditButton, + EditButtonWrapper, CastItem, UserImage, Username, diff --git a/apps/admin/src/components/ShowCastInfo/index.tsx b/apps/admin/src/components/ShowCastInfo/index.tsx index be9ff360..d58a0a8e 100644 --- a/apps/admin/src/components/ShowCastInfo/index.tsx +++ b/apps/admin/src/components/ShowCastInfo/index.tsx @@ -1,122 +1,95 @@ -import { useDialog } from '@boolti/ui'; -import { useDrag, useDrop } from 'react-dnd' +import { TextButton, useDialog } from '@boolti/ui'; +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; import Styled from './ShowCastInfo.styles'; import { EditIcon, ChevronDownIcon, ChevronUpIcon, UserIcon, MenuIcon } from '@boolti/icon'; -import { useRef, useState } from 'react'; +import { useState } from 'react'; import ShowCastInfoFormDialogContent, { TempShowCastInfoFormInput, } from '../ShowCastInfoFormDialogContent'; interface Props { showCastInfo: TempShowCastInfoFormInput; - index: number; onSave: (value: TempShowCastInfoFormInput) => Promise; - onDropHover: (draggedItemId: number, hoverIndex: number) => void; - onDrop?: () => void; onDelete?: () => Promise; } -interface DragItem { - id: number - index: number -} - -const ShowCastInfo = ({ showCastInfo, index, onSave, onDropHover, onDrop, onDelete }: Props) => { - const ref = useRef(null) - const [{ isDragging }, drag, preview] = useDrag(() => ({ - type: 'castTeam', - previewOptions: { - captureDraggingState: true, - }, - item: { id: showCastInfo.id, index }, - collect: (monitor) => ({ - isDragging: monitor.isDragging() - }), - })) - const [, drop] = useDrop({ - accept: 'castTeam', - hover(item: DragItem, monitor) { - if (!ref.current) return; - if (!monitor.canDrop()) return; - if (item.id === showCastInfo.id) return; - - const dragIndex = item.index; - const hoverIndex = index; - - const hoverBoundingRect = ref.current.getBoundingClientRect(); - const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; - const clientOffset = monitor.getClientOffset(); - if (!clientOffset) return; - - const hoverClientY = clientOffset.y - hoverBoundingRect.top; - if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return; - if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return; - - item.index = hoverIndex; - - onDropHover(item.id, index); - }, - drop() { - onDrop?.() - } - }) - +const ShowCastInfo = ({ showCastInfo, onSave, onDelete }: Props) => { const { members = [] } = showCastInfo; const memberLength = members.length ?? 0; const dialog = useDialog(); const [isOpen, setIsOpen] = useState(false); - const toggle = () => setIsOpen((prev) => !prev); + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging + } = useSortable({ id: showCastInfo.id }); - preview(drop(ref)) + const style = { + transform: CSS.Translate.toString(transform), + transition, + backgroundColor: isDragging ? 'rgba(231, 234, 242, 0.5)' : undefined, + backdropFilter: isDragging ? 'blur(3px)' : undefined, + zIndex: isDragging ? 100 : 99, + cursor: isDragging ? 'grabbing' : undefined, + }; + + const toggle = () => setIsOpen((prev) => !prev); return ( - + - { event.stopPropagation() }}> + {showCastInfo.name} - { - e.preventDefault(); - dialog.open({ - title: '출연진 정보 편집', - isAuto: true, - content: ( - { - try { - await onSave(castInfo); - dialog.close(); - } catch { - return new Promise((_, reject) => reject('저장 중 오류가 발생하였습니다.')); - } - }} - prevShowCastInfo={showCastInfo} - onDelete={async () => { - try { - await onDelete?.(); - dialog.close(); - } catch { - return new Promise((_, reject) => reject('삭제 중 오류가 발생하였습니다.')); - } - }} - /> - ), - }); - }} - > - - 정보 편집 - + + { + e.preventDefault(); + dialog.open({ + title: '출연진 정보 편집', + isAuto: true, + content: ( + { + try { + await onSave(castInfo); + dialog.close(); + } catch { + return new Promise((_, reject) => reject('저장 중 오류가 발생하였습니다.')); + } + }} + prevShowCastInfo={showCastInfo} + onDelete={async () => { + try { + await onDelete?.(); + dialog.close(); + } catch { + return new Promise((_, reject) => reject('삭제 중 오류가 발생하였습니다.')); + } + }} + /> + ), + }); + }} + > + + 정보 편집 + + {memberLength > 0 && ( <> @@ -141,6 +114,7 @@ const ShowCastInfo = ({ showCastInfo, index, onSave, onDropHover, onDrop, onDele ))} { e.preventDefault(); toggle(); diff --git a/apps/admin/src/components/ShowCastInfoFormDialogContent/DraggableShowCastInfoMemberRow.tsx b/apps/admin/src/components/ShowCastInfoFormDialogContent/DraggableShowCastInfoMemberRow.tsx new file mode 100644 index 00000000..162f1188 --- /dev/null +++ b/apps/admin/src/components/ShowCastInfoFormDialogContent/DraggableShowCastInfoMemberRow.tsx @@ -0,0 +1,30 @@ +import { useSortable } from "@dnd-kit/sortable"; +import { CSS } from '@dnd-kit/utilities'; +import ShowCastInfoMemberRow, { ShowCastInfoMemberRowProps } from "./ShowCastInfoMemberRow"; + +interface DraggableShowCastInfoMemberRowProps extends ShowCastInfoMemberRowProps { + id: string +} + +const DraggableShowCastInfoMemberRow = ({ id, ...props }: DraggableShowCastInfoMemberRowProps) => { + const { + attributes, + listeners, + setNodeRef, + transform, + transition, + isDragging + } = useSortable({ id }); + + const style = { + transform: CSS.Translate.toString(transform), + transition, + opacity: isDragging ? 0 : 1 + }; + + return ( + + ) +} + +export default DraggableShowCastInfoMemberRow \ No newline at end of file diff --git a/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoFormDialogContent.styles.ts b/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoFormDialogContent.styles.ts index 315ad466..77d44bef 100644 --- a/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoFormDialogContent.styles.ts +++ b/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoFormDialogContent.styles.ts @@ -29,6 +29,9 @@ const ShowInfoFormLabel = styled.span` `; const MemberList = styled.div` + display: flex; + flex-direction: column; + gap: 20px; max-height: 364px; overflow-y: scroll; ::-webkit-scrollbar { @@ -70,7 +73,7 @@ const Handle = styled.button` color: ${({ theme }) => theme.palette.grey.g40}; margin-top: 12px; margin-right: 8px; - cursor: move; + cursor: grab; user-select: none; user-zoom: none; ` @@ -102,9 +105,9 @@ const Row = styled.div` display: flex; justify-content: center; align-items: flex-start; - margin-bottom: 20px; - background-color: ${({ theme }) => theme.palette.grey.w}; border-radius: 4px; + position: relative; + z-index: 99; `; const TrashCanButton = styled.button` @@ -185,6 +188,30 @@ const ErrorMessage = styled.span` color: ${({ theme }) => theme.palette.status.error}; `; +const DraggableShowCastInfoMemberRow = styled.div` + border-radius: 4px; + cursor: grabbing; + backdrop-filter: blur(1.5px); + z-index: 100; + + & > div > div > div { + background: none; + } + + &::after { + content: ''; + position: absolute; + top: -10px; + left: -10px; + width: calc(100% + 20px); + height: calc(100% + 20px); + background-color: rgba(231, 234, 242, 0.5); + border-radius: 4px; + z-index: -1; + } +` + + export default { ShowInfoFormLabel, InputWrapper, @@ -204,4 +231,5 @@ export default { DeleteButton, ErrorMessage, FieldWrap, + DraggableShowCastInfoMemberRow }; diff --git a/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoMemberRow.tsx b/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoMemberRow.tsx index b14b7a64..db016ab9 100644 --- a/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoMemberRow.tsx +++ b/apps/admin/src/components/ShowCastInfoFormDialogContent/ShowCastInfoMemberRow.tsx @@ -1,80 +1,27 @@ -import { useDrag, useDrop } from "react-dnd"; import { ClearIcon, MenuIcon, TrashIcon, UserIcon } from '@boolti/icon'; import { Member } from '@boolti/api'; import { replaceUserCode } from '~/utils/replace'; import Styled from './ShowCastInfoFormDialogContent.styles'; import { TempShowCastInfoFormInput } from "."; import { Control, Controller } from "react-hook-form"; -import { useRef } from "react"; +import { forwardRef } from 'react'; -interface DragItem { - id: number - index: number -} - -interface ShowCastInfoMemberRowProps { +export interface ShowCastInfoMemberRowProps { control: Control; - field: Partial & { id: number }; + field: Partial & { id: number, _id: string }; index: number; isFieldBlurred: { userCode: boolean; roleName: boolean }; - onSetUser: (userCode: string) => void; - onResetUser: () => void; - onBlurRoleName: () => void; - onDelete: () => void - onDropHover: (draggedItemId: number, hoverIndex: number) => void; - onDrop?: () => void; + draggingStyle?: React.CSSProperties; + onSetUser?: (userCode: string) => void; + onResetUser?: () => void; + onBlurRoleName?: () => void; + onDelete?: () => void } -const ShowCastInfoMemberRow = ({ control, field, index, isFieldBlurred, onSetUser, onResetUser, onBlurRoleName, onDelete, onDropHover, onDrop }: ShowCastInfoMemberRowProps) => { - const ref = useRef(null) - const [{ isDragging }, drag, preview] = useDrag(() => ({ - type: 'castMember', - previewOptions: { - captureDraggingState: true, - }, - item: { id: field.id, index }, - collect: (monitor) => ({ - isDragging: monitor.isDragging() - }), - })) - const [, drop] = useDrop({ - accept: 'castMember', - hover(item: DragItem, monitor) { - if (!ref.current) return; - if (!monitor.canDrop()) return; - if (item.id === field.id) return; - - const dragIndex = item.index; - const hoverIndex = index; - - const hoverBoundingRect = ref.current.getBoundingClientRect(); - const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; - const clientOffset = monitor.getClientOffset(); - if (!clientOffset) return; - - const hoverClientY = clientOffset.y - hoverBoundingRect.top; - if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return; - if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return; - - item.index = hoverIndex; - - onDropHover(item.id, index); - }, - drop() { - onDrop?.() - } - }) - - preview(drop(ref)) - +const ShowCastInfoMemberRow = forwardRef(({ control, field, index, isFieldBlurred, draggingStyle, onSetUser, onResetUser, onBlurRoleName, onDelete, ...props }, ref) => { return ( - - { event.stopPropagation() }}> + + { onChange(undefined); - onResetUser(); + onResetUser?.(); }} > @@ -122,7 +69,7 @@ const ShowCastInfoMemberRow = ({ control, field, index, isFieldBlurred, onSetUse }} onBlur={async (event) => { onBlur(); - onSetUser(event.target.value); + onSetUser?.(event.target.value); }} value={value ?? ''} /> @@ -152,7 +99,7 @@ const ShowCastInfoMemberRow = ({ control, field, index, isFieldBlurred, onSetUse onChange={onChange} onBlur={() => { onBlur(); - onBlurRoleName(); + onBlurRoleName?.(); }} value={value ?? ''} /> @@ -170,6 +117,6 @@ const ShowCastInfoMemberRow = ({ control, field, index, isFieldBlurred, onSetUse ); -} +}); export default ShowCastInfoMemberRow diff --git a/apps/admin/src/components/ShowCastInfoFormDialogContent/index.tsx b/apps/admin/src/components/ShowCastInfoFormDialogContent/index.tsx index e7dbf9ee..e4deb806 100644 --- a/apps/admin/src/components/ShowCastInfoFormDialogContent/index.tsx +++ b/apps/admin/src/components/ShowCastInfoFormDialogContent/index.tsx @@ -1,11 +1,15 @@ import { TextField, useConfirm, useToast } from '@boolti/ui'; import { Controller, useFieldArray, useForm } from 'react-hook-form'; import Styled from './ShowCastInfoFormDialogContent.styles'; -import { useCallback, useRef, useState } from 'react'; +import { useCallback, useState } from 'react'; import { useBodyScrollLock } from '~/hooks/useBodyScrollLock'; import { PlusIcon } from '@boolti/icon'; import { Member, queryKeys, useQueryClient } from '@boolti/api'; import ShowCastInfoMemberRow from './ShowCastInfoMemberRow'; +import { DndContext, DragOverlay, DragStartEvent, DragOverEvent, UniqueIdentifier, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors, closestCenter } from '@dnd-kit/core'; +import { SortableContext, arrayMove, verticalListSortingStrategy, sortableKeyboardCoordinates } from '@dnd-kit/sortable'; +import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; +import DraggableShowCastInfoMemberRow from './DraggableShowCastInfoMemberRow'; export interface TempShowCastInfoFormInput { id: number; @@ -42,7 +46,6 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P ...watchMemberFields[index], }; }); - const prevControlledFields = useRef(controlledFields); const toast = useToast(); const confirm = useConfirm(); @@ -68,27 +71,53 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P (!userNickname || !roleName), )); - const dragHoverHandler = useCallback((draggedItemId: number, hoverIndex: number) => { - const draggedItemIndex = controlledFields.findIndex(({ id }) => id === draggedItemId); - if (draggedItemIndex === -1 || hoverIndex < 0 || hoverIndex >= controlledFields.length) { - return; - } + const [draggingItemId, setDraggingItemId] = useState(null); + const draggingField = controlledFields.find(({ _id }) => _id === draggingItemId); + const draggingFieldIndex = controlledFields.findIndex(({ _id }) => _id === draggingItemId); - prevControlledFields.current = { ...controlledFields }; + const sensors = useSensors( + useSensor(MouseSensor, { + activationConstraint: { + distance: 10, + }, + }), + useSensor(TouchSensor, { + activationConstraint: { + delay: 0, + tolerance: 5, + }, + }), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); + + const dragStartHandler = (event: DragStartEvent) => { + setDraggingItemId(event.active.id); + }; - const nextFields = [...fields]; - const [draggedItem] = nextFields.splice(draggedItemIndex, 1); - nextFields.splice(hoverIndex, 0, draggedItem); + const dragEndHandler = useCallback((event: DragOverEvent) => { + const { active, over } = event; + if (!(active && over && over.id !== active.id)) return; + + const oldIndex = controlledFields.findIndex(({ _id }) => _id === active.id); + const newIndex = controlledFields.findIndex(({ _id }) => _id === over.id); + + if (oldIndex === -1 || newIndex === -1) return; + + const nextFields = arrayMove(controlledFields, oldIndex, newIndex); replace(nextFields); setIsMemberFieldBlurred((prev) => { const nextMemberFieldBlurred = [...prev]; - nextMemberFieldBlurred.splice(draggedItemIndex, 1); - nextMemberFieldBlurred.splice(hoverIndex, 0, prev[draggedItemIndex]); + nextMemberFieldBlurred.splice(oldIndex, 1); + nextMemberFieldBlurred.splice(newIndex, 0, prev[oldIndex]); return nextMemberFieldBlurred; }) - }, [fields, controlledFields, replace]) + + setDraggingItemId(null); + }, [controlledFields, replace]); return ( <> @@ -119,74 +148,90 @@ const ShowCastInfoFormDialogContent = ({ prevShowCastInfo, onDelete, onSave }: P /> 팀원 - {controlledFields.map((field, index) => ( - { - if (userCode !== '') { - try { - const { imgPath, nickname } = await queryClient.fetchQuery( - queryKeys.user.userCode(userCode), - ); + + field._id)} strategy={verticalListSortingStrategy}> + {controlledFields.map((field, index) => ( + { + if (userCode !== '') { + try { + const { imgPath, nickname } = await queryClient.fetchQuery( + queryKeys.user.userCode(userCode), + ); + update(index, { + ...controlledFields[index], + userImgPath: imgPath, + userNickname: nickname, + }); + } catch { + toast.error( + '불티에 회원으로 등록된 식별 코드로만 등록이 가능합니다.' + + '\n' + + '식별 코드를 확인 후 다시 시도해 주세요.', + ); + } finally { + setIsMemberFieldBlurred((prev) => { + const nextMemberFieldBlurred = [...prev]; + nextMemberFieldBlurred[index].userCode = true; + return nextMemberFieldBlurred; + }); + } + } + }} + onResetUser={() => { + setIsMemberFieldBlurred((prev) => { + const nextMemberFieldBlurred = [...prev]; + nextMemberFieldBlurred[index].userCode = true; + return nextMemberFieldBlurred; + }); update(index, { - ...controlledFields[index], - userImgPath: imgPath, - userNickname: nickname, + id: field.id, + roleName: field.roleName, }); - } catch { - toast.error( - '불티에 회원으로 등록된 식별 코드로만 등록이 가능합니다.' + - '\n' + - '식별 코드를 확인 후 다시 시도해 주세요.', - ); - } finally { + }} + onBlurRoleName={() => { setIsMemberFieldBlurred((prev) => { const nextMemberFieldBlurred = [...prev]; - nextMemberFieldBlurred[index].userCode = true; + nextMemberFieldBlurred[index].roleName = true; return nextMemberFieldBlurred; }); - } - } - }} - onResetUser={() => { - setIsMemberFieldBlurred((prev) => { - const nextMemberFieldBlurred = [...prev]; - nextMemberFieldBlurred[index].userCode = true; - return nextMemberFieldBlurred; - }); - update(index, { - id: field.id, - roleName: field.roleName, - }); - }} - onBlurRoleName={() => { - setIsMemberFieldBlurred((prev) => { - const nextMemberFieldBlurred = [...prev]; - nextMemberFieldBlurred[index].roleName = true; - return nextMemberFieldBlurred; - }); - }} - onDelete={async () => { - const isConfirm = await confirm('팀원 정보를 삭제하시겠어요?', { - confirm: '삭제하기', - cancel: '취소하기', - }); + }} + onDelete={async () => { + const isConfirm = await confirm('팀원 정보를 삭제하시겠어요?', { + confirm: '삭제하기', + cancel: '취소하기', + }); - if (isConfirm) { - toast.success('팀원 정보를 삭제했습니다.'); - setIsMemberFieldBlurred((prev) => - prev.filter((_, blurredIndex) => blurredIndex !== index), - ); - remove(index); - } - }} - onDropHover={dragHoverHandler} - /> - ))} + if (isConfirm) { + toast.success('팀원 정보를 삭제했습니다.'); + setIsMemberFieldBlurred((prev) => + prev.filter((_, blurredIndex) => blurredIndex !== index), + ); + remove(index); + } + }} + /> + ))} + + + {(draggingItemId && draggingField && draggingFieldIndex > -1) ? ( + + + + ) : null} + + { append({ id: -Math.floor(Math.random() * 1000000) }); diff --git a/apps/admin/src/hooks/useCastTeamListOrder.ts b/apps/admin/src/hooks/useCastTeamListOrder.ts index 1d1f3af6..121c6cd4 100644 --- a/apps/admin/src/hooks/useCastTeamListOrder.ts +++ b/apps/admin/src/hooks/useCastTeamListOrder.ts @@ -1,5 +1,8 @@ import { useChangeCastTeamOrder } from "@boolti/api"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { DragOverEvent, KeyboardSensor, MouseSensor, TouchSensor, useSensor, useSensors } from "@dnd-kit/core"; +import { arrayMove, sortableKeyboardCoordinates } from '@dnd-kit/sortable'; + import { TempShowCastInfoFormInput } from "~/components/ShowCastInfoFormDialogContent"; interface UseCastTeamListOrderParams { @@ -11,59 +14,74 @@ interface UseCastTeamListOrderParams { const useCastTeamListOrder = (params?: UseCastTeamListOrderParams) => { const showId = params?.showId; const castTeamList = params?.castTeamList; - const onChange = params?.onChange; const [castTeamListDraft, setCastTeamListDraft] = useState([]); + const currentCastTeamIds = useRef(null) const changeCastTeamOrder = useChangeCastTeamOrder(); - const changeCastTeamIndex = useCallback((draggedItemId: number, targetIndex: number) => { - setCastTeamListDraft((prevDraft) => { - if (!prevDraft) return prevDraft; - - const draggedItemIndex = prevDraft.findIndex(({ id }) => id === draggedItemId); - if (draggedItemIndex === -1 || targetIndex < 0 || targetIndex >= prevDraft.length) { - return prevDraft; - } - - const nextDraft = [...prevDraft]; - const [draggedItem] = nextDraft.splice(draggedItemIndex, 1); - nextDraft.splice(targetIndex, 0, draggedItem); - - return nextDraft; - }) - }, []) - - const castTeamDropHoverHandler = useCallback((draggedItemId: number, hoverIndex: number) => { - changeCastTeamIndex(draggedItemId, hoverIndex); - }, [changeCastTeamIndex]); - - const castTeamDropHandler = useCallback(async () => { - if (!castTeamListDraft) return; + const castTeamDragEndHandler = useCallback((event: DragOverEvent) => { + const { active, over } = event; - if (showId !== undefined) { - await changeCastTeamOrder.mutateAsync({ - showId, - body: { - castTeamIds: castTeamListDraft.map(({ id }) => id), - }, + if (active && over && over.id !== active.id) { + setCastTeamListDraft((prev) => { + const oldIndex = prev.findIndex(({ id }) => id === active.id); + const newIndex = prev.findIndex(({ id }) => id === over.id); + + return arrayMove(prev, oldIndex, newIndex); }); } - - onChange?.(); - }, [castTeamListDraft, changeCastTeamOrder, onChange, showId]) + }, []); + + const fetchCastTeamOrder = useCallback(async (castTeamIds: number[]) => { + if (showId === undefined || castTeamIds.length === 0) return; + + await changeCastTeamOrder.mutateAsync({ + showId, + body: { + castTeamIds, + }, + }); + }, [changeCastTeamOrder, showId]); + + const sensors = useSensors( + useSensor(MouseSensor, { + activationConstraint: { + distance: 10, + }, + }), + useSensor(TouchSensor, { + activationConstraint: { + delay: 0, + tolerance: 5, + }, + }), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); useEffect(() => { if (!castTeamList) return; setCastTeamListDraft(castTeamList); - }, [castTeamList]) + }, [castTeamList]); + + useEffect(() => { + const castTeamIds = castTeamListDraft.map(({ id }) => id) + const stringifiedCastTeamIds = JSON.stringify(castTeamIds); + + if (stringifiedCastTeamIds === currentCastTeamIds.current) return; + + currentCastTeamIds.current = stringifiedCastTeamIds; + fetchCastTeamOrder(castTeamIds); + }, [castTeamListDraft, fetchCastTeamOrder]); return { castTeamListDraft, + sensors, setCastTeamListDraft, - castTeamDropHoverHandler, - castTeamDropHandler, + castTeamDragEndHandler, } } diff --git a/apps/admin/src/pages/ShowAddPage/index.tsx b/apps/admin/src/pages/ShowAddPage/index.tsx index 7c9ed9a5..f8495a78 100644 --- a/apps/admin/src/pages/ShowAddPage/index.tsx +++ b/apps/admin/src/pages/ShowAddPage/index.tsx @@ -9,6 +9,9 @@ import { Button, useToast } from '@boolti/ui'; import { useState } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; import { Navigate, useNavigate } from 'react-router-dom'; +import { DndContext, closestCenter } from '@dnd-kit/core'; +import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'; +import { restrictToVerticalAxis } from '@dnd-kit/modifiers'; import ShowBasicInfoFormContent from '~/components/ShowInfoFormContent/ShowBasicInfoFormContent'; import ShowDetailInfoFormContent from '~/components/ShowInfoFormContent/ShowDetailInfoFormContent'; @@ -46,7 +49,7 @@ const ShowAddPage = ({ step }: ShowAddPageProps) => { const uploadShowImageMutation = useUploadShowImage(); const addShowMutation = useAddShow(); - const { castTeamListDraft, setCastTeamListDraft, castTeamDropHoverHandler, castTeamDropHandler } = useCastTeamListOrder(); + const { castTeamListDraft, sensors, setCastTeamListDraft, castTeamDragEndHandler } = useCastTeamListOrder(); const toast = useToast(); @@ -175,29 +178,30 @@ const ShowAddPage = ({ step }: ShowAddPageProps) => { return new Promise((reslve) => reslve()); }} /> - {castTeamListDraft.map((info, index) => ( - { - setCastTeamListDraft((prev) => - prev.map((prevCastInfo, currentIndex) => - index === currentIndex ? showCastInfoFormInput : prevCastInfo, - ) - ); - return new Promise((reslve) => reslve()); - }} - onDelete={() => { - setCastTeamListDraft((prev) => - prev.filter((_, currentIndex) => index !== currentIndex) - ); - return new Promise((reslve) => reslve()); - }} - onDrop={castTeamDropHandler} - onDropHover={castTeamDropHoverHandler} - /> - ))} + + info.id)} strategy={verticalListSortingStrategy}> + {castTeamListDraft.map((info) => ( + { + setCastTeamListDraft((prev) => + prev.map((item) => + item.id === info.id ? showCastInfoFormInput : item, + ) + ); + return new Promise((reslve) => reslve()); + }} + onDelete={() => { + setCastTeamListDraft((prev) => + prev.filter((item) => item.id !== info.id) + ); + return new Promise((reslve) => reslve()); + }} + /> + ))} + + - - - { - await postCastTeams.mutateAsync( - { - showId, - name, - members: members - ?.filter(({ userCode, roleName }) => userCode && roleName) - .map(({ id, userCode, roleName }) => ({ - id: id < 0 ? undefined : id, - userCode, - roleName, - })) as ShowCastTeamCreateOrUpdateRequest['members'], - }, - { - onSuccess: () => { - queryClient.invalidateQueries(queryKeys.castTeams.list(showId)); - }, + + + + { + await postCastTeams.mutateAsync( + { + showId, + name, + members: members + ?.filter(({ userCode, roleName }) => userCode && roleName) + .map(({ id, userCode, roleName }) => ({ + id: id < 0 ? undefined : id, + userCode, + roleName, + })) as ShowCastTeamCreateOrUpdateRequest['members'], + }, + { + onSuccess: () => { + queryClient.invalidateQueries(queryKeys.castTeams.list(showId)); }, - ); - }} - /> - {castTeamListDraft?.map((info, index) => ( - { - if (info.id === undefined) return; + }, + ); + }} + /> + + info.id)} strategy={verticalListSortingStrategy}> + {castTeamListDraft?.map((info) => ( + { + if (info.id === undefined) return; - await putCastTeams.mutateAsync( - { - name, - members: members - ?.filter(({ userCode, roleName }) => userCode && roleName) - .map(({ id, userCode, roleName }) => ({ - id: id < 0 ? undefined : id, - userCode, - roleName, - })) as ShowCastTeamCreateOrUpdateRequest['members'], - castTeamId: info.id, - }, - { + await putCastTeams.mutateAsync( + { + name, + members: members + ?.filter(({ userCode, roleName }) => userCode && roleName) + .map(({ id, userCode, roleName }) => ({ + id: id < 0 ? undefined : id, + userCode, + roleName, + })) as ShowCastTeamCreateOrUpdateRequest['members'], + castTeamId: info.id, + }, + { + onSuccess: () => { + queryClient.invalidateQueries(queryKeys.castTeams.list(showId)); + }, + }, + ); + }} + onDelete={async () => { + if (info.id === undefined) return; + + await deleteCastTeams.mutateAsync(info.id, { onSuccess: () => { queryClient.invalidateQueries(queryKeys.castTeams.list(showId)); }, - }, - ); - }} - onDropHover={castTeamDropHoverHandler} - onDrop={castTeamDropHandler} - onDelete={async () => { - if (info.id === undefined) return; + }); + }} + /> + ))} + + + + + + + + + { + setPreviewDrawerOpen(false); + }} + > + + + + + + + + + file.preview), + name: showInfoForm.watch('name') ? showInfoForm.watch('name') : '', + date: showInfoForm.watch('date') + ? format(showInfoForm.watch('date'), 'yyyy.MM.dd (E)') + : '', + startTime: showInfoForm.watch('startTime'), + runningTime: showInfoForm.watch('runningTime'), + salesStartTime: showSalesInfo + ? format(showSalesInfo.salesStartTime, 'yyyy.MM.dd (E)') + : '', + salesEndTime: showSalesInfo + ? format(showSalesInfo.salesEndTime, 'yyyy.MM.dd (E)') + : '', + placeName: showInfoForm.watch('placeName'), + placeStreetAddress: showInfoForm.watch('placeStreetAddress'), + placeDetailAddress: showInfoForm.watch('placeDetailAddress'), + notice: showInfoForm.watch('notice'), + hostName: showInfoForm.watch('hostName'), + hostPhoneNumber: showInfoForm.watch('hostPhoneNumber'), + }} + showCastTeams={castTeamList} + hasNoticePage + containerRef={showPreviewRef} + /> + + + + + + { + setPreviewDrawerOpen(false); }} - /> - ))} - - - - - - - { - setPreviewDrawerOpen(false); - }} - > - + 저장하기 + + + + + {previewDrawerOpen && ( + + - - - - - - - file.preview), - name: showInfoForm.watch('name') ? showInfoForm.watch('name') : '', - date: showInfoForm.watch('date') - ? format(showInfoForm.watch('date'), 'yyyy.MM.dd (E)') - : '', - startTime: showInfoForm.watch('startTime'), - runningTime: showInfoForm.watch('runningTime'), - salesStartTime: showSalesInfo - ? format(showSalesInfo.salesStartTime, 'yyyy.MM.dd (E)') - : '', - salesEndTime: showSalesInfo - ? format(showSalesInfo.salesEndTime, 'yyyy.MM.dd (E)') - : '', - placeName: showInfoForm.watch('placeName'), - placeStreetAddress: showInfoForm.watch('placeStreetAddress'), - placeDetailAddress: showInfoForm.watch('placeDetailAddress'), - notice: showInfoForm.watch('notice'), - hostName: showInfoForm.watch('hostName'), - hostPhoneNumber: showInfoForm.watch('hostPhoneNumber'), - }} - showCastTeams={castTeamList} - hasNoticePage - containerRef={showPreviewRef} - /> - - - + file.preview), + name: showInfoForm.watch('name') ? showInfoForm.watch('name') : '', + date: showInfoForm.watch('date') + ? format(showInfoForm.watch('date'), 'yyyy.MM.dd (E)') + : '', + startTime: showInfoForm.watch('startTime'), + runningTime: showInfoForm.watch('runningTime'), + salesStartTime: showSalesInfo + ? format(showSalesInfo.salesStartTime, 'yyyy.MM.dd (E)') + : '', + salesEndTime: showSalesInfo + ? format(showSalesInfo.salesEndTime, 'yyyy.MM.dd (E)') + : '', + placeName: showInfoForm.watch('placeName'), + placeStreetAddress: showInfoForm.watch('placeStreetAddress'), + placeDetailAddress: showInfoForm.watch('placeDetailAddress'), + notice: showInfoForm.watch('notice'), + hostName: showInfoForm.watch('hostName'), + hostPhoneNumber: showInfoForm.watch('hostPhoneNumber'), + }} + showCastTeams={castTeamList} + hasNoticePage + containerRef={showPreviewMobileRef} + /> { 저장하기 - - - {previewDrawerOpen && ( - - - - file.preview), - name: showInfoForm.watch('name') ? showInfoForm.watch('name') : '', - date: showInfoForm.watch('date') - ? format(showInfoForm.watch('date'), 'yyyy.MM.dd (E)') - : '', - startTime: showInfoForm.watch('startTime'), - runningTime: showInfoForm.watch('runningTime'), - salesStartTime: showSalesInfo - ? format(showSalesInfo.salesStartTime, 'yyyy.MM.dd (E)') - : '', - salesEndTime: showSalesInfo - ? format(showSalesInfo.salesEndTime, 'yyyy.MM.dd (E)') - : '', - placeName: showInfoForm.watch('placeName'), - placeStreetAddress: showInfoForm.watch('placeStreetAddress'), - placeDetailAddress: showInfoForm.watch('placeDetailAddress'), - notice: showInfoForm.watch('notice'), - hostName: showInfoForm.watch('hostName'), - hostPhoneNumber: showInfoForm.watch('hostPhoneNumber'), - }} - showCastTeams={castTeamList} - hasNoticePage - containerRef={showPreviewMobileRef} - /> - - - { - setPreviewDrawerOpen(false); - }} - > - 닫기 - - { - showInfoForm.handleSubmit(onSubmit)(); - }} - > - 저장하기 - - - - - )} - + + + )} )} diff --git a/packages/ui/package.json b/packages/ui/package.json index d056ecb5..07a9198e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -19,10 +19,6 @@ "nanoid": "^5.0.4", "rdndmb-html5-to-touch": "^8.0.3", "react": "^18.2.0", - "react-dnd": "^16.0.1", - "react-dnd-html5-backend": "^16.0.1", - "react-dnd-multi-backend": "^8.0.3", - "react-dnd-touch-backend": "^16.0.1", "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", "swiper": "^11.0.7" diff --git a/packages/ui/src/components/BooltiUIProvider/index.tsx b/packages/ui/src/components/BooltiUIProvider/index.tsx index 68db223a..28972924 100644 --- a/packages/ui/src/components/BooltiUIProvider/index.tsx +++ b/packages/ui/src/components/BooltiUIProvider/index.tsx @@ -1,30 +1,25 @@ -import { DndProvider } from 'react-dnd-multi-backend' -import { HTML5toTouch } from 'rdndmb-html5-to-touch' - -import '../../index.css'; import AlertProvider from '../AlertProvider'; - import ConfirmProvider from '../ConfirmProvider'; import DialogProvider from '../DialogProvider'; import ThemeProvider from '../ThemeProvider'; +import '../../index.css'; + interface BooltiUIProviderProps { children: React.ReactNode; } const BooltiUIProvider = ({ children }: BooltiUIProviderProps) => { return ( - - - - - - {children} - - - - - + + + + + {children} + + + + ); }; diff --git a/yarn.lock b/yarn.lock index 744e3429..3ab0c8b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1732,10 +1732,6 @@ __metadata: nanoid: "npm:^5.0.4" rdndmb-html5-to-touch: "npm:^8.0.3" react: "npm:^18.2.0" - react-dnd: "npm:^16.0.1" - react-dnd-html5-backend: "npm:^16.0.1" - react-dnd-multi-backend: "npm:^8.0.3" - react-dnd-touch-backend: "npm:^16.0.1" react-dom: "npm:^18.2.0" react-hot-toast: "npm:^2.4.1" swiper: "npm:^11.0.7" @@ -1764,6 +1760,68 @@ __metadata: languageName: node linkType: hard +"@dnd-kit/accessibility@npm:^3.1.0": + version: 3.1.0 + resolution: "@dnd-kit/accessibility@npm:3.1.0" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/4f9d24e801d66d4fbb551ec389ed90424dd4c5bbdf527000a618e9abb9833cbd84d9a79e362f470ccbccfbd6d00217a9212c92f3cef66e01c951c7f79625b9d7 + languageName: node + linkType: hard + +"@dnd-kit/core@npm:^6.1.0": + version: 6.1.0 + resolution: "@dnd-kit/core@npm:6.1.0" + dependencies: + "@dnd-kit/accessibility": "npm:^3.1.0" + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/c793eb97cb59285ca8937ebcdfcd27cff09d750ae06722e36ca5ed07925e41abc36a38cff98f9f6056f7a07810878d76909826142a2968330e7e22060e6be584 + languageName: node + linkType: hard + +"@dnd-kit/modifiers@npm:^7.0.0": + version: 7.0.0 + resolution: "@dnd-kit/modifiers@npm:7.0.0" + dependencies: + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@dnd-kit/core": ^6.1.0 + react: ">=16.8.0" + checksum: 10c0/542e1d2b6102a5c826118c36158aab23c5437d24008cab4848b0866d3d850b4410c4f465690767dd1f31fde33a1fa9d238675be70f174c179485ce376f0c8aa6 + languageName: node + linkType: hard + +"@dnd-kit/sortable@npm:^8.0.0": + version: 8.0.0 + resolution: "@dnd-kit/sortable@npm:8.0.0" + dependencies: + "@dnd-kit/utilities": "npm:^3.2.2" + tslib: "npm:^2.0.0" + peerDependencies: + "@dnd-kit/core": ^6.1.0 + react: ">=16.8.0" + checksum: 10c0/a6066c652b892c6a11320c7d8f5c18fdf723e721e8eea37f4ab657dee1ac5e7ca710ac32ce0712a57fe968bc07c13bcea5d5599d90dfdd95619e162befd4d2fb + languageName: node + linkType: hard + +"@dnd-kit/utilities@npm:^3.2.2": + version: 3.2.2 + resolution: "@dnd-kit/utilities@npm:3.2.2" + dependencies: + tslib: "npm:^2.0.0" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/9aa90526f3e3fd567b5acc1b625a63177b9e8d00e7e50b2bd0e08fa2bf4dba7e19529777e001fdb8f89a7ce69f30b190c8364d390212634e0afdfa8c395e85a0 + languageName: node + linkType: hard + "@emotion/babel-plugin@npm:^11.11.0": version: 11.11.0 resolution: "@emotion/babel-plugin@npm:11.11.0" @@ -3326,13 +3384,6 @@ __metadata: languageName: node linkType: hard -"@react-dnd/shallowequal@npm:^4.0.1": - version: 4.0.2 - resolution: "@react-dnd/shallowequal@npm:4.0.2" - checksum: 10c0/9a352fd176752e5d9c2797d598aca034b7829111ae0c992d80f40d5f068fcd6a039b1841c741dfa1ab67a36a00664310aec4f0ce216e4112f80875c9fe6fd8dc - languageName: node - linkType: hard - "@react-pdf/fns@npm:2.2.1": version: 2.2.1 resolution: "@react-pdf/fns@npm:2.2.1" @@ -5394,6 +5445,10 @@ __metadata: "@boolti/icon": "npm:*" "@boolti/typescript-config": "npm:*" "@boolti/ui": "npm:*" + "@dnd-kit/core": "npm:^6.1.0" + "@dnd-kit/modifiers": "npm:^7.0.0" + "@dnd-kit/sortable": "npm:^8.0.0" + "@dnd-kit/utilities": "npm:^3.2.2" "@emotion/babel-plugin": "npm:^11.11.0" "@emotion/react": "npm:^11.11.3" "@emotion/styled": "npm:^11.11.0" @@ -5413,8 +5468,6 @@ __metadata: qrcode.react: "npm:^3.1.0" react: "npm:^18.2.0" react-daum-postcode: "npm:^3.1.3" - react-dnd: "npm:^16.0.1" - react-dnd-html5-backend: "npm:^16.0.1" react-dom: "npm:^18.2.0" react-dropzone: "npm:^14.2.3" react-hook-form: "npm:^7.50.0" @@ -8558,7 +8611,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.3.1": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -11711,31 +11764,6 @@ __metadata: languageName: node linkType: hard -"react-dnd-multi-backend@npm:^8.0.3": - version: 8.0.3 - resolution: "react-dnd-multi-backend@npm:8.0.3" - dependencies: - dnd-multi-backend: "npm:^8.0.3" - react-dnd-preview: "npm:^8.0.3" - peerDependencies: - dnd-core: ^16.0.1 - react: ^16.14.0 || ^17.0.2 || ^18.0.0 - react-dnd: ^16.0.1 - react-dom: ^16.14.0 || ^17.0.2 || ^18.0.0 - checksum: 10c0/9a6842cdf290bf4cd28105dc14cfe8aa6c09bd1f0da74fd459c2300aa26d0b141fcf5e2906dbe77b46eb6f555a93f7e1fdd64bac6930cd8d971992be79a6b39c - languageName: node - linkType: hard - -"react-dnd-preview@npm:^8.0.3": - version: 8.0.3 - resolution: "react-dnd-preview@npm:8.0.3" - peerDependencies: - react: ^16.14.0 || ^17.0.2 || ^18.0.0 - react-dnd: ^16.0.1 - checksum: 10c0/f010d04a386debe37c5375a0655686dc00b6e42f3b45aabb074ac52f9f1d92379fec552714991125789cbc7d370f94b0100b6c77d2d41037e87bff00234bc3ef - languageName: node - linkType: hard - "react-dnd-touch-backend@npm:^16.0.1": version: 16.0.1 resolution: "react-dnd-touch-backend@npm:16.0.1" @@ -11746,31 +11774,6 @@ __metadata: languageName: node linkType: hard -"react-dnd@npm:^16.0.1": - version: 16.0.1 - resolution: "react-dnd@npm:16.0.1" - dependencies: - "@react-dnd/invariant": "npm:^4.0.1" - "@react-dnd/shallowequal": "npm:^4.0.1" - dnd-core: "npm:^16.0.1" - fast-deep-equal: "npm:^3.1.3" - hoist-non-react-statics: "npm:^3.3.2" - peerDependencies: - "@types/hoist-non-react-statics": ">= 3.3.1" - "@types/node": ">= 12" - "@types/react": ">= 16" - react: ">= 16.14" - peerDependenciesMeta: - "@types/hoist-non-react-statics": - optional: true - "@types/node": - optional: true - "@types/react": - optional: true - checksum: 10c0/d069435750f0d6653cfa2b951cac8abb3583fb144ff134a20176608877d9c5964c63384ebbacaa0fdeef819b592a103de0d8e06f3b742311d64a029ffed0baa3 - languageName: node - linkType: hard - "react-docgen-typescript@npm:^2.2.2": version: 2.2.2 resolution: "react-docgen-typescript@npm:2.2.2"