Skip to content

Commit

Permalink
Merge pull request #10 from himorishige/feat/screenshot
Browse files Browse the repository at this point in the history
add: sticky export function
  • Loading branch information
himorishige authored Jun 1, 2022
2 parents 99a996a + 63f317c commit 14025d8
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 29 deletions.
2 changes: 1 addition & 1 deletion apps/remix-app/app/components/elements/TaskCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Props = {
export const TaskCard: React.FC<Props> = ({ task, completeTaskHandler }) => {
const isMe = false;
return (
<div className="flex flex-col p-4 h-full text-gray-700 bg-yellow-200/75 rounded-md corner_box corner_box_outer">
<div className="flex flex-col p-4 h-full text-gray-700 bg-yellow-200 rounded-md corner_box corner_box_outer">
<div className="flex items-center mb-4">
<div className="flex items-center">
<UserAvatar user={task.owner} isMe={isMe} />
Expand Down
21 changes: 21 additions & 0 deletions apps/remix-app/app/components/icons/DownloadIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ComponentProps } from 'react';

type Props = ComponentProps<'svg'>;

export const DownloadIcon: React.FC<Props> = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
className="w-5 h-5"
fill="none"
viewBox="0 0 22 22"
stroke="currentColor"
strokeWidth={2}
{...props}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
/>
</svg>
);
89 changes: 63 additions & 26 deletions apps/remix-app/app/components/layout/StickyArea.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Fragment, useEffect, useState } from 'react';
import { Fragment, useEffect, useRef, useState } from 'react';
import { Listbox, Transition } from '@headlessui/react';
import { SelectorIcon, FilterIcon, CheckIcon } from '~/components/icons';
import { useAtomValue } from 'jotai';
import { newTaskAtom } from '~/state/store';
import type { Task } from 'board-do';
import { TaskCard } from '~/components/elements';
import type ReconnectingWebSocket from 'reconnecting-websocket';
import html2canvas from 'html2canvas';
import { Button } from '../ui';
import { DownloadIcon } from '../icons/DownloadIcon';

type Props = {
socket: ReconnectingWebSocket | null;
Expand All @@ -21,6 +24,34 @@ export const StickyArea: React.FC<Props> = ({ socket, latestTasks }) => {
const newTasks = useAtomValue(newTaskAtom);
const [latestTasksStore, setLatestTasksStore] = useState(latestTasks);

const target = useRef<HTMLDivElement>(null);

const onClickExport = () => {
if (target.current) {
html2canvas(target.current, {
backgroundColor: 'rgba(186, 230, 253, 1)',
useCORS: true,
}).then((canvas) => {
const targetImgUri = canvas.toDataURL('img/png');
saveAsImage(targetImgUri);
});
}
};

const saveAsImage = (uri: string) => {
const downloadLink = document.createElement('a');

if (typeof downloadLink.download === 'string') {
downloadLink.href = uri;
downloadLink.download = 'sticky-notes.png';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
} else {
window.open(uri);
}
};

const [people, setPeople] = useState([
'all',
...latestTasksStore.map(({ assignee }) => assignee),
Expand Down Expand Up @@ -55,9 +86,9 @@ export const StickyArea: React.FC<Props> = ({ socket, latestTasks }) => {

return (
<>
<div className="p-2 pt-5">
<div className="flex justify-between items-center p-2 pt-5 pr-4 w-full">
<Listbox value={selected} onChange={setSelected}>
<div className="relative">
<div className="relative pr-4 w-full">
<Listbox.Button className="relative py-2 pr-10 pl-3 w-full text-left bg-white rounded-lg focus-visible:border-indigo-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 shadow-md cursor-default sm:text-sm">
<span className="block truncate">
<span className="flex items-center text-gray-700">
Expand Down Expand Up @@ -113,31 +144,37 @@ export const StickyArea: React.FC<Props> = ({ socket, latestTasks }) => {
</Transition>
</div>
</Listbox>
<Button type="button" onClick={onClickExport}>
<DownloadIcon />
<span className="ml-1">export</span>
</Button>
</div>
{latestTasks.length === 0 && newTasks.length === 0 ? (
<div className="p-3 text-center">
<p className="text-gray-500">No stickies</p>
<div ref={target}>
{latestTasks.length === 0 && newTasks.length === 0 ? (
<div className="p-3 text-center">
<p className="text-gray-500">No stickies</p>
</div>
) : null}
<div className="grid gap-2 p-2 sm:grid-cols-2 xl:grid-cols-3">
{newTasks
.filter((task) => task.assignee === selected || selected === 'all')
.map((task) => (
<TaskCard
key={task.id}
task={task}
completeTaskHandler={completeTaskHandler}
/>
))}
{latestTasksStore
.filter((task) => task.assignee === selected || selected === 'all')
.map((task) => (
<TaskCard
key={task.id}
task={task}
completeTaskHandler={completeTaskHandler}
/>
))}
</div>
) : null}
<div className="grid gap-2 p-2 sm:grid-cols-2 xl:grid-cols-3">
{newTasks
.filter((task) => task.assignee === selected || selected === 'all')
.map((task) => (
<TaskCard
key={task.id}
task={task}
completeTaskHandler={completeTaskHandler}
/>
))}
{latestTasksStore
.filter((task) => task.assignee === selected || selected === 'all')
.map((task) => (
<TaskCard
key={task.id}
task={task}
completeTaskHandler={completeTaskHandler}
/>
))}
</div>
</>
);
Expand Down
3 changes: 2 additions & 1 deletion apps/remix-app/app/routes/board.$boardId.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react';
import { createRef, useCallback, useRef, useState } from 'react';
import { useEffect } from 'react';
import type { ActionFunction, LoaderFunction } from '@remix-run/cloudflare';
import { json, redirect } from '@remix-run/cloudflare';
Expand All @@ -19,6 +19,7 @@ import ReconnectingWebSocket from 'reconnecting-websocket';
import { StickyArea } from '~/components/layout';
import { MessageArea } from '~/components/layout/MessageArea';
import toast, { Toaster } from 'react-hot-toast';
import html2canvas from 'html2canvas';

type LoaderData = {
loaderCalls: number;
Expand Down
1 change: 1 addition & 0 deletions apps/remix-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@remix-run/cloudflare": "^1.5.1",
"@remix-run/cloudflare-workers": "^1.5.1",
"@remix-run/react": "^1.5.1",
"html2canvas": "^1.4.1",
"intl-parse-accept-language": "^1.0.0",
"jotai": "^1.6.7",
"qr-image": "^3.2.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/remix-app/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
}

.corner_box_outer {
box-shadow: inset 0 0 30px rgba(255, 219, 112, 0.8);
/* box-shadow: inset 0 0 30px rgba(255, 219, 112, 0.8); */
}

.corner_box:before {
Expand Down
34 changes: 34 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==

base64-arraybuffer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==

base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
Expand Down Expand Up @@ -1932,6 +1937,13 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"

css-line-break@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
dependencies:
utrie "^1.0.2"

cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
Expand Down Expand Up @@ -3452,6 +3464,14 @@ html-rewriter-wasm@^0.4.1:
resolved "https://registry.yarnpkg.com/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz#235e3d96c1aa4bfd2182661ee13881e290ff5ff2"
integrity sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==

html2canvas@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
dependencies:
css-line-break "^2.1.0"
text-segmentation "^1.0.3"

http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
Expand Down Expand Up @@ -6272,6 +6292,13 @@ temp@^0.8.4:
dependencies:
rimraf "~2.6.2"

text-segmentation@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
dependencies:
utrie "^1.0.2"

text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
Expand Down Expand Up @@ -6706,6 +6733,13 @@ [email protected]:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=

utrie@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
dependencies:
base64-arraybuffer "^1.0.2"

uvu@^0.5.0:
version "0.5.3"
resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.3.tgz#3d83c5bc1230f153451877bfc7f4aea2392219ae"
Expand Down

0 comments on commit 14025d8

Please sign in to comment.