Skip to content

Commit 0ae0353

Browse files
authored
Merge pull request #17 from github/add-flow-declaration-file
Add flow declaration file
2 parents 3886f9e + 3147151 commit 0ae0353

File tree

3 files changed

+73
-64
lines changed

3 files changed

+73
-64
lines changed

index.js

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@ tmpl.innerHTML = `
1616
</div>
1717
`
1818

19+
const startPositions: WeakMap<ImageCropElement, {startX: number, startY: number}> = new WeakMap()
20+
const dragStartPositions: WeakMap<ImageCropElement, {dragStartX: number, dragStartY: number}> = new WeakMap()
21+
const constructedElements: WeakMap<ImageCropElement, {image: HTMLImageElement, box: HTMLElement}> = new WeakMap()
22+
1923
function moveCropArea(event: MouseEvent | KeyboardEvent) {
2024
const el = event.currentTarget
2125
if (!(el instanceof ImageCropElement)) return
26+
const {box, image} = constructedElements.get(el) || {}
2227

2328
let deltaX = 0
2429
let deltaY = 0
@@ -32,23 +37,27 @@ function moveCropArea(event: MouseEvent | KeyboardEvent) {
3237
} else if (event.key === 'ArrowRight') {
3338
deltaX = 1
3439
}
35-
} else if (el.dragStartX && el.dragStartY && event instanceof MouseEvent) {
36-
deltaX = event.pageX - el.dragStartX
37-
deltaY = event.pageY - el.dragStartY
40+
} else if (dragStartPositions.has(el) && event instanceof MouseEvent) {
41+
const pos = dragStartPositions.get(el)
42+
if (!pos) return
43+
deltaX = event.pageX - pos.dragStartX
44+
deltaY = event.pageY - pos.dragStartY
3845
}
3946

4047
if (deltaX !== 0 || deltaY !== 0) {
41-
const x = Math.min(Math.max(0, el.box.offsetLeft + deltaX), el.image.width - el.box.offsetWidth)
42-
const y = Math.min(Math.max(0, el.box.offsetTop + deltaY), el.image.height - el.box.offsetHeight)
43-
el.box.style.left = `${x}px`
44-
el.box.style.top = `${y}px`
48+
const x = Math.min(Math.max(0, box.offsetLeft + deltaX), image.width - box.offsetWidth)
49+
const y = Math.min(Math.max(0, box.offsetTop + deltaY), image.height - box.offsetHeight)
50+
box.style.left = `${x}px`
51+
box.style.top = `${y}px`
4552

46-
fireChangeEvent(el, {x, y, width: el.box.offsetWidth, height: el.box.offsetHeight})
53+
fireChangeEvent(el, {x, y, width: box.offsetWidth, height: box.offsetHeight})
4754
}
4855

4956
if (event instanceof MouseEvent) {
50-
el.dragStartX = event.pageX
51-
el.dragStartY = event.pageY
57+
dragStartPositions.set(el, {
58+
dragStartX: event.pageX,
59+
dragStartY: event.pageY
60+
})
5261
}
5362
}
5463

@@ -58,6 +67,7 @@ function updateCropArea(event: MouseEvent | KeyboardEvent) {
5867

5968
const el = target.closest('image-crop')
6069
if (!(el instanceof ImageCropElement)) return
70+
const {box} = constructedElements.get(el) || {}
6171

6272
const rect = el.getBoundingClientRect()
6373
let deltaX, deltaY, delta
@@ -66,13 +76,14 @@ function updateCropArea(event: MouseEvent | KeyboardEvent) {
6676
if (event.key === '-') delta = -10
6777
if (event.key === '=') delta = +10
6878
if (!delta) return
69-
deltaX = el.box.offsetWidth + delta
70-
deltaY = el.box.offsetHeight + delta
71-
el.startX = el.box.offsetLeft
72-
el.startY = el.box.offsetTop
79+
deltaX = box.offsetWidth + delta
80+
deltaY = box.offsetHeight + delta
81+
startPositions.set(el, {startX: box.offsetLeft, startY: box.offsetTop})
7382
} else if (event instanceof MouseEvent) {
74-
deltaX = event.pageX - el.startX - rect.left - window.pageXOffset
75-
deltaY = event.pageY - el.startY - rect.top - window.pageYOffset
83+
const pos = startPositions.get(el)
84+
if (!pos) return
85+
deltaX = event.pageX - pos.startX - rect.left - window.pageXOffset
86+
deltaY = event.pageY - pos.startY - rect.top - window.pageYOffset
7687
}
7788

7889
if (deltaX && deltaY) updateDimensions(el, deltaX, deltaY, !(event instanceof KeyboardEvent))
@@ -84,6 +95,7 @@ function startUpdate(event: MouseEvent) {
8495

8596
const el = currentTarget.closest('image-crop')
8697
if (!(el instanceof ImageCropElement)) return
98+
const {box} = constructedElements.get(el) || {}
8799

88100
const target = event.target
89101
if (!(target instanceof HTMLElement)) return
@@ -94,8 +106,10 @@ function startUpdate(event: MouseEvent) {
94106
el.addEventListener('mousemove', updateCropArea)
95107
if (['nw', 'se'].indexOf(direction) >= 0) el.classList.add('nwse')
96108
if (['ne', 'sw'].indexOf(direction) >= 0) el.classList.add('nesw')
97-
el.startX = el.box.offsetLeft + (['se', 'ne'].indexOf(direction) >= 0 ? 0 : el.box.offsetWidth)
98-
el.startY = el.box.offsetTop + (['se', 'sw'].indexOf(direction) >= 0 ? 0 : el.box.offsetHeight)
109+
startPositions.set(el, {
110+
startX: box.offsetLeft + (['se', 'ne'].indexOf(direction) >= 0 ? 0 : box.offsetWidth),
111+
startY: box.offsetTop + (['se', 'sw'].indexOf(direction) >= 0 ? 0 : box.offsetHeight)
112+
})
99113
updateCropArea(event)
100114
} else {
101115
// Move crop area
@@ -104,25 +118,24 @@ function startUpdate(event: MouseEvent) {
104118
}
105119

106120
function updateDimensions(target, deltaX, deltaY, reposition = true) {
107-
let newSide = Math.max(Math.abs(deltaX), Math.abs(deltaY), target.minWidth)
121+
let newSide = Math.max(Math.abs(deltaX), Math.abs(deltaY), 10)
122+
const pos = startPositions.get(target)
123+
if (!pos) return
124+
const {box, image} = constructedElements.get(target) || {}
108125
newSide = Math.min(
109126
newSide,
110-
deltaY > 0 ? target.image.height - target.startY : target.startY,
111-
deltaX > 0 ? target.image.width - target.startX : target.startX
127+
deltaY > 0 ? image.height - pos.startY : pos.startY,
128+
deltaX > 0 ? image.width - pos.startX : pos.startX
112129
)
113130

114-
const x = reposition
115-
? Math.round(Math.max(0, deltaX > 0 ? target.startX : target.startX - newSide))
116-
: target.box.offsetLeft
117-
const y = reposition
118-
? Math.round(Math.max(0, deltaY > 0 ? target.startY : target.startY - newSide))
119-
: target.box.offsetTop
131+
const x = reposition ? Math.round(Math.max(0, deltaX > 0 ? pos.startX : pos.startX - newSide)) : box.offsetLeft
132+
const y = reposition ? Math.round(Math.max(0, deltaY > 0 ? pos.startY : pos.startY - newSide)) : box.offsetTop
120133

121-
target.box.style.left = `${x}px`
122-
target.box.style.top = `${y}px`
134+
box.style.left = `${x}px`
135+
box.style.top = `${y}px`
123136

124-
target.box.style.width = `${newSide}px`
125-
target.box.style.height = `${newSide}px`
137+
box.style.width = `${newSide}px`
138+
box.style.height = `${newSide}px`
126139
fireChangeEvent(target, {x, y, width: newSide, height: newSide})
127140
}
128141

@@ -138,25 +151,28 @@ function imageReady(event: Event) {
138151
}
139152

140153
function setInitialPosition(el) {
141-
const image = el.image
154+
const {image} = constructedElements.get(el) || {}
142155
const side = Math.round(image.clientWidth > image.clientHeight ? image.clientHeight : image.clientWidth)
143-
el.startX = (image.clientWidth - side) / 2
144-
el.startY = (image.clientHeight - side) / 2
156+
startPositions.set(el, {
157+
startX: (image.clientWidth - side) / 2,
158+
startY: (image.clientHeight - side) / 2
159+
})
145160
updateDimensions(el, side, side)
146161
}
147162

148163
function stopUpdate(event: MouseEvent) {
149164
const el = event.currentTarget
150165
if (!(el instanceof ImageCropElement)) return
151166

152-
el.dragStartX = el.dragStartY = null
167+
dragStartPositions.delete(el)
153168
el.classList.remove('nwse', 'nesw')
154169
el.removeEventListener('mousemove', updateCropArea)
155170
el.removeEventListener('mousemove', moveCropArea)
156171
}
157172

158173
function fireChangeEvent(target: ImageCropElement, result: {x: number, y: number, width: number, height: number}) {
159-
const ratio = target.image.naturalWidth / target.image.width
174+
const {image} = constructedElements.get(target) || {}
175+
const ratio = image.naturalWidth / image.width
160176
for (const key in result) {
161177
const value = Math.round(result[key] * ratio)
162178
result[key] = value
@@ -167,42 +183,24 @@ function fireChangeEvent(target: ImageCropElement, result: {x: number, y: number
167183
target.dispatchEvent(new CustomEvent('image-crop-change', {bubbles: true, detail: result}))
168184
}
169185

170-
export class ImageCropElement extends HTMLElement {
171-
image: HTMLImageElement
172-
box: HTMLElement
173-
constructed: boolean
174-
minWidth: number
175-
dragStartX: ?number
176-
dragStartY: ?number
177-
startX: number
178-
startY: number
179-
180-
constructor() {
181-
super()
182-
this.minWidth = 10
183-
}
184-
186+
class ImageCropElement extends HTMLElement {
185187
connectedCallback() {
186-
if (this.constructed) return
187-
this.constructed = true
188-
188+
if (constructedElements.has(this)) return
189189
this.appendChild(document.importNode(tmpl.content, true))
190-
const image = this.querySelector('img')
191-
if (!(image instanceof HTMLImageElement)) return
192-
this.image = image
193-
194190
const box = this.querySelector('[data-crop-box]')
195191
if (!(box instanceof HTMLElement)) return
196-
this.box = box
192+
const image = this.querySelector('img')
193+
if (!(image instanceof HTMLImageElement)) return
194+
constructedElements.set(this, {box, image})
197195

198-
this.image.addEventListener('load', imageReady)
196+
image.addEventListener('load', imageReady)
199197
this.addEventListener('mouseleave', stopUpdate)
200198
this.addEventListener('mouseup', stopUpdate)
201-
this.box.addEventListener('mousedown', startUpdate)
199+
box.addEventListener('mousedown', startUpdate)
202200
this.addEventListener('keydown', moveCropArea)
203201
this.addEventListener('keydown', updateCropArea)
204202

205-
if (this.src) this.image.src = this.src
203+
if (this.src) image.src = this.src
206204
}
207205

208206
static get observedAttributes() {
@@ -234,9 +232,10 @@ export class ImageCropElement extends HTMLElement {
234232
}
235233

236234
attributeChangedCallback(attribute: string, oldValue: string, newValue: string) {
235+
const {image} = constructedElements.get(this) || {}
237236
if (attribute === 'src') {
238237
this.loaded = false
239-
if (this.image) this.image.src = newValue
238+
if (image) image.src = newValue
240239
}
241240
}
242241
}

index.js.flow

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* @flow strict */
2+
3+
declare module '@github/image-crop-element' {
4+
declare export default class ImageCropElement extends HTMLElement {
5+
get src(): ?string;
6+
set src(val: ?string): void;
7+
get loaded(): boolean;
8+
set loaded(val: boolean): void;
9+
}
10+
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"prebuild": "npm run clean && npm run lint && mkdir dist",
1515
"build-umd": "BABEL_ENV=umd babel index.js -o dist/index.umd.js",
1616
"build-esm": "BABEL_ENV=esm babel index.js -o dist/index.esm.js",
17-
"build": "npm run build-umd && npm run build-esm",
17+
"build": "npm run build-umd && npm run build-esm && cp index.js.flow dist/index.umd.js.flow && cp index.js.flow dist/index.esm.js.flow",
1818
"pretest": "npm run build",
1919
"prepublishOnly": "npm run build",
2020
"test": "karma start test/karma.config.js"

0 commit comments

Comments
 (0)