Skip to content

Commit

Permalink
feat(puzzle): add puzzle component
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerd Müller committed Jun 13, 2020
1 parent 4c86b83 commit f673acf
Show file tree
Hide file tree
Showing 44 changed files with 278 additions and 63 deletions.
2 changes: 1 addition & 1 deletion apps/demol/src/app/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ html {
min-height: 100vh;
scroll-behavior: smooth;
text-rendering: optimizeSpeed;
line-height: 1.6;
line-height: 1.2;
}

html, body, div, span, applet, object, iframe,
Expand Down
Binary file added apps/demol/src/assets/daheim_puzzle.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_01.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_02.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_03.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_04.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_05.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_06.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_07.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_08.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_09.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_10.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_11.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_12.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_13.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_14.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_15.webp
Binary file not shown.
Binary file added apps/demol/src/assets/daheim_puzzle_16.webp
Binary file not shown.
Binary file added apps/demol/src/assets/fallback/daheim_puzzle.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions libs/ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from './lib/scrollicon/scrollicon';
export * from './lib/puzzle/puzzle';
export * from './lib/smokingpit/smokingpit';
export * from './lib/decission/decission';
export * from './lib/image/image';
Expand Down
44 changes: 0 additions & 44 deletions libs/ui/src/lib/chapter/chapter.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,60 +36,16 @@
z-index: 4;
background: 50% 50% / cover;
filter: saturate(.8);

picture {
margin-top: 50%;
}
}

&--back {
transform: translateZ(-300px) scale(2);
z-index: 3;

picture {
margin-top: -25%;
}
}

&--deep {
transform: translateZ(-600px) scale(3);
z-index: 2;
}
}
}

.icon-scroll,
.icon-scroll:before {
position: absolute;
left: 50%;
}
.icon-scroll {
width: 48px;
height: 84px;
margin-left: -20px;
top: 85%;
margin-top: -35px;
border: 4px solid var(--color-title);
border-radius: 25px;
}
.icon-scroll:before {
content: '';
width: 10px;
height: 10px;
background: var(--color-title);
margin-left: -4px;
top: 8px;
border-radius: 4px;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-name: scroll;
}
@keyframes scroll {
0% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(46px);
}
}
39 changes: 21 additions & 18 deletions libs/ui/src/lib/chapter/chapter.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react';
import React, { Fragment } from 'react';
import Title from '../title/title';
import Video from '../video/video';
import Image from '../image/image';
import Text from '../text/text';
import Decission from '../decission/decission';
import Smokingpit from '../smokingpit/smokingpit';
import Puzzle from '../puzzle/puzzle';
import Scrollicon from '../scrollicon/scrollicon';
import { makeStyles } from '@material-ui/core/styles';
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import { Chapter, ContentType } from '@gerdesque/data';
Expand All @@ -28,32 +30,33 @@ export const ChapterComponent = (props: Chapter) => {
</div>)
}

const renderContent = (content, index) => {
return <Fragment key={index}>
{content.type === ContentType.Text && <Text value={content.value} />}
{content.type === ContentType.Video && <Video value={content.value} width={content.layer} title={content.title} />}
{content.type === ContentType.Image && <Image value={content.value} width={content.layer}/>}
{content.type === ContentType.Decission && <Decission value={content.value} />}
{content.type === ContentType.SmokingPit && <Smokingpit value={content.value} />}
{content.type === ContentType.Puzzle && <Puzzle/>}
</Fragment>
}

const renderChapterContent = ({content : contentList, grouped, row}) => {

const chapterGroupContent =
<div className={`parallax__layer parallax__layer--fore grouped ${row ? 'row' : 'column'}`}>
{contentList.map((content) =>
<>
{content.type === ContentType.Text && <Text value={content.value} />}
{content.type === ContentType.Video && <Video value={content.value} title={content.title} />}
{content.type === ContentType.Image && <Image value={content.value} />}
{content.type === ContentType.Decission && <Decission value={content.value} />}
{content.type === ContentType.Misc && <Smokingpit value={content.value} />}
</>
const chapterGroupedContent =
<div className={`parallax__layer parallax__layer--base grouped ${row ? 'row' : 'column'}`}>
{contentList.map((content, index) =>
renderContent(content, index)
)}
</div>;

const chapterContent =
contentList.map((content, index) =>
<div key={index} className={`parallax__layer parallax__layer--${content.layer}`}>
{content.type === ContentType.Text && <Text value={content.value} />}
{content.type === ContentType.Video && <Video value={content.value} title={content.title} />}
{content.type === ContentType.Image && <Image value={content.value} />}
{content.type === ContentType.Decission && <Decission value={content.value} />}
{content.type === ContentType.Misc && <Smokingpit value={content.value} />}
{renderContent(content, index)}
</div>);

return grouped ? chapterGroupContent : chapterContent
return grouped ? chapterGroupedContent : chapterContent
}

return (
Expand All @@ -64,7 +67,7 @@ export const ChapterComponent = (props: Chapter) => {
<audio controls loop>
<source src={"./assets/sounds/daheim.mp3"} type='audio/mpeg' />
</audio>
<div className='icon-scroll'></div>
<Scrollicon/>
</div>
</div>
{props.groups && renderChapterGroups()}
Expand Down
76 changes: 76 additions & 0 deletions libs/ui/src/lib/puzzle/puzzle.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// .puzzleBox {
// display: flex;
// flex-direction: column;
// align-items: center;
// justify-content: center;
// }

// .puzzleBox .description {
// width: 75vw;
// margin: auto;
// text-align: center;
// }

// .puzzle{
// display: flex;
// flex-flow: row wrap;
// }

// .puzzle > * {
// flex: 1 50%;
// }

.puzzle__solved-board{
position: relative;
background-size: cover;
}

.puzzle__solved-board:before{
content: '';
background-color: rgba(255, 255, 255, 0.6);
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
}

.puzzle__solved-board,
.puzzle__shuffled-board{
margin: 20px auto;
width: 400px;
list-style-type: none;
padding: 0;
font-size: 0;
border: 1px solid #DDD;
border-width: 1px 0 0 1px;
}

.puzzle li{
width: 99px;
height: 99px;
position: relative;
text-align: left;
display: inline-block;
border: 1px solid #DDD;
border-width: 0 1px 1px 0;
}

.puzzle li:empty:hover:before{
opacity: 1;
}

.puzzle li img{
width: 100px;
height: 100px;
position: absolute;
cursor: grab;
transition: transform 200ms ease, box-shadow 200ms ease;
}

.puzzle li img:hover{
z-index: 2;
transform: scale(1.1);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.8);
}
11 changes: 11 additions & 0 deletions libs/ui/src/lib/puzzle/puzzle.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { render } from '@testing-library/react';

import Puzzle from './puzzle';

describe(' Puzzle', () => {
it('should render successfully', () => {
const { baseElement } = render(<Puzzle />);
expect(baseElement).toBeTruthy();
});
});
106 changes: 106 additions & 0 deletions libs/ui/src/lib/puzzle/puzzle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, {useState} from 'react';
import { IMAGE_SUFFIX } from '@gerdesque/data';
import { makeStyles } from '@material-ui/core/styles';
import './puzzle.scss';

export const Puzzle = () => {
const useStyles = makeStyles(() => ({
puzzle: {
backgroundImage: `url(${"./assets/daheim_puzzle"+IMAGE_SUFFIX})`,
boxShadow: '0 0 8px 8px #dcd5cc inset',
},
}));
const classes = useStyles();

const pieces = [...Array(16)].map((_, i) => ({
img: `daheim_puzzle_${("0" + (i + 1)).substr(-2)}`,
order: i,
board: "shuffled"
}));

const shufflePieces = (pieces) => {
const shuffled = [...pieces];

for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}

return shuffled;
}
const [shuffled, setShuffled] = useState(shufflePieces(pieces));
const [solved, setSolved] = useState([...Array(16)]);

// const handleDrop = (e, targetName) => {
// let target = this.state[targetName];
// const pieceOrder = e.dragData.piece.order;
// if (target[pieceOrder]) return;

// const pieceData = this.state.pieces.find(p => p.order === +pieceOrder);
// const origin = this.state[pieceData.board];

// if (targetName === pieceData.board) target = origin;
// origin[origin.indexOf(pieceData)] = undefined;
// target[pieceOrder] = pieceData;
// pieceData.board = targetName;

// this.setState({ [pieceData.board]: origin, [targetName]: target });
// this.checkBoard();
// }

const handleDrop = (e, index, targetName) => {
e.preventDefault();
let target = targetName === 'shuffled' ? shuffled : solved;
//if (target[index]) return;

const pieceOrder = e.dataTransfer.getData('text');
const pieceData = pieces.find(p => p.order === +pieceOrder);
const origin = pieceData.board === 'shuffled' ? shuffled : solved;

if (targetName === pieceData.board) target = origin;
//origin[origin.indexOf(pieceData)] = undefined;
//target[pieceOrder] = pieceData;
//pieceData.board = targetName;

// this.setState({ [pieceData.board]: origin, [targetName]: target });

const newShuffled = [...shuffled];
newShuffled[newShuffled.indexOf(pieceData)] = undefined;

solved[index] = pieceData;
setShuffled(newShuffled);
}

const handleDragStart = (e, order) => {
e.dataTransfer.setData('text/plain', order);
}

const renderPieceContainer = (piece, index, boardName) => {
return (
<li key={index}
onDragOver={(e) => e.preventDefault()}
onDrop={(e) => handleDrop(e, index, boardName)}>
{piece && <img
draggable
onDragStart={(e) => handleDragStart(e, piece.order)}
alt=""
src={`./assets/${piece.img}.webp`}/>}
</li>
);
}

return (
<div className='puzzleBox'>
<div className='puzzle'>
<ul className='puzzle__shuffled-board'>
{shuffled.map((piece, i) => renderPieceContainer(piece, i, "shuffled"))}
</ul>
<ol className={`puzzle__solved-board ${classes.puzzle}`}>
{solved.map((piece, i) => renderPieceContainer(piece, i, "solved"))}
</ol>
</div>
</div>
);
};

export default Puzzle;
36 changes: 36 additions & 0 deletions libs/ui/src/lib/scrollicon/scrollicon.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

.icon-scroll,
.icon-scroll:before {
position: absolute;
left: 50%;
}
.icon-scroll {
width: 48px;
height: 84px;
margin-left: -20px;
top: 85%;
margin-top: -35px;
border: 4px solid var(--color-title);
border-radius: 25px;
}
.icon-scroll:before {
content: '';
width: 10px;
height: 10px;
background: var(--color-title);
margin-left: -4px;
top: 8px;
border-radius: 4px;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-name: scroll;
}
@keyframes scroll {
0% {
opacity: 1;
}
100% {
opacity: 0;
transform: translateY(46px);
}
}
11 changes: 11 additions & 0 deletions libs/ui/src/lib/scrollicon/scrollicon.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import { render } from '@testing-library/react';

import Scrollicon from './scrollicon';

describe(' Scrollicon', () => {
it('should render successfully', () => {
const { baseElement } = render(<Scrollicon />);
expect(baseElement).toBeTruthy();
});
});
Loading

0 comments on commit f673acf

Please sign in to comment.