@mirrormedia/lilith-draft-editor 1.0.0-beta → 1.0.0-beta4

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 (171) hide show
  1. package/lib/draft-js/block-renderer/background-image-block.js +123 -0
  2. package/lib/draft-js/block-renderer/background-video-block.js +133 -0
  3. package/lib/draft-js/block-renderer/color-box-block.js +92 -0
  4. package/lib/draft-js/block-renderer/divider-block.js +24 -0
  5. package/lib/draft-js/block-renderer/embedded-code-block.js +63 -0
  6. package/lib/draft-js/block-renderer/image-block.js +47 -0
  7. package/lib/draft-js/block-renderer/info-box-block.js +89 -0
  8. package/lib/draft-js/block-renderer/media-block.js +65 -0
  9. package/lib/draft-js/block-renderer/related-post-block.js +51 -0
  10. package/lib/draft-js/block-renderer/side-index-block.js +103 -0
  11. package/lib/draft-js/block-renderer/slideshow-block.js +71 -0
  12. package/lib/draft-js/block-renderer/table-block.js +408 -0
  13. package/lib/draft-js/buttons/annotation.js +117 -0
  14. package/lib/draft-js/buttons/background-color.js +123 -0
  15. package/lib/draft-js/buttons/background-image.js +222 -0
  16. package/lib/draft-js/buttons/background-video.js +222 -0
  17. package/lib/draft-js/buttons/color-box.js +172 -0
  18. package/lib/draft-js/buttons/divider.js +63 -0
  19. package/lib/draft-js/buttons/embedded-code.js +109 -0
  20. package/lib/draft-js/buttons/enlarge.js +24 -0
  21. package/lib/draft-js/buttons/font-color.js +116 -0
  22. package/lib/draft-js/buttons/image.js +68 -0
  23. package/lib/draft-js/buttons/info-box.js +147 -0
  24. package/lib/draft-js/buttons/link.js +107 -0
  25. package/lib/draft-js/buttons/media.js +121 -0
  26. package/lib/draft-js/buttons/related-post.js +71 -0
  27. package/lib/draft-js/buttons/selector/align-selector.js +71 -0
  28. package/lib/draft-js/buttons/selector/image-selector.js +428 -0
  29. package/lib/draft-js/buttons/selector/pagination.js +82 -0
  30. package/lib/draft-js/buttons/selector/post-selector.js +317 -0
  31. package/lib/draft-js/buttons/selector/search-box.js +46 -0
  32. package/lib/draft-js/buttons/selector/video-selector.js +284 -0
  33. package/lib/draft-js/buttons/side-index.js +200 -0
  34. package/lib/draft-js/buttons/slideshow.js +71 -0
  35. package/lib/draft-js/buttons/table.js +67 -0
  36. package/lib/draft-js/buttons/text-align.js +88 -0
  37. package/lib/draft-js/editor/basic-editor.js +366 -0
  38. package/lib/draft-js/editor/block-renderer-fn.js +117 -0
  39. package/lib/draft-js/editor/entity-decorator.js +16 -0
  40. package/lib/draft-js/editor/modifier.js +68 -0
  41. package/lib/draft-js/entity-decorator/annotation-decorator.js +86 -0
  42. package/lib/draft-js/entity-decorator/link-decorator.js +39 -0
  43. package/lib/website/mirrormedia/custom/block-renderer/background-image-block.js +91 -0
  44. package/lib/website/mirrormedia/custom/block-renderer/background-video-block.js +91 -0
  45. package/lib/website/mirrormedia/custom/block-renderer/color-box-block.js +84 -0
  46. package/lib/website/mirrormedia/custom/block-renderer/info-box-block.js +84 -0
  47. package/lib/website/mirrormedia/custom/block-renderer/side-index-block.js +90 -0
  48. package/lib/website/mirrormedia/custom/block-renderer/table-block.js +408 -0
  49. package/lib/website/mirrormedia/custom/selector/align-selector.js +71 -0
  50. package/lib/website/mirrormedia/custom/selector/image-selector.js +427 -0
  51. package/lib/website/mirrormedia/custom/selector/pagination.js +82 -0
  52. package/lib/website/mirrormedia/custom/selector/post-selector.js +318 -0
  53. package/lib/website/mirrormedia/custom/selector/search-box.js +46 -0
  54. package/lib/website/mirrormedia/custom/selector/video-selector.js +282 -0
  55. package/lib/website/mirrormedia/draft-editor/block-renderer-fn.js +117 -0
  56. package/lib/website/mirrormedia/draft-editor/entity-decorator.js +18 -0
  57. package/lib/website/mirrormedia/draft-editor/index.js +799 -0
  58. package/lib/website/mirrormedia/index.js +1 -4
  59. package/lib/website/readr/custom/block-renderer/background-image-block.js +91 -0
  60. package/lib/website/readr/custom/block-renderer/background-video-block.js +91 -0
  61. package/lib/website/readr/custom/block-renderer/color-box-block.js +84 -0
  62. package/lib/website/readr/custom/block-renderer/info-box-block.js +84 -0
  63. package/lib/website/readr/custom/block-renderer/side-index-block.js +90 -0
  64. package/lib/website/readr/custom/block-renderer/table-block.js +408 -0
  65. package/lib/website/readr/custom/selector/align-selector.js +71 -0
  66. package/lib/website/readr/custom/selector/image-selector.js +427 -0
  67. package/lib/website/readr/custom/selector/pagination.js +82 -0
  68. package/lib/website/readr/custom/selector/post-selector.js +318 -0
  69. package/lib/website/readr/custom/selector/search-box.js +46 -0
  70. package/lib/website/readr/custom/selector/video-selector.js +282 -0
  71. package/lib/website/readr/draft-editor/block-renderer-fn.js +117 -0
  72. package/lib/website/readr/draft-editor/entity-decorator.js +18 -0
  73. package/lib/website/readr/draft-editor/index.js +799 -0
  74. package/lib/website/readr/index.js +1 -4
  75. package/package.json +3 -2
  76. package/lib/draft-js/block-renderer/background-image-block.tsx +0 -113
  77. package/lib/draft-js/block-renderer/background-video-block.tsx +0 -120
  78. package/lib/draft-js/block-renderer/color-box-block.tsx +0 -85
  79. package/lib/draft-js/block-renderer/divider-block.tsx +0 -12
  80. package/lib/draft-js/block-renderer/embedded-code-block.tsx +0 -65
  81. package/lib/draft-js/block-renderer/image-block.tsx +0 -41
  82. package/lib/draft-js/block-renderer/info-box-block.tsx +0 -85
  83. package/lib/draft-js/block-renderer/media-block.tsx +0 -36
  84. package/lib/draft-js/block-renderer/related-post-block.tsx +0 -47
  85. package/lib/draft-js/block-renderer/side-index-block.tsx +0 -113
  86. package/lib/draft-js/block-renderer/slideshow-block.tsx +0 -62
  87. package/lib/draft-js/block-renderer/table-block.tsx +0 -488
  88. package/lib/draft-js/buttons/annotation.tsx +0 -113
  89. package/lib/draft-js/buttons/background-color.tsx +0 -125
  90. package/lib/draft-js/buttons/background-image.tsx +0 -276
  91. package/lib/draft-js/buttons/background-video.tsx +0 -275
  92. package/lib/draft-js/buttons/color-box.tsx +0 -207
  93. package/lib/draft-js/buttons/divider.tsx +0 -56
  94. package/lib/draft-js/buttons/embedded-code.tsx +0 -126
  95. package/lib/draft-js/buttons/enlarge.tsx +0 -11
  96. package/lib/draft-js/buttons/font-color.tsx +0 -113
  97. package/lib/draft-js/buttons/image.tsx +0 -71
  98. package/lib/draft-js/buttons/info-box.tsx +0 -170
  99. package/lib/draft-js/buttons/link.tsx +0 -103
  100. package/lib/draft-js/buttons/media.tsx +0 -120
  101. package/lib/draft-js/buttons/related-post.tsx +0 -81
  102. package/lib/draft-js/buttons/selector/align-selector.tsx +0 -65
  103. package/lib/draft-js/buttons/selector/image-selector.tsx +0 -485
  104. package/lib/draft-js/buttons/selector/pagination.tsx +0 -83
  105. package/lib/draft-js/buttons/selector/post-selector.tsx +0 -367
  106. package/lib/draft-js/buttons/selector/search-box.tsx +0 -39
  107. package/lib/draft-js/buttons/selector/video-selector.tsx +0 -312
  108. package/lib/draft-js/buttons/side-index.tsx +0 -257
  109. package/lib/draft-js/buttons/slideshow.tsx +0 -81
  110. package/lib/draft-js/buttons/table.tsx +0 -63
  111. package/lib/draft-js/buttons/text-align.tsx +0 -88
  112. package/lib/draft-js/editor/basic-editor.tsx +0 -384
  113. package/lib/draft-js/editor/block-redender-fn.tsx +0 -77
  114. package/lib/draft-js/editor/entity-decorator.tsx +0 -7
  115. package/lib/draft-js/editor/modifier.tsx +0 -71
  116. package/lib/draft-js/entity-decorator/annotation-decorator.tsx +0 -81
  117. package/lib/draft-js/entity-decorator/link-decorator.tsx +0 -27
  118. package/lib/website/mirrormedia/custom/block-renderer/background-image-block.tsx +0 -128
  119. package/lib/website/mirrormedia/custom/block-renderer/background-video-block.tsx +0 -135
  120. package/lib/website/mirrormedia/custom/block-renderer/color-box-block.tsx +0 -98
  121. package/lib/website/mirrormedia/custom/block-renderer/divider-block.tsx +0 -12
  122. package/lib/website/mirrormedia/custom/block-renderer/embedded-code-block.tsx +0 -65
  123. package/lib/website/mirrormedia/custom/block-renderer/image-block.tsx +0 -41
  124. package/lib/website/mirrormedia/custom/block-renderer/info-box-block.tsx +0 -98
  125. package/lib/website/mirrormedia/custom/block-renderer/media-block.tsx +0 -36
  126. package/lib/website/mirrormedia/custom/block-renderer/related-post-block.tsx +0 -47
  127. package/lib/website/mirrormedia/custom/block-renderer/side-index-block.tsx +0 -125
  128. package/lib/website/mirrormedia/custom/block-renderer/slideshow-block.tsx +0 -62
  129. package/lib/website/mirrormedia/custom/block-renderer/table-block.tsx +0 -537
  130. package/lib/website/mirrormedia/custom/entity-decorator/annotation-decorator.tsx +0 -81
  131. package/lib/website/mirrormedia/custom/entity-decorator/link-decorator.tsx +0 -27
  132. package/lib/website/mirrormedia/custom/selector/align-selector.tsx +0 -65
  133. package/lib/website/mirrormedia/custom/selector/image-selector.tsx +0 -485
  134. package/lib/website/mirrormedia/custom/selector/pagination.tsx +0 -83
  135. package/lib/website/mirrormedia/custom/selector/post-selector.tsx +0 -367
  136. package/lib/website/mirrormedia/custom/selector/search-box.tsx +0 -39
  137. package/lib/website/mirrormedia/custom/selector/video-selector.tsx +0 -310
  138. package/lib/website/mirrormedia/draft-editor/block-redender-fn.tsx +0 -77
  139. package/lib/website/mirrormedia/draft-editor/entity-decorator.tsx +0 -7
  140. package/lib/website/mirrormedia/draft-editor/index.tsx +0 -909
  141. package/lib/website/mirrormedia/draft-renderer/block-redender-fn.tsx +0 -77
  142. package/lib/website/mirrormedia/draft-renderer/entity-decorator.tsx +0 -7
  143. package/lib/website/mirrormedia/draft-renderer/index-deprecated.tsx +0 -43
  144. package/lib/website/mirrormedia/draft-renderer/index.tsx +0 -150
  145. package/lib/website/readr/custom/block-renderer/background-image-block.tsx +0 -128
  146. package/lib/website/readr/custom/block-renderer/background-video-block.tsx +0 -135
  147. package/lib/website/readr/custom/block-renderer/color-box-block.tsx +0 -98
  148. package/lib/website/readr/custom/block-renderer/divider-block.tsx +0 -12
  149. package/lib/website/readr/custom/block-renderer/embedded-code-block.tsx +0 -65
  150. package/lib/website/readr/custom/block-renderer/image-block.tsx +0 -41
  151. package/lib/website/readr/custom/block-renderer/info-box-block.tsx +0 -98
  152. package/lib/website/readr/custom/block-renderer/media-block.tsx +0 -36
  153. package/lib/website/readr/custom/block-renderer/related-post-block.tsx +0 -47
  154. package/lib/website/readr/custom/block-renderer/side-index-block.tsx +0 -125
  155. package/lib/website/readr/custom/block-renderer/slideshow-block.tsx +0 -62
  156. package/lib/website/readr/custom/block-renderer/table-block.tsx +0 -537
  157. package/lib/website/readr/custom/entity-decorator/annotation-decorator.tsx +0 -81
  158. package/lib/website/readr/custom/entity-decorator/link-decorator.tsx +0 -27
  159. package/lib/website/readr/custom/selector/align-selector.tsx +0 -65
  160. package/lib/website/readr/custom/selector/image-selector.tsx +0 -485
  161. package/lib/website/readr/custom/selector/pagination.tsx +0 -83
  162. package/lib/website/readr/custom/selector/post-selector.tsx +0 -367
  163. package/lib/website/readr/custom/selector/search-box.tsx +0 -39
  164. package/lib/website/readr/custom/selector/video-selector.tsx +0 -310
  165. package/lib/website/readr/draft-editor/block-redender-fn.tsx +0 -77
  166. package/lib/website/readr/draft-editor/entity-decorator.tsx +0 -7
  167. package/lib/website/readr/draft-editor/index.tsx +0 -909
  168. package/lib/website/readr/draft-renderer/block-redender-fn.tsx +0 -77
  169. package/lib/website/readr/draft-renderer/entity-decorator.tsx +0 -7
  170. package/lib/website/readr/draft-renderer/index-deprecated.tsx +0 -43
  171. package/lib/website/readr/draft-renderer/index.tsx +0 -150
@@ -1,81 +0,0 @@
1
- import React, { useState } from 'react'
2
- import styled from 'styled-components'
3
-
4
- const AnnotatedText = styled.span`
5
- vertical-align: middle;
6
- color: #d0a67d;
7
-
8
- svg {
9
- vertical-align: middle;
10
- margin-left: 5px;
11
- }
12
- `
13
-
14
- const AnnotationBody = styled.div`
15
- background-color: #f1f1f1;
16
- padding: 10px;
17
- margin-top: 5px;
18
- margin-bottom: 10px;
19
- `
20
-
21
- function indicatorSvg(shouldRotate: boolean) {
22
- const transform = shouldRotate ? 'rotate(180 10 10)' : ''
23
- return (
24
- <svg
25
- width="20"
26
- height="20"
27
- viewBox="0 0 20 20"
28
- fill="none"
29
- xmlns="http://www.w3.org/2000/svg"
30
- >
31
- <circle cx="10" cy="10" r="10" fill="#f1f1f1" />
32
- <path
33
- d="M10 15L5.66987 7.5L14.3301 7.5L10 15Z"
34
- fill="#D0A67D"
35
- transform={transform}
36
- />
37
- </svg>
38
- )
39
- }
40
-
41
- function AnnotationBlock(props) {
42
- const { children: annotated } = props
43
- const [toShowAnnotation, setToShowAnnotation] = useState(false)
44
- const { bodyHTML } = props.contentState.getEntity(props.entityKey).getData()
45
- return (
46
- <React.Fragment>
47
- <AnnotatedText>
48
- {annotated}
49
- <span
50
- onClick={(e) => {
51
- e.preventDefault()
52
- setToShowAnnotation(!toShowAnnotation)
53
- }}
54
- >
55
- {toShowAnnotation ? indicatorSvg(false) : indicatorSvg(true)}
56
- </span>
57
- </AnnotatedText>
58
- {toShowAnnotation ? (
59
- <AnnotationBody
60
- contentEditable={false}
61
- dangerouslySetInnerHTML={{ __html: bodyHTML }}
62
- ></AnnotationBody>
63
- ) : null}
64
- </React.Fragment>
65
- )
66
- }
67
-
68
- function findAnnotationEntities(contentBlock, callback, contentState) {
69
- contentBlock.findEntityRanges((character) => {
70
- const entityKey = character.getEntity()
71
- return (
72
- entityKey !== null &&
73
- contentState.getEntity(entityKey).getType() === 'ANNOTATION'
74
- )
75
- }, callback)
76
- }
77
-
78
- export const annotationDecorator = {
79
- strategy: findAnnotationEntities,
80
- component: AnnotationBlock,
81
- }
@@ -1,27 +0,0 @@
1
- import React from 'react'
2
- import styled from 'styled-components'
3
-
4
- const LinkWrapper = styled.a`
5
- color: #3b5998;
6
- text-decoration: underline;
7
- `
8
-
9
- function findLinkEntities(contentBlock, callback, contentState) {
10
- contentBlock.findEntityRanges((character) => {
11
- const entityKey = character.getEntity()
12
- return (
13
- entityKey !== null &&
14
- contentState.getEntity(entityKey).getType() === 'LINK'
15
- )
16
- }, callback)
17
- }
18
-
19
- export const linkDecorator = {
20
- strategy: findLinkEntities,
21
- component: Link,
22
- }
23
-
24
- function Link(props) {
25
- const { url } = props.contentState.getEntity(props.entityKey).getData()
26
- return <LinkWrapper href={url}>{props.children}</LinkWrapper>
27
- }
@@ -1,65 +0,0 @@
1
- import React, { useEffect, useState } from 'react'
2
- import styled from 'styled-components'
3
- import { Select } from '@keystone-ui/fields'
4
-
5
- const Label = styled.label`
6
- display: block;
7
- margin: 10px 0;
8
- font-weight: 600;
9
- `
10
-
11
- const AlignSelect = styled(Select)`
12
- ${({ menuHeight }) => {
13
- return `margin-bottom: ${menuHeight}px;`
14
- }}
15
- `
16
-
17
- type Option = { label: string; value: string; isDisabled?: boolean }
18
- type AlignSelectorOnChangeFn = (param: string) => void
19
- type Options = Option[]
20
-
21
- export function AlignSelector(props: {
22
- align: string
23
- options: Options
24
- onChange: AlignSelectorOnChangeFn
25
- onOpen?: () => void
26
- }) {
27
- const [isOpen, setIsOpen] = useState(false)
28
- const [menuHeight, setMenuHeight] = useState(0)
29
- const { align, options, onChange, onOpen } = props
30
-
31
- useEffect(() => {
32
- const selectMenu = document.querySelector(
33
- '.css-nabggt-menu'
34
- ) as HTMLElement | null
35
-
36
- if (selectMenu) {
37
- const styles = window.getComputedStyle(selectMenu)
38
- const margin =
39
- parseFloat(styles['marginTop']) + parseFloat(styles['marginBottom'])
40
- setMenuHeight(selectMenu.offsetHeight + margin)
41
- } else {
42
- setMenuHeight(0)
43
- }
44
- if (isOpen && onOpen) {
45
- onOpen()
46
- }
47
- }, [isOpen])
48
- return (
49
- <React.Fragment>
50
- <Label htmlFor="alignment">對齊</Label>
51
- <AlignSelect
52
- id="alignment"
53
- // default align === undefined
54
- value={options.find((option) => option.value === align)}
55
- options={options}
56
- onChange={(option) => {
57
- onChange(option.value)
58
- }}
59
- onMenuOpen={() => setIsOpen(true)}
60
- onMenuClose={() => setIsOpen(false)}
61
- menuHeight={menuHeight}
62
- />
63
- </React.Fragment>
64
- )
65
- }
@@ -1,485 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react'
2
- import debounce from 'lodash/debounce'
3
- import styled from 'styled-components'
4
- import { TextInput } from '@keystone-ui/fields'
5
- import { Drawer, DrawerController } from '@keystone-ui/modals'
6
- import { gql, useLazyQuery } from '@keystone-6/core/admin-ui/apollo'
7
- import { AlignSelector } from './align-selector'
8
- import { SearchBox, SearchBoxOnChangeFn } from './search-box'
9
- import { Pagination } from './pagination'
10
-
11
- const imagesQuery = gql`
12
- query Photos($searchText: String!, $take: Int, $skip: Int) {
13
- photosCount(where: { name: { contains: $searchText } })
14
- photos(
15
- where: { name: { contains: $searchText } }
16
- take: $take
17
- skip: $skip
18
- ) {
19
- id
20
- name
21
- imageFile {
22
- url
23
- }
24
- resized {
25
- original
26
- w480
27
- w800
28
- w1200
29
- w1600
30
- w2400
31
- }
32
- }
33
- }
34
- `
35
-
36
- const _ = {
37
- debounce,
38
- }
39
-
40
- const ImageSearchBox = styled(SearchBox)`
41
- margin-top: 10px;
42
- `
43
-
44
- const ImageSelectionWrapper = styled.div`
45
- overflow: auto;
46
- margin-top: 10px;
47
- `
48
- const ImageBlockMetaWrapper = styled.div``
49
-
50
- const ImageGridsWrapper = styled.div`
51
- display: flex;
52
- flex-wrap: wrap;
53
- overflow: auto;
54
- margin-top: 5px;
55
- `
56
-
57
- const ImageGridWrapper = styled.div`
58
- flex: 0 0 33.3333%;
59
- cursor: pointer;
60
- padding: 0 10px 10px;
61
- `
62
-
63
- const ImageMetaGridsWrapper = styled.div`
64
- display: flex;
65
- flex-wrap: wrap;
66
- overflow: auto;
67
- `
68
-
69
- const ImageMetaGridWrapper = styled.div`
70
- flex: 0 0 33.3333%;
71
- cursor: pointer;
72
- padding: 0 10px 10px;
73
- `
74
-
75
- const Image = styled.img`
76
- display: block;
77
- width: 100%;
78
- aspect-ratio: 2;
79
- object-fit: cover;
80
- `
81
-
82
- const Label = styled.label`
83
- display: block;
84
- margin: 10px 0;
85
- font-weight: 600;
86
- `
87
-
88
- const SeparationLine = styled.div`
89
- border: #e1e5e9 1px solid;
90
- margin-top: 10px;
91
- margin-bottom: 10px;
92
- `
93
-
94
- const ImageSelected = styled.div`
95
- height: 1.4rem;
96
- `
97
-
98
- const ErrorWrapper = styled.div`
99
- & * {
100
- margin: 0;
101
- }
102
- `
103
-
104
- type ID = string
105
-
106
- export type ImageEntity = {
107
- id: ID
108
- name?: string
109
- imageFile: {
110
- url: string
111
- }
112
- resized: {
113
- original: string
114
- w480: string
115
- w800: string
116
- w1200: string
117
- w1600: string
118
- w2400: string
119
- }
120
- }
121
-
122
- export type ImageEntityWithMeta = {
123
- image: ImageEntity
124
- desc?: string
125
- url?: string
126
- }
127
-
128
- type ImageEntityOnSelectFn = (param: ImageEntity) => void
129
-
130
- function ImageGrids(props: {
131
- images: ImageEntity[]
132
- selected: ImageEntity[]
133
- onSelect: ImageEntityOnSelectFn
134
- }): React.ReactElement {
135
- const { images, selected, onSelect } = props
136
-
137
- return (
138
- <ImageGridsWrapper>
139
- {images.map((image) => {
140
- return (
141
- <ImageGrid
142
- key={image.id}
143
- isSelected={selected?.includes(image)}
144
- onSelect={() => onSelect(image)}
145
- image={image}
146
- />
147
- )
148
- })}
149
- </ImageGridsWrapper>
150
- )
151
- }
152
-
153
- function ImageGrid(props: {
154
- image: ImageEntity
155
- isSelected: boolean
156
- onSelect: ImageEntityOnSelectFn
157
- }) {
158
- const { image, onSelect, isSelected } = props
159
- return (
160
- <ImageGridWrapper key={image?.id} onClick={() => onSelect(image)}>
161
- <ImageSelected>
162
- {isSelected ? <i className="fas fa-check-circle"></i> : null}
163
- </ImageSelected>
164
- <Image
165
- src={image?.resized?.w800}
166
- onError={(e) => (e.currentTarget.src = image?.imageFile?.url)}
167
- />
168
- </ImageGridWrapper>
169
- )
170
- }
171
-
172
- type ImageMetaOnChangeFn = (params: ImageEntityWithMeta) => void
173
-
174
- function ImageMetaGrids(props: {
175
- imageMetas: ImageEntityWithMeta[]
176
- onChange: ImageMetaOnChangeFn
177
- enableCaption: boolean
178
- enableUrl: boolean
179
- }) {
180
- const { imageMetas, onChange, enableCaption, enableUrl } = props
181
- return (
182
- <ImageMetaGridsWrapper>
183
- {imageMetas.map((imageMeta) => (
184
- <ImageMetaGrid
185
- key={imageMeta?.image?.id}
186
- imageMeta={imageMeta}
187
- enableCaption={enableCaption}
188
- enableUrl={enableUrl}
189
- onChange={onChange}
190
- />
191
- ))}
192
- </ImageMetaGridsWrapper>
193
- )
194
- }
195
-
196
- function ImageMetaGrid(props: {
197
- imageMeta: ImageEntityWithMeta
198
- onChange: ImageMetaOnChangeFn
199
- enableCaption: boolean
200
- enableUrl: boolean
201
- }): React.ReactElement {
202
- const { imageMeta, enableCaption, enableUrl, onChange } = props
203
- const { image, desc, url } = imageMeta
204
-
205
- return (
206
- <ImageMetaGridWrapper>
207
- <Image
208
- src={image?.resized?.w800}
209
- onError={(e) => (e.currentTarget.src = image?.imageFile?.url)}
210
- />
211
- {enableCaption && (
212
- <React.Fragment>
213
- <Label htmlFor="caption">Image Caption:</Label>
214
- <TextInput
215
- id="caption"
216
- type="text"
217
- placeholder={image?.name}
218
- defaultValue={desc}
219
- onChange={_.debounce((e) => {
220
- onChange({
221
- image,
222
- desc: e.target.value,
223
- url,
224
- })
225
- })}
226
- />
227
- </React.Fragment>
228
- )}
229
- {enableUrl && (
230
- <React.Fragment>
231
- <Label htmlFor="url">Url:</Label>
232
- <TextInput
233
- id="url"
234
- type="text"
235
- placeholder="(Optional)"
236
- defaultValue={url}
237
- onChange={_.debounce((e) => {
238
- onChange({
239
- image,
240
- desc,
241
- url: e.target.value,
242
- })
243
- })}
244
- />
245
- </React.Fragment>
246
- )}
247
- </ImageMetaGridWrapper>
248
- )
249
- }
250
-
251
- type DelayInputOnChangeFn = (param: string) => void
252
-
253
- function DelayInput(props: {
254
- delay: string
255
- onChange: DelayInputOnChangeFn
256
- }): React.ReactElement {
257
- const { delay, onChange } = props
258
-
259
- return (
260
- <React.Fragment>
261
- <Label>Slideshow delay:</Label>
262
- <TextInput
263
- type="number"
264
- placeholder="請輸入自動切換秒數"
265
- step="0.5"
266
- min="1"
267
- value={delay}
268
- onChange={(e) => {
269
- onChange(e.target.value)
270
- }}
271
- />
272
- </React.Fragment>
273
- )
274
- }
275
-
276
- type ImageSelectorOnChangeFn = (
277
- params: ImageEntityWithMeta[],
278
- align?: string,
279
- delay?: number
280
- ) => void
281
-
282
- export function ImageSelector(props: {
283
- enableMultiSelect?: boolean
284
- enableCaption?: boolean
285
- enableUrl?: boolean
286
- enableAlignment?: boolean
287
- enableDelay?: boolean
288
- onChange: ImageSelectorOnChangeFn
289
- }) {
290
- const [
291
- queryImages,
292
- {
293
- loading,
294
- error,
295
- data: { photos: images = [], photosCount: imagesCount = 0 } = {},
296
- },
297
- ] = useLazyQuery(imagesQuery, { fetchPolicy: 'no-cache' })
298
- const [currentPage, setCurrentPage] = useState(0) // page starts with 1, 0 is used to detect initialization
299
- const [searchText, setSearchText] = useState('')
300
- const [selected, setSelected] = useState<ImageEntityWithMeta[]>([])
301
- const [delay, setDelay] = useState('5')
302
- const [align, setAlign] = useState(undefined)
303
- const contentWrapperRef = useRef<HTMLDivElement>()
304
-
305
- const pageSize = 6
306
-
307
- const options = [
308
- { value: undefined, label: 'default', isDisabled: false },
309
- { value: 'left', label: 'left', isDisabled: false },
310
- { value: 'right', label: 'right', isDisabled: false },
311
- ]
312
-
313
- const {
314
- enableMultiSelect = false,
315
- enableCaption = false,
316
- enableUrl = false,
317
- enableAlignment = false,
318
- enableDelay = false,
319
- onChange,
320
- } = props
321
-
322
- const onSave = () => {
323
- let adjustedDelay = +delay
324
- adjustedDelay = adjustedDelay < 1 ? 1 : adjustedDelay
325
- onChange(selected, align, adjustedDelay)
326
- }
327
-
328
- const onCancel = () => {
329
- onChange([])
330
- }
331
-
332
- const onSearchBoxChange: SearchBoxOnChangeFn = async (searchInput) => {
333
- setSearchText(searchInput)
334
- setCurrentPage(1)
335
- }
336
-
337
- const onDealyChange = (delay: string) => {
338
- setDelay(delay)
339
- }
340
-
341
- const onAlignSelectChange = (align: string) => {
342
- setAlign(align)
343
- }
344
-
345
- const onAlignSelectOpen = () => {
346
- const scrollWrapper = contentWrapperRef.current?.parentElement
347
- scrollWrapper.scrollTop = scrollWrapper.scrollHeight
348
- }
349
-
350
- const onImageMetaChange: ImageMetaOnChangeFn = (imageEntityWithMeta) => {
351
- if (enableMultiSelect) {
352
- const foundIndex = selected.findIndex(
353
- (ele) => ele?.image?.id === imageEntityWithMeta?.image?.id
354
- )
355
- if (foundIndex !== -1) {
356
- selected[foundIndex] = imageEntityWithMeta
357
- setSelected(selected)
358
- }
359
- return
360
- }
361
- setSelected([imageEntityWithMeta])
362
- }
363
-
364
- const onImagesGridSelect: ImageEntityOnSelectFn = (imageEntity) => {
365
- setSelected((selected) => {
366
- const filterdSelected = selected.filter(
367
- (ele) => ele.image?.id !== imageEntity.id
368
- )
369
-
370
- // deselect the image
371
- if (filterdSelected.length !== selected.length) {
372
- return filterdSelected
373
- }
374
-
375
- // add new selected one
376
- if (enableMultiSelect) {
377
- return selected.concat([{ image: imageEntity, desc: '' }])
378
- }
379
-
380
- // single select
381
- return [{ image: imageEntity, desc: '' }]
382
- })
383
- }
384
-
385
- const selectedImages = selected.map((ele: ImageEntityWithMeta) => {
386
- return ele.image
387
- })
388
-
389
- useEffect(() => {
390
- if (currentPage !== 0) {
391
- queryImages({
392
- variables: {
393
- searchText: searchText,
394
- skip: (currentPage - 1) * pageSize,
395
- take: pageSize,
396
- },
397
- })
398
- }
399
- }, [currentPage, searchText])
400
-
401
- let searchResult = (
402
- <React.Fragment>
403
- <ImageGrids
404
- images={images}
405
- selected={selectedImages}
406
- onSelect={onImagesGridSelect}
407
- />
408
- <Pagination
409
- currentPage={currentPage}
410
- total={imagesCount}
411
- pageSize={pageSize}
412
- onChange={(pageIndex) => {
413
- setCurrentPage(pageIndex)
414
- }}
415
- />
416
- </React.Fragment>
417
- )
418
- if (loading) {
419
- searchResult = <p>searching...</p>
420
- }
421
- if (error) {
422
- searchResult = (
423
- <ErrorWrapper>
424
- <h3>Errors occurs in the `images` query</h3>
425
- <div>
426
- <br />
427
- <b>Message:</b>
428
- <div>{error.message}</div>
429
- <br />
430
- <b>Stack:</b>
431
- <div>{error.stack}</div>
432
- <br />
433
- <b>Query:</b>
434
- <pre>{imagesQuery.loc.source.body}</pre>
435
- </div>
436
- </ErrorWrapper>
437
- )
438
- }
439
-
440
- return (
441
- <DrawerController isOpen={true}>
442
- <Drawer
443
- title="Select image"
444
- actions={{
445
- cancel: {
446
- label: 'Cancel',
447
- action: onCancel,
448
- },
449
- confirm: {
450
- label: 'Confirm',
451
- action: onSave,
452
- },
453
- }}
454
- >
455
- <div ref={contentWrapperRef}>
456
- <ImageSearchBox onChange={onSearchBoxChange} />
457
- <ImageSelectionWrapper>
458
- <div>{searchResult}</div>
459
- {!!selected.length && <SeparationLine />}
460
- <ImageMetaGrids
461
- imageMetas={selected}
462
- onChange={onImageMetaChange}
463
- enableCaption={enableCaption}
464
- enableUrl={enableUrl}
465
- />
466
- </ImageSelectionWrapper>
467
- <ImageBlockMetaWrapper>
468
- {(enableDelay || enableAlignment) && <SeparationLine />}
469
- {enableDelay && (
470
- <DelayInput delay={delay} onChange={onDealyChange} />
471
- )}
472
- {enableAlignment && (
473
- <AlignSelector
474
- align={align}
475
- options={options}
476
- onChange={onAlignSelectChange}
477
- onOpen={onAlignSelectOpen}
478
- />
479
- )}
480
- </ImageBlockMetaWrapper>
481
- </div>
482
- </Drawer>
483
- </DrawerController>
484
- )
485
- }