@sunggang/ui-lib 0.0.3 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/.eslintrc.json +18 -0
  2. package/.storybook/main.js +14 -0
  3. package/.storybook/preview.js +1 -0
  4. package/.storybook/tailwind-imports.css +81 -0
  5. package/components.json +15 -0
  6. package/package.json +8 -4
  7. package/postcss.config.js +10 -0
  8. package/project.json +31 -0
  9. package/src/components/ui/switch.tsx +27 -0
  10. package/src/index.ts +7 -0
  11. package/src/lib/DropImage/index.stories.tsx +42 -0
  12. package/src/lib/DropImage/index.tsx +110 -0
  13. package/src/lib/Modal/BaseModal.jsx +52 -0
  14. package/src/lib/Modal/ErrorContent.tsx +21 -0
  15. package/src/lib/Modal/FullScreenModal.jsx +39 -0
  16. package/src/lib/Modal/Modal.tsx +36 -0
  17. package/src/lib/Modal/ModalContent.tsx +60 -0
  18. package/src/lib/Modal/NoticeModal.jsx +91 -0
  19. package/src/lib/Modal/index.jsx +1 -0
  20. package/src/lib/Spin/index.tsx +20 -0
  21. package/src/lib/Switch/index.stories.tsx +24 -0
  22. package/src/lib/UploadImage/BaseTemplate.tsx +73 -0
  23. package/src/lib/UploadImage/CustomUpload.stories.tsx +52 -0
  24. package/src/lib/UploadImage/CustomUpload.tsx +95 -0
  25. package/src/lib/hello-server.tsx +4 -0
  26. package/src/lib/uiLibrary.spec.tsx +10 -0
  27. package/src/lib/uiLibrary.tsx +14 -0
  28. package/src/lib/utils.ts +6 -0
  29. package/src/server.ts +2 -0
  30. package/storybook-static/249.d700bcb8.iframe.bundle.js +95 -0
  31. package/storybook-static/249.d700bcb8.iframe.bundle.js.LICENSE.txt +25 -0
  32. package/storybook-static/249.d700bcb8.iframe.bundle.js.map +1 -0
  33. package/storybook-static/272.859c45b5.iframe.bundle.js +1 -0
  34. package/storybook-static/297.86b29044.iframe.bundle.js +1 -0
  35. package/storybook-static/301.7b83a51f.iframe.bundle.js +1 -0
  36. package/storybook-static/311.5beb2d3d.iframe.bundle.js +1 -0
  37. package/storybook-static/312.aa18d841.iframe.bundle.js +1 -0
  38. package/storybook-static/501.1fba3663.iframe.bundle.js +1 -0
  39. package/storybook-static/754.9ec23ac4.iframe.bundle.js +1 -0
  40. package/storybook-static/777.4b1d90f9.iframe.bundle.js +1 -0
  41. package/storybook-static/794.2230a3f3.iframe.bundle.js +1 -0
  42. package/storybook-static/852.18487f4c.iframe.bundle.js +402 -0
  43. package/storybook-static/852.18487f4c.iframe.bundle.js.LICENSE.txt +23 -0
  44. package/storybook-static/852.18487f4c.iframe.bundle.js.map +1 -0
  45. package/storybook-static/DropImage-index-stories.befa1b35.iframe.bundle.js +1 -0
  46. package/storybook-static/Switch-index-stories.9cfb2ba1.iframe.bundle.js +1 -0
  47. package/storybook-static/UploadImage-CustomUpload-stories.521444aa.iframe.bundle.js +1 -0
  48. package/storybook-static/favicon.svg +7 -0
  49. package/storybook-static/iframe.html +370 -0
  50. package/storybook-static/index.html +151 -0
  51. package/storybook-static/index.json +1 -0
  52. package/storybook-static/main.6f3c811b.iframe.bundle.js +1 -0
  53. package/storybook-static/project.json +1 -0
  54. package/storybook-static/runtime~main.28408b82.iframe.bundle.js +1 -0
  55. package/storybook-static/sb-addons/essentials-actions-2/manager-bundle.js +3 -0
  56. package/storybook-static/sb-addons/essentials-actions-2/manager-bundle.js.LEGAL.txt +0 -0
  57. package/storybook-static/sb-addons/essentials-backgrounds-3/manager-bundle.js +12 -0
  58. package/storybook-static/sb-addons/essentials-backgrounds-3/manager-bundle.js.LEGAL.txt +0 -0
  59. package/storybook-static/sb-addons/essentials-controls-1/manager-bundle.js +79 -0
  60. package/storybook-static/sb-addons/essentials-controls-1/manager-bundle.js.LEGAL.txt +28 -0
  61. package/storybook-static/sb-addons/essentials-measure-6/manager-bundle.js +3 -0
  62. package/storybook-static/sb-addons/essentials-measure-6/manager-bundle.js.LEGAL.txt +0 -0
  63. package/storybook-static/sb-addons/essentials-outline-7/manager-bundle.js +3 -0
  64. package/storybook-static/sb-addons/essentials-outline-7/manager-bundle.js.LEGAL.txt +0 -0
  65. package/storybook-static/sb-addons/essentials-toolbars-5/manager-bundle.js +3 -0
  66. package/storybook-static/sb-addons/essentials-toolbars-5/manager-bundle.js.LEGAL.txt +0 -0
  67. package/storybook-static/sb-addons/essentials-viewport-4/manager-bundle.js +3 -0
  68. package/storybook-static/sb-addons/essentials-viewport-4/manager-bundle.js.LEGAL.txt +0 -0
  69. package/storybook-static/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js +3 -0
  70. package/storybook-static/sb-addons/storybook-core-server-presets-0/common-manager-bundle.js.LEGAL.txt +0 -0
  71. package/storybook-static/sb-common-assets/fonts.css +31 -0
  72. package/storybook-static/sb-common-assets/nunito-sans-bold-italic.woff2 +0 -0
  73. package/storybook-static/sb-common-assets/nunito-sans-bold.woff2 +0 -0
  74. package/storybook-static/sb-common-assets/nunito-sans-italic.woff2 +0 -0
  75. package/storybook-static/sb-common-assets/nunito-sans-regular.woff2 +0 -0
  76. package/storybook-static/sb-manager/WithTooltip-Y7J54OF7-KHQOWZXS.js +1 -0
  77. package/storybook-static/sb-manager/chunk-3F3RRPB3.js +347 -0
  78. package/storybook-static/sb-manager/chunk-62YMTM65.js +6 -0
  79. package/storybook-static/sb-manager/chunk-AQBE2B7B.js +183 -0
  80. package/storybook-static/sb-manager/chunk-Q3RBXCN3.js +231 -0
  81. package/storybook-static/sb-manager/chunk-XP3HGWTR.js +1 -0
  82. package/storybook-static/sb-manager/chunk-YME6VNXZ.js +9 -0
  83. package/storybook-static/sb-manager/formatter-B5HCVTEV-7DCBOGO6.js +58 -0
  84. package/storybook-static/sb-manager/globals-module-info.js +1 -0
  85. package/storybook-static/sb-manager/globals-runtime.js +1 -0
  86. package/storybook-static/sb-manager/globals.js +1 -0
  87. package/storybook-static/sb-manager/index.js +1 -0
  88. package/storybook-static/sb-manager/runtime.js +1 -0
  89. package/storybook-static/sb-manager/syntaxhighlighter-JOJW2KGS-WAFIMSO6.js +1 -0
  90. package/storybook-static/sb-preview/globals.js +1 -0
  91. package/storybook-static/sb-preview/runtime.js +128 -0
  92. package/tailwind.config.js +76 -0
  93. package/tsconfig.json +25 -0
  94. package/tsconfig.lib.json +29 -0
  95. package/tsconfig.storybook.json +31 -0
  96. package/index.esm.d.ts +0 -1
  97. package/index.esm.js +0 -5755
  98. package/src/index.d.ts +0 -4
  99. package/src/lib/DropImage/index.d.ts +0 -8
  100. package/src/lib/Modal/ErrorContent.d.ts +0 -7
  101. package/src/lib/Modal/Modal.d.ts +0 -7
  102. package/src/lib/Modal/ModalContent.d.ts +0 -10
  103. package/src/lib/Spin/index.d.ts +0 -5
  104. package/src/lib/UploadImage/BaseTemplate.d.ts +0 -7
  105. package/src/lib/UploadImage/CustomUpload.d.ts +0 -10
  106. package/src/lib/hello-server.d.ts +0 -1
  107. package/src/lib/uiLibrary.d.ts +0 -4
  108. package/src/server.d.ts +0 -1
  109. /package/{index.esm.css → src/lib/uiLibrary.module.css} +0 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "extends": ["plugin:@nx/react", "../.eslintrc.json"],
3
+ "ignorePatterns": ["!**/*", "storybook-static"],
4
+ "overrides": [
5
+ {
6
+ "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7
+ "rules": {}
8
+ },
9
+ {
10
+ "files": ["*.ts", "*.tsx"],
11
+ "rules": {}
12
+ },
13
+ {
14
+ "files": ["*.js", "*.jsx"],
15
+ "rules": {}
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,14 @@
1
+ const config = {
2
+ stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
3
+ addons: ['@storybook/addon-essentials'],
4
+ framework: {
5
+ name: '@storybook/nextjs',
6
+ options: {},
7
+ },
8
+ };
9
+
10
+ export default config;
11
+
12
+ // To customize your webpack configuration you can use the webpackFinal field.
13
+ // Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
14
+ // and https://nx.dev/recipes/storybook/custom-builder-configs
@@ -0,0 +1 @@
1
+ import './tailwind-imports.css';
@@ -0,0 +1,81 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 0 0% 100%;
8
+ --foreground: 222.2 47.4% 11.2%;
9
+
10
+ --muted: 210 40% 96.1%;
11
+ --muted-foreground: 215.4 16.3% 46.9%;
12
+
13
+ --popover: 0 0% 100%;
14
+ --popover-foreground: 222.2 47.4% 11.2%;
15
+
16
+ --border: 214.3 31.8% 91.4%;
17
+ --input: 214.3 31.8% 91.4%;
18
+
19
+ --card: 0 0% 100%;
20
+ --card-foreground: 222.2 47.4% 11.2%;
21
+
22
+ --primary: 222.2 47.4% 11.2%;
23
+ --primary-foreground: 210 40% 98%;
24
+
25
+ --secondary: 210 40% 96.1%;
26
+ --secondary-foreground: 222.2 47.4% 11.2%;
27
+
28
+ --accent: 210 40% 96.1%;
29
+ --accent-foreground: 222.2 47.4% 11.2%;
30
+
31
+ --destructive: 0 100% 50%;
32
+ --destructive-foreground: 210 40% 98%;
33
+
34
+ --ring: 215 20.2% 65.1%;
35
+
36
+ --radius: 0.5rem;
37
+ }
38
+
39
+ .dark {
40
+ --background: 224 71% 4%;
41
+ --foreground: 213 31% 91%;
42
+
43
+ --muted: 223 47% 11%;
44
+ --muted-foreground: 215.4 16.3% 56.9%;
45
+
46
+ --accent: 216 34% 17%;
47
+ --accent-foreground: 210 40% 98%;
48
+
49
+ --popover: 224 71% 4%;
50
+ --popover-foreground: 215 20.2% 65.1%;
51
+
52
+ --border: 216 34% 17%;
53
+ --input: 216 34% 17%;
54
+
55
+ --card: 224 71% 4%;
56
+ --card-foreground: 213 31% 91%;
57
+
58
+ --primary: 210 40% 98%;
59
+ --primary-foreground: 222.2 47.4% 1.2%;
60
+
61
+ --secondary: 222.2 47.4% 11.2%;
62
+ --secondary-foreground: 210 40% 98%;
63
+
64
+ --destructive: 0 63% 31%;
65
+ --destructive-foreground: 210 40% 98%;
66
+
67
+ --ring: 216 34% 17%;
68
+
69
+ --radius: 0.5rem;
70
+ }
71
+ }
72
+
73
+ @layer base {
74
+ * {
75
+ @apply border-border;
76
+ }
77
+ body {
78
+ @apply bg-background text-foreground;
79
+ font-feature-settings: 'rlig' 1, 'calt' 1;
80
+ }
81
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": false,
5
+ "tailwind": {
6
+ "config": "apps/**/tailwind.config.js",
7
+ "css": "apps/**/app/global.css",
8
+ "baseColor": "stone",
9
+ "cssVariables": true
10
+ },
11
+ "aliases": {
12
+ "components": "@sunggang/uiLibrary/components",
13
+ "utils": "@sunggang/uiLibrary/lib/utils"
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@sunggang/ui-lib",
3
- "version": "0.0.3",
4
- "module": "./index.esm.js",
5
- "type": "module",
6
- "main": "./index.esm.js"
3
+ "version": "0.0.5",
4
+ "dependencies": {
5
+ "@radix-ui/react-switch": "^1.0.3",
6
+ "class-variance-authority": "^0.7.0",
7
+ "clsx": "^2.1.0",
8
+ "tailwind-merge": "^2.2.2",
9
+ "tailwindcss-animate": "^1.0.7"
10
+ }
7
11
  }
@@ -0,0 +1,10 @@
1
+ const { join } = require('path');
2
+
3
+ module.exports = {
4
+ plugins: {
5
+ tailwindcss: {
6
+ config: join(__dirname, 'tailwind.config.js'),
7
+ },
8
+ autoprefixer: {},
9
+ },
10
+ };
package/project.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "uiLibrary",
3
+ "$schema": "../node_modules/nx/schemas/project-schema.json",
4
+ "sourceRoot": "uiLibrary/src",
5
+ "projectType": "library",
6
+ "tags": [],
7
+ "targets": {
8
+ "build": {
9
+ "executor": "@nx/rollup:rollup",
10
+ "outputs": ["{options.outputPath}"],
11
+ "options": {
12
+ "outputPath": "dist/uiLibrary",
13
+ "styles": ["apps/ui-lib/styles.css"],
14
+ "postcssConfig": "apps/ui-lib/postcss.config.js",
15
+ "tsConfig": "uiLibrary/tsconfig.lib.json",
16
+ "project": "uiLibrary/package.json",
17
+ "entryFile": "uiLibrary/src/index.ts",
18
+ "external": ["react", "react-dom", "react/jsx-runtime"],
19
+ "rollupConfig": "@nx/react/plugins/bundle-rollup",
20
+ "compiler": "swc",
21
+ "assets": [
22
+ {
23
+ "glob": "uiLibrary/README.md",
24
+ "input": ".",
25
+ "output": "."
26
+ }
27
+ ]
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,27 @@
1
+ import * as React from "react"
2
+ import * as SwitchPrimitives from "@radix-ui/react-switch"
3
+
4
+ import { cn } from "@sunggang/uiLibrary/lib/utils"
5
+
6
+ const Switch = React.forwardRef<
7
+ React.ElementRef<typeof SwitchPrimitives.Root>,
8
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
9
+ >(({ className, ...props }, ref) => (
10
+ <SwitchPrimitives.Root
11
+ className={cn(
12
+ "peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
13
+ className
14
+ )}
15
+ {...props}
16
+ ref={ref}
17
+ >
18
+ <SwitchPrimitives.Thumb
19
+ className={cn(
20
+ "pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0"
21
+ )}
22
+ />
23
+ </SwitchPrimitives.Root>
24
+ ))
25
+ Switch.displayName = SwitchPrimitives.Root.displayName
26
+
27
+ export { Switch }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ // Use this file to export React client components (e.g. those with 'use client' directive) or other non-server utilities
2
+
3
+ export * from './lib/uiLibrary';
4
+ export * from './lib/Spin';
5
+ export * from './lib/DropImage';
6
+ export * from './lib/UploadImage/CustomUpload';
7
+ export * from './components/ui/switch';
@@ -0,0 +1,42 @@
1
+ import type { Meta, StoryFn } from '@storybook/react';
2
+ import { DropImage } from './index';
3
+
4
+ interface ImageItem {
5
+ map?: any;
6
+ length?: ImageItem | undefined;
7
+ url: string;
8
+ book_items_category_id: string | null;
9
+ }
10
+
11
+ interface DropImageProps {
12
+ preview: boolean;
13
+ imageUrls?: ImageItem[];
14
+ setFiles: React.Dispatch<React.SetStateAction<File[]>>;
15
+ }
16
+
17
+ export default {
18
+ component: DropImage,
19
+ title: 'DropImage',
20
+ } as Meta;
21
+
22
+ const demoImage: ImageItem[] = [
23
+ {
24
+ url: 'https://gobobook.s3.ap-northeast-1.amazonaws.com/regions/DEV/region1.png',
25
+ book_items_category_id: null,
26
+ },
27
+ {
28
+ url: 'https://gobobook.s3.ap-northeast-1.amazonaws.com/regions/DEV/region2.png',
29
+ book_items_category_id: null,
30
+ },
31
+ ];
32
+
33
+ const Template: StoryFn<DropImageProps> = (args) => {
34
+ return <DropImage {...args} />;
35
+ };
36
+
37
+ export const Base = Template.bind({});
38
+ Base.args = {
39
+ preview: true,
40
+ setFiles: () => ({}),
41
+ imageUrls: demoImage,
42
+ };
@@ -0,0 +1,110 @@
1
+ import React, { useState } from 'react';
2
+ import { useDropzone } from 'react-dropzone';
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { Icon } from '@iconify/react';
5
+
6
+ interface ImageItem {
7
+ map?: any;
8
+ length?: ImageItem | undefined;
9
+ url: string;
10
+ book_items_category_id: string | null;
11
+ }
12
+
13
+ interface DropImageProps {
14
+ preview?: boolean;
15
+ imageUrls?: ImageItem[];
16
+ setFiles: React.Dispatch<React.SetStateAction<File[]>>;
17
+ }
18
+
19
+ export const DropImage: React.FC<DropImageProps> = ({
20
+ preview = true,
21
+ setFiles,
22
+ imageUrls,
23
+ }) => {
24
+ const [previewFiles, setPreviewFiles] = useState<File[]>([]);
25
+
26
+ const {
27
+ getRootProps,
28
+ getInputProps,
29
+ isDragActive,
30
+ isDragAccept,
31
+ isDragReject,
32
+ } = useDropzone({
33
+ accept: { 'image/png': ['.png'], 'image/jpeg': ['.jpg', '.jpeg'] },
34
+ maxFiles: 10,
35
+ maxSize: 5000000,
36
+ onDrop: (dropFiles) => {
37
+ const newFiles: File[] = [];
38
+
39
+ setFiles(dropFiles);
40
+
41
+ dropFiles.forEach((dropFile) => {
42
+ Object.assign(dropFile, {
43
+ preview: URL.createObjectURL(dropFile),
44
+ uuid: uuidv4(),
45
+ });
46
+ newFiles.push(dropFile);
47
+
48
+ setPreviewFiles(newFiles);
49
+ });
50
+ },
51
+ });
52
+
53
+ return (
54
+ <section className="w-full">
55
+ <div {...getRootProps({ className: 'dropzone' })}>
56
+ <input {...getInputProps()} />
57
+ <div className="flex justify-between">
58
+ <div className="w-full bg-gray-light rounded-lg">
59
+ <div className="border-2 border-dashed border-gray-400 rounded-lg bg-gray-100">
60
+ <div className="flex justify-center py-8">
61
+ <Icon
62
+ icon="material-symbols:upload"
63
+ width="2.5rem"
64
+ height="2.5rem"
65
+ />
66
+ </div>
67
+
68
+ <div className="flex items-center flex-col justify-center">
69
+ <p className="font-normal text-sm text-gray-400 pb-4">
70
+ 圖片僅支援 png、jpg、jpeg
71
+ </p>
72
+
73
+ <h5 className="mb-2 text-xl font-bold tracking-tight text-gray-700 pb-4">
74
+ 拖曳圖片至此
75
+ </h5>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ {preview && (
82
+ <div className="flex pt-2 gap-2">
83
+ {previewFiles?.length
84
+ ? previewFiles.length > 0
85
+ ? previewFiles.map((item: any) => (
86
+ <div key={item?.uuid}>
87
+ <img
88
+ className="w-24 h-24 object-cover"
89
+ src={item?.preview}
90
+ alt=""
91
+ />
92
+ </div>
93
+ ))
94
+ : null
95
+ : imageUrls && imageUrls.length
96
+ ? imageUrls.map((item) => (
97
+ <div key={item?.url}>
98
+ <img
99
+ className="w-24 h-24 object-cover"
100
+ src={item?.url}
101
+ alt=""
102
+ />
103
+ </div>
104
+ ))
105
+ : null}
106
+ </div>
107
+ )}
108
+ </section>
109
+ );
110
+ };
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Icon } from '@iconify/react';
4
+
5
+ const BaseModal = ({
6
+ isOpen = false,
7
+ content,
8
+ height,
9
+ className,
10
+ onClose,
11
+ showCloseIcon = false,
12
+ fixCloseBtn = false,
13
+ }) =>
14
+ isOpen && (
15
+ <div className="fixed top-0 left-0 h-full z-[1000] p-4 duration-300 transition-opacity bg-[#000000b3] w-full">
16
+ <div className="w-full h-full flex justify-center items-center relative">
17
+ {showCloseIcon && (
18
+ <div className="text-right p-2 absolute right-0 top-0">
19
+ <Icon
20
+ className="ml-auto cursor-pointer text-white"
21
+ icon="ion:close"
22
+ width="24"
23
+ height="24"
24
+ onClick={onClose}
25
+ />
26
+ </div>
27
+ )}
28
+ <div
29
+ className={[
30
+ 'bg-white rounded-xl overflow-y-auto m-auto',
31
+ fixCloseBtn ? '' : 'max-w-[932px] max-h-[90vh]',
32
+ height,
33
+ className,
34
+ ].join(' ')}
35
+ >
36
+ {content}
37
+ </div>
38
+ </div>
39
+ </div>
40
+ );
41
+
42
+ BaseModal.propTypes = {
43
+ isOpen: PropTypes.bool,
44
+ content: PropTypes.element,
45
+ height: PropTypes.string,
46
+ className: PropTypes.string,
47
+ onClose: PropTypes.func,
48
+ showCloseIcon: PropTypes.bool,
49
+ fixCloseBtn: PropTypes.bool,
50
+ };
51
+
52
+ export default BaseModal;
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { Icon } from '@iconify/react';
3
+ import ModalContent from './ModalContent';
4
+
5
+ interface ErrorContentProps {
6
+ setModal: any;
7
+ errmsg: React.ReactNode | string;
8
+ }
9
+
10
+ const ErrorContent: React.FC<ErrorContentProps> = ({ setModal, errmsg }) => (
11
+ <ModalContent setModal={setModal} onClick={setModal} notice>
12
+ <div className="w-full max-w-xl">
13
+ <div className="flex justify-center items-center mb-6">
14
+ <Icon icon="fluent-mdl2:status-error-full" color="red" height={60} />
15
+ </div>
16
+ <div className="text-center text-lg text-gray-500">{errmsg}</div>
17
+ </div>
18
+ </ModalContent>
19
+ );
20
+
21
+ export default ErrorContent;
@@ -0,0 +1,39 @@
1
+ import { useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ const FullScreenModal = ({ overHidden, isOpen, children }) => {
5
+ useEffect(() => {
6
+ if (overHidden) return;
7
+ // 在 Modal 打開時,禁止滾動
8
+ if (isOpen) {
9
+ document.body.style.overflow = 'hidden';
10
+ } else {
11
+ // 在 Modal 關閉時,啟用滾動
12
+ document.body.style.overflow = 'visible';
13
+ }
14
+
15
+ // 在組件卸載時清除樣式
16
+ // eslint-disable-next-line consistent-return
17
+ return () => {
18
+ document.body.style.overflow = 'visible';
19
+ };
20
+ }, [isOpen]);
21
+
22
+ return (
23
+ <div
24
+ className={`fixed top-0 left-0 w-full h-full overflow-hidden flex items-center justify-center z-[9999] bg-black bg-opacity-10 ${
25
+ isOpen ? 'block' : 'hidden'
26
+ }`}
27
+ >
28
+ <div className="modal-content bg-white p-[30px] rounded-lg relative">{children}</div>
29
+ </div>
30
+ );
31
+ };
32
+
33
+ FullScreenModal.propTypes = {
34
+ isOpen: PropTypes.bool.isRequired,
35
+ children: PropTypes.node.isRequired,
36
+ overHidden: PropTypes.bool,
37
+ };
38
+
39
+ export default FullScreenModal;
@@ -0,0 +1,36 @@
1
+ import React, { useEffect, KeyboardEvent } from 'react';
2
+
3
+ interface ModalProps {
4
+ modal: React.ReactElement;
5
+ unsetModal?: () => void;
6
+ }
7
+
8
+ const Modal: React.FC<ModalProps> = ({ modal, unsetModal = () => {} }) => {
9
+ const handleKeyUp = (e: any) => {
10
+ if (
11
+ e.key === 'Escape' &&
12
+ !['INPUT', 'SELECT'].includes(
13
+ (document.activeElement as HTMLElement)?.tagName
14
+ )
15
+ ) {
16
+ unsetModal();
17
+ }
18
+ };
19
+
20
+ useEffect(() => {
21
+ document.addEventListener('keyup', handleKeyUp);
22
+ return () => document.removeEventListener('keyup', handleKeyUp);
23
+ }, [unsetModal]);
24
+
25
+ return (
26
+ <div className="fixed top-0 left-0 h-screen z-[2000] transition-opacity duration-300 transition-opacity bg-[#000000b3] w-full">
27
+ <div className="w-full h-full flex justify-center items-center p-6">
28
+ <div className="bg-white rounded overflow-y-auto w-full m-auto max-w-[455px] rounded-xl">
29
+ {modal}
30
+ </div>
31
+ </div>
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export default Modal;
@@ -0,0 +1,60 @@
1
+ import React from 'react';
2
+
3
+ interface ModalContentProps {
4
+ title?: string;
5
+ setModal?: any;
6
+ children: React.ReactNode;
7
+ onClick?: any;
8
+ notice?: boolean;
9
+ }
10
+
11
+ const ModalContent: React.FC<ModalContentProps> = ({
12
+ title,
13
+ setModal,
14
+ children,
15
+ onClick,
16
+ notice = false,
17
+ }) => (
18
+ <form>
19
+ {title && (
20
+ <div className="text-2xl text-gray-700 font-medium border-solid border-b border-gray-300 mb-4 p-4">
21
+ {title}
22
+ </div>
23
+ )}
24
+
25
+ {children && <div className="py-4 px-8">{children}</div>}
26
+
27
+ <div className="flex items-center text-base p-4 px-8 justify-around">
28
+ {notice ? (
29
+ <button
30
+ type="button"
31
+ className="w-24 h-10 px-2 text-white bg-gray-600 rounded"
32
+ onClick={setModal}
33
+ >
34
+ 確定
35
+ </button>
36
+ ) : (
37
+ <>
38
+ <button
39
+ type="button"
40
+ className="w-24 h-10 px-2 text-white bg-gray-600 rounded"
41
+ onClick={setModal}
42
+ >
43
+ 取消
44
+ </button>
45
+ {onClick && (
46
+ <button
47
+ type="button"
48
+ className="w-24 h-10 mx-3 text-white bg-gray-500 rounded"
49
+ onClick={onClick}
50
+ >
51
+ 送出
52
+ </button>
53
+ )}
54
+ </>
55
+ )}
56
+ </div>
57
+ </form>
58
+ );
59
+
60
+ export default ModalContent;
@@ -0,0 +1,91 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Icon } from '@iconify/react';
4
+
5
+ const NoticeModal = ({
6
+ title,
7
+ buttonName,
8
+ content,
9
+ setModal,
10
+ onClick,
11
+ onlyCheck,
12
+ action,
13
+ cancelButtonName = '取消',
14
+ cancelFunc,
15
+ }) => {
16
+ const statusCode = action?.statusCode;
17
+
18
+ const actionFunc = () => {
19
+ if (statusCode === 'book_dates') return action?.resetCalendar();
20
+ if (statusCode === 'book_expired') return action?.redirectBook();
21
+ if (statusCode === 'not_registered_2times') return action?.redirectToPay();
22
+ return null;
23
+ };
24
+
25
+ const actionName =
26
+ statusCode === 'book_dates' ? action?.resetCalendarName : action?.redirectCalendarName;
27
+
28
+ const onlyCheckList = ['points', 'tickets', 'promo_tickets', 'member_group'];
29
+
30
+ const onlyCheckAction = onlyCheck || onlyCheckList.includes(statusCode);
31
+
32
+ return (
33
+ <div className="px-8 py-9">
34
+ <button className="w-full flex justify-end" type="button" onClick={() => setModal()}>
35
+ <Icon className="text-[#000000]" icon="ic:baseline-close" width="24" height="24" />
36
+ </button>
37
+ <div className="text-[28px] text-gray-700 font-medium min-w-[16rem] pb-2">{title}</div>
38
+
39
+ <div className="text-[20px] text-[#5A5A5A] pb-8">
40
+ <div>{content}</div>
41
+ </div>
42
+ <div className="flex items-center text-lg text-base justify-center gap-8">
43
+ {onlyCheckAction ? (
44
+ <button
45
+ type="button"
46
+ className={['px-8 py-3 px-2 text-white bg-[#0C7489] rounded-[10px]'].join(' ')}
47
+ onClick={() => {
48
+ setModal();
49
+ }}
50
+ >
51
+ 確認
52
+ </button>
53
+ ) : (
54
+ <>
55
+ <button
56
+ type="button"
57
+ className={[
58
+ 'px-6 lg:px-8 py-3 rounded-[10px]',
59
+ 'border border-solid border-[#656565] text-[#5A5A5A] bg-[#F5F5F5]',
60
+ ].join(' ')}
61
+ onClick={() => (cancelFunc ? cancelFunc() : setModal())}
62
+ >
63
+ {cancelButtonName}
64
+ </button>
65
+ <button
66
+ type="button"
67
+ className="px-6 lg:px-8 py-3 text-white bg-[#0C7489] rounded-[10px]"
68
+ onClick={statusCode ? actionFunc : onClick}
69
+ >
70
+ {actionName || buttonName}
71
+ </button>
72
+ </>
73
+ )}
74
+ </div>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ NoticeModal.propTypes = {
80
+ setModal: PropTypes.func,
81
+ title: PropTypes.string,
82
+ buttonName: PropTypes.string,
83
+ content: PropTypes.node,
84
+ onClick: PropTypes.func,
85
+ onlyCheck: PropTypes.bool,
86
+ action: PropTypes.shape(),
87
+ cancelButtonName: PropTypes.string,
88
+ cancelFunc: PropTypes.func,
89
+ };
90
+
91
+ export default NoticeModal;