svelte-colourpicker 0.0.8 → 0.2.0
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.
- package/dist/ColourPicker.svelte +396 -313
- package/dist/ColourPicker.svelte.d.ts +24 -37
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -3
- package/package.json +56 -56
package/dist/ColourPicker.svelte
CHANGED
|
@@ -1,346 +1,428 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const size = 230
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
const hueColours = ["#ff0000", "#ffff00", "#00ff00", "#00ffff", "#0000ff", "#ff00ff", "#ff0000"]
|
|
3
|
+
|
|
4
|
+
const size = 230
|
|
5
|
+
|
|
6
|
+
type RGB = { r: number; g: number; b: number }
|
|
7
|
+
type RGBA = RGB & A
|
|
8
|
+
|
|
9
|
+
type HSV = { h: number; s: number; v: number }
|
|
10
|
+
type HSVA = HSV & A
|
|
11
|
+
|
|
12
|
+
type A = { a: number }
|
|
13
|
+
|
|
14
|
+
const numberRegExp = "(\\d*\\.?\\d{1,3})"
|
|
15
|
+
const delimiterRegExp = "[, ]+"
|
|
16
|
+
|
|
17
|
+
const rgbRegExp = new RegExp(`rgb\\(\\s*${numberRegExp}${delimiterRegExp}${numberRegExp}${delimiterRegExp}${numberRegExp}\\s*\\)`, "i")
|
|
18
|
+
const rgbaRegExp = new RegExp(`rgba\\(\\s*${numberRegExp}${delimiterRegExp}${numberRegExp}${delimiterRegExp}${numberRegExp}${delimiterRegExp}${numberRegExp}\\s*\\)`, "i")
|
|
19
|
+
|
|
20
|
+
const hexRegExp = (n: number) => `([\\dA-F]{${n}})`
|
|
21
|
+
|
|
22
|
+
const hex3RegExp = new RegExp(`^#?${hexRegExp(1).repeat(3)}$`, "i")
|
|
23
|
+
const hex6RegExp = new RegExp(`^#?${hexRegExp(2).repeat(3)}$`, "i")
|
|
24
|
+
const hex8RegExp = new RegExp(`^#?${hexRegExp(2).repeat(4)}$`, "i")
|
|
25
|
+
|
|
26
|
+
function colour(input: string | RGB | RGBA | HSV | HSVA) {
|
|
27
|
+
let r: number, g: number, b: number, h: number, s: number, v: number, a: number, rgba: string, hex: `#${string}`
|
|
28
|
+
|
|
29
|
+
if (typeof input == "string") {
|
|
30
|
+
let result: RegExpMatchArray | null
|
|
31
|
+
|
|
32
|
+
if ((result = rgbRegExp.exec(input))) {
|
|
33
|
+
r = parseFloat(result[1])
|
|
34
|
+
g = parseFloat(result[2])
|
|
35
|
+
b = parseFloat(result[3])
|
|
36
|
+
a = 1
|
|
37
|
+
} else if ((result = rgbaRegExp.exec(input))) {
|
|
38
|
+
r = parseFloat(result[1])
|
|
39
|
+
g = parseFloat(result[2])
|
|
40
|
+
b = parseFloat(result[3])
|
|
41
|
+
a = parseFloat(result[4])
|
|
42
|
+
} else if ((result = hex3RegExp.exec(input))) {
|
|
43
|
+
r = parseInt(result[1].repeat(2), 16)
|
|
44
|
+
g = parseInt(result[2].repeat(2), 16)
|
|
45
|
+
b = parseInt(result[3].repeat(2), 16)
|
|
46
|
+
a = 1
|
|
47
|
+
hex = `#${input}`
|
|
48
|
+
} else if ((result = hex6RegExp.exec(input))) {
|
|
49
|
+
r = parseInt(result[1], 16)
|
|
50
|
+
g = parseInt(result[2], 16)
|
|
51
|
+
b = parseInt(result[3], 16)
|
|
52
|
+
a = 1
|
|
53
|
+
hex = `#${input}`
|
|
54
|
+
} else if ((result = hex8RegExp.exec(input))) {
|
|
55
|
+
r = parseInt(result[1], 16)
|
|
56
|
+
g = parseInt(result[2], 16)
|
|
57
|
+
b = parseInt(result[3], 16)
|
|
58
|
+
a = parseInt(result[4], 16)
|
|
59
|
+
hex = `#${input}`
|
|
60
|
+
} else {
|
|
61
|
+
throw new Error(`Invalid input: "${input}"`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hsv = RGBToHSV(r, g, b)
|
|
65
|
+
|
|
66
|
+
h = hsv.h
|
|
67
|
+
s = hsv.s
|
|
68
|
+
v = hsv.v
|
|
69
|
+
} else if ("r" in input) {
|
|
70
|
+
r = input.r
|
|
71
|
+
g = input.g
|
|
72
|
+
b = input.b
|
|
73
|
+
|
|
74
|
+
const hsv = RGBToHSV(r, g, b)
|
|
75
|
+
|
|
76
|
+
h = hsv.h
|
|
77
|
+
s = hsv.s
|
|
78
|
+
v = hsv.v
|
|
79
|
+
|
|
80
|
+
a = "a" in input ? input.a : 1
|
|
81
|
+
} else {
|
|
82
|
+
h = input.h
|
|
83
|
+
s = input.s
|
|
84
|
+
v = input.v
|
|
85
|
+
|
|
86
|
+
const rgb = HSVToRGB(h, s, v)
|
|
87
|
+
|
|
88
|
+
r = rgb.r
|
|
89
|
+
g = rgb.g
|
|
90
|
+
b = rgb.b
|
|
91
|
+
|
|
92
|
+
a = "a" in input ? input.a : 1
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
rgba = `rgba(${r}, ${g}, ${b}, ${a})`
|
|
96
|
+
hex ??= `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}${a != 1 ? Math.round(a * 255).toString(16) : ""}`
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
r,
|
|
100
|
+
g,
|
|
101
|
+
b,
|
|
102
|
+
h,
|
|
103
|
+
s,
|
|
104
|
+
v,
|
|
105
|
+
a,
|
|
106
|
+
rgba,
|
|
107
|
+
hex
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* https://www.rapidtables.com/convert/color/rgb-to-hsv.html
|
|
113
|
+
* https://www.geeksforgeeks.org/program-change-rgb-color-model-hsv-color-model/
|
|
114
|
+
*/
|
|
115
|
+
function RGBToHSV(r: number, g: number, b: number): HSV {
|
|
116
|
+
let h: number, s: number, v: number
|
|
117
|
+
|
|
118
|
+
const r1 = r / 255
|
|
119
|
+
const g1 = g / 255
|
|
120
|
+
const b1 = b / 255
|
|
121
|
+
|
|
122
|
+
const max = Math.max(r1, g1, b1)
|
|
123
|
+
const min = Math.min(r1, g1, b1)
|
|
124
|
+
|
|
125
|
+
const difference = max - min
|
|
126
|
+
|
|
127
|
+
if (difference == 0) {
|
|
128
|
+
h = 0
|
|
129
|
+
} else {
|
|
130
|
+
switch (max) {
|
|
131
|
+
case r1:
|
|
132
|
+
h = (60 * ((g1 - b1) / difference) + 360) % 360
|
|
133
|
+
break
|
|
134
|
+
case g1:
|
|
135
|
+
h = (60 * ((b1 - r1) / difference) + 120) % 360
|
|
136
|
+
break
|
|
137
|
+
case b1:
|
|
138
|
+
h = (60 * ((r1 - g1) / difference) + 240) % 360
|
|
139
|
+
break
|
|
140
|
+
default:
|
|
141
|
+
throw new Error("h did not match r1 | g1 | b1")
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
s = max == 0 ? 0 : difference / max
|
|
146
|
+
v = max
|
|
147
|
+
|
|
148
|
+
return { h, s, v }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* https://www.rapidtables.com/convert/color/hsv-to-rgb.html
|
|
153
|
+
*/
|
|
154
|
+
function HSVToRGB(h: number, s: number, v: number): RGB {
|
|
155
|
+
let r: number, g: number, b: number
|
|
156
|
+
|
|
157
|
+
const chroma = s * v
|
|
158
|
+
const x = chroma * (1 - Math.abs(((h / 60) % 2) - 1))
|
|
159
|
+
const m = v - chroma
|
|
160
|
+
|
|
161
|
+
let r1: number, g1: number, b1: number
|
|
162
|
+
|
|
163
|
+
if (h < 60) {
|
|
164
|
+
r1 = chroma
|
|
165
|
+
g1 = x
|
|
166
|
+
b1 = 0
|
|
167
|
+
} else if (60 <= h && h < 120) {
|
|
168
|
+
r1 = x
|
|
169
|
+
g1 = chroma
|
|
170
|
+
b1 = 0
|
|
171
|
+
} else if (h < 180) {
|
|
172
|
+
r1 = 0
|
|
173
|
+
g1 = chroma
|
|
174
|
+
b1 = x
|
|
175
|
+
} else if (h < 240) {
|
|
176
|
+
r1 = 0
|
|
177
|
+
g1 = x
|
|
178
|
+
b1 = chroma
|
|
179
|
+
} else if (h < 300) {
|
|
180
|
+
r1 = x
|
|
181
|
+
g1 = 0
|
|
182
|
+
b1 = chroma
|
|
183
|
+
} /* if (h < 360) */ else {
|
|
184
|
+
r1 = chroma
|
|
185
|
+
g1 = 0
|
|
186
|
+
b1 = x
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
r = Math.round((r1 + m) * 255)
|
|
190
|
+
g = Math.round((g1 + m) * 255)
|
|
191
|
+
b = Math.round((b1 + m) * 255)
|
|
192
|
+
|
|
193
|
+
return { r, g, b }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function clamp(value: number, min: number, max: number): number {
|
|
197
|
+
if (value < min) {
|
|
198
|
+
return min
|
|
199
|
+
}
|
|
200
|
+
if (value > max) {
|
|
201
|
+
return max
|
|
202
|
+
}
|
|
203
|
+
return value
|
|
204
|
+
}
|
|
159
205
|
</script>
|
|
160
206
|
|
|
161
|
-
<script
|
|
162
|
-
import {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
async function
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
207
|
+
<script lang="ts">
|
|
208
|
+
import { tick } from "svelte"
|
|
209
|
+
import { fade, fly } from "svelte/transition"
|
|
210
|
+
|
|
211
|
+
interface Props {
|
|
212
|
+
value?: string
|
|
213
|
+
onchange?: (event: CustomEvent<{ rgb: RGB; hsv: HSV; hex: `#${string}` } & A>) => any
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
let { value = $bindable("rgba(255, 0, 0, 1)"), onchange }: Props = $props()
|
|
217
|
+
|
|
218
|
+
let c = $state.raw(colour(value))
|
|
219
|
+
|
|
220
|
+
$effect(() => {
|
|
221
|
+
value = c.rgba
|
|
222
|
+
onchange?.(new CustomEvent("change", { detail: { rgb: { r: c.r, b: c.b, g: c.g }, hsv: { h: c.h, s: c.s, v: c.v }, hex: c.hex, a: c.a } }))
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
$effect(() => {
|
|
226
|
+
if (value != c.rgba) {
|
|
227
|
+
c = colour(value)
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
let open = $state(false)
|
|
232
|
+
let dragging = $state(false)
|
|
233
|
+
|
|
234
|
+
async function openPicker(event: Event) {
|
|
235
|
+
event.stopPropagation()
|
|
236
|
+
open = true
|
|
237
|
+
await tick()
|
|
238
|
+
drawColourCanvas()
|
|
239
|
+
drawHueSlider()
|
|
240
|
+
drawOpacitySlider()
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function tryClosePicker() {
|
|
244
|
+
await tick()
|
|
245
|
+
if (!dragging) {
|
|
246
|
+
open = false
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let colourCanvas: HTMLCanvasElement = $state()!
|
|
251
|
+
let hueSlider: HTMLCanvasElement = $state()!
|
|
252
|
+
let opacitySlider: HTMLCanvasElement = $state()!
|
|
253
|
+
|
|
254
|
+
// #region Draw Canvas
|
|
255
|
+
|
|
256
|
+
function drawColourCanvas() {
|
|
257
|
+
const context = colourCanvas.getContext("2d")!
|
|
258
|
+
|
|
259
|
+
context.clearRect(0, 0, colourCanvas.width, colourCanvas.height)
|
|
260
|
+
|
|
261
|
+
const horizontalGradient = context.createLinearGradient(0, 0, colourCanvas.width, 0)
|
|
262
|
+
horizontalGradient.addColorStop(0, "#ffffff")
|
|
263
|
+
horizontalGradient.addColorStop(1, colour({ h: c.h, s: 1, v: 1 }).rgba)
|
|
264
|
+
context.fillStyle = horizontalGradient
|
|
265
|
+
context.fillRect(0, 0, colourCanvas.width, colourCanvas.height)
|
|
266
|
+
|
|
267
|
+
const verticalGradient = context.createLinearGradient(0, 0, 0, colourCanvas.height)
|
|
268
|
+
verticalGradient.addColorStop(0, "transparent")
|
|
269
|
+
verticalGradient.addColorStop(1, "#000000")
|
|
270
|
+
context.fillStyle = verticalGradient
|
|
271
|
+
context.fillRect(0, 0, colourCanvas.width, colourCanvas.height)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function drawHueSlider() {
|
|
275
|
+
const context = hueSlider.getContext("2d")!
|
|
276
|
+
context.clearRect(0, 0, hueSlider.width, hueSlider.height)
|
|
277
|
+
const gradient = context.createLinearGradient(0, 0, 0, hueSlider.height)
|
|
278
|
+
for (let i = 0, offset = 0, step = 1 / (hueColours.length - 1); i < hueColours.length; i++, offset += step) {
|
|
279
|
+
gradient.addColorStop(offset, hueColours[i])
|
|
280
|
+
}
|
|
281
|
+
context.fillStyle = gradient
|
|
282
|
+
context.fillRect(0, 0, hueSlider.width, hueSlider.height)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function drawOpacitySlider() {
|
|
286
|
+
const context = opacitySlider.getContext("2d")!
|
|
287
|
+
context.clearRect(0, 0, opacitySlider.width, opacitySlider.height)
|
|
288
|
+
|
|
289
|
+
const gradient = context.createLinearGradient(0, 0, 0, opacitySlider.height)
|
|
290
|
+
gradient.addColorStop(0, "transparent")
|
|
291
|
+
gradient.addColorStop(1, colour({ h: c.h, s: c.s, v: c.v, a: 1 }).rgba)
|
|
292
|
+
context.fillStyle = gradient
|
|
293
|
+
context.fillRect(0, 0, opacitySlider.width, opacitySlider.height)
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// #endregion
|
|
297
|
+
|
|
298
|
+
// #region Events
|
|
299
|
+
|
|
300
|
+
const initListener = (fn: (e: MouseEvent) => void): ((e: MouseEvent) => void) => {
|
|
301
|
+
const fn_preventDefault = (e: MouseEvent) => {
|
|
302
|
+
e.preventDefault()
|
|
303
|
+
fn(e)
|
|
304
|
+
}
|
|
305
|
+
return function (e: MouseEvent) {
|
|
306
|
+
if (e.button != 0) {
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
dragging = true
|
|
310
|
+
fn(e)
|
|
311
|
+
addEventListener("mousemove", fn_preventDefault)
|
|
312
|
+
addEventListener("mouseup", () => {
|
|
313
|
+
removeEventListener("mousemove", fn_preventDefault)
|
|
314
|
+
dragging = false
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function colourCanvasMove(e: MouseEvent): void {
|
|
320
|
+
const rect = colourCanvas.getBoundingClientRect()
|
|
321
|
+
c = colour({ h: c.h, s: clamp(e.pageX - rect.left, 0, size) / size, v: (size - clamp(e.pageY - rect.top, 0, size)) / size, a: c.a })
|
|
322
|
+
drawOpacitySlider()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function hueSliderMove(e: MouseEvent): void {
|
|
326
|
+
c = colour({ h: (clamp(e.pageY - hueSlider.getBoundingClientRect().top, 0, size) / size) * 360, s: c.s, v: c.v, a: c.a })
|
|
327
|
+
drawColourCanvas()
|
|
328
|
+
drawOpacitySlider()
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function opacitySliderMove(e: MouseEvent): void {
|
|
332
|
+
c = colour({ h: c.h, s: c.s, v: c.v, a: parseFloat((clamp(e.pageY - opacitySlider.getBoundingClientRect().top, 0, size) / size).toFixed(2)) })
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function hexInputChange(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
|
|
336
|
+
let value = e.currentTarget.value
|
|
337
|
+
if (value.startsWith("#")) {
|
|
338
|
+
value = value.substring(1)
|
|
339
|
+
}
|
|
340
|
+
if ([3, 6, 8].includes(value.length)) {
|
|
341
|
+
c = colour(value)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function redInputChange(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
|
|
346
|
+
const value = parseFloat(e.currentTarget.value)
|
|
347
|
+
if (value >= 0 && value <= 255) {
|
|
348
|
+
c = colour({ r: value, g: c.g, b: c.b, a: c.a })
|
|
349
|
+
drawColourCanvas()
|
|
350
|
+
drawOpacitySlider()
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function greenInputChange(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
|
|
355
|
+
const value = parseFloat(e.currentTarget.value)
|
|
356
|
+
if (value >= 0 && value <= 255) {
|
|
357
|
+
c = colour({ r: c.r, g: value, b: c.b, a: c.a })
|
|
358
|
+
drawColourCanvas()
|
|
359
|
+
drawOpacitySlider()
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function blueInputChange(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
|
|
364
|
+
const value = parseFloat(e.currentTarget.value)
|
|
365
|
+
if (value >= 0 && value <= 255) {
|
|
366
|
+
c = colour({ r: c.r, g: c.g, b: value, a: c.a })
|
|
367
|
+
drawColourCanvas()
|
|
368
|
+
drawOpacitySlider()
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function alphaInputChange(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
|
|
373
|
+
const value = parseFloat(e.currentTarget.value)
|
|
374
|
+
if (value >= 0 && value <= 1) {
|
|
375
|
+
c = colour({ h: c.h, s: c.s, v: c.v, a: value })
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// #endregion
|
|
300
380
|
</script>
|
|
301
381
|
|
|
302
|
-
<svelte:window
|
|
382
|
+
<svelte:window onmouseup={open && !dragging ? tryClosePicker : null} />
|
|
303
383
|
|
|
304
384
|
<div class="container">
|
|
305
385
|
<div class="colour-input-container transparent">
|
|
306
|
-
|
|
386
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
387
|
+
<div class="colour-input" style:--value={value} onclick={openPicker} onkeypress={openPicker}></div>
|
|
307
388
|
</div>
|
|
308
389
|
{#if open}
|
|
390
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
309
391
|
<div
|
|
310
392
|
class="colour-picker-container"
|
|
311
393
|
in:fly={{ y: 10, duration: 150 }}
|
|
312
394
|
out:fade={{ duration: 50 }}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
395
|
+
onclick={(event) => event.stopPropagation()}
|
|
396
|
+
onkeypress={(event) => event.stopPropagation()}
|
|
397
|
+
onmouseup={(e) => !dragging && e.stopPropagation()}
|
|
316
398
|
>
|
|
317
399
|
<div class="colour-picker-arrow">
|
|
318
400
|
<div class="colour-picker">
|
|
319
401
|
<div class="main">
|
|
320
402
|
<div class="canvas-container">
|
|
321
|
-
<canvas bind:this={colourCanvas} width={size} height={size}
|
|
403
|
+
<canvas bind:this={colourCanvas} width={size} height={size} onmousedown={initListener(colourCanvasMove)}></canvas>
|
|
322
404
|
<div
|
|
323
405
|
class="pointer pointer-both"
|
|
324
406
|
style:--pointer-bg={colour({ h: c.h, s: c.s, v: c.v, a: 1 }).rgba}
|
|
325
407
|
style:--x={`${c.s * size}px`}
|
|
326
408
|
style:--y={`${size - c.v * size}px`}
|
|
327
|
-
|
|
409
|
+
></div>
|
|
328
410
|
</div>
|
|
329
411
|
<div class="canvas-container">
|
|
330
|
-
<canvas bind:this={hueSlider} width={18} height={size}
|
|
331
|
-
<div class="pointer pointer-vertical" style:--pointer-bg={colour({ h: c.h, s: 1, v: 1 }).rgba} style:--y={`${(c.h / 360) * size}px`}
|
|
412
|
+
<canvas bind:this={hueSlider} width={18} height={size} onmousedown={initListener(hueSliderMove)}></canvas>
|
|
413
|
+
<div class="pointer pointer-vertical" style:--pointer-bg={colour({ h: c.h, s: 1, v: 1 }).rgba} style:--y={`${(c.h / 360) * size}px`}></div>
|
|
332
414
|
</div>
|
|
333
415
|
<div class="canvas-container transparent">
|
|
334
|
-
<canvas bind:this={opacitySlider} width={18} height={size}
|
|
335
|
-
<div class="pointer pointer-vertical" style:--pointer-bg={c.rgba} style:--y={`${c.a * size}px`}
|
|
416
|
+
<canvas bind:this={opacitySlider} width={18} height={size} onmousedown={initListener(opacitySliderMove)}></canvas>
|
|
417
|
+
<div class="pointer pointer-vertical" style:--pointer-bg={c.rgba} style:--y={`${c.a * size}px`}></div>
|
|
336
418
|
</div>
|
|
337
419
|
</div>
|
|
338
420
|
<div class="inputs-container">
|
|
339
|
-
<input type="text" maxlength={7} id="hex" value={c.hex}
|
|
340
|
-
<input type="number" autocomplete="off" min={0} max={255} id="r" value={c.r}
|
|
341
|
-
<input type="number" autocomplete="off" min={0} max={255} id="g" value={c.g}
|
|
342
|
-
<input type="number" autocomplete="off" min={0} max={255} id="b" value={c.b}
|
|
343
|
-
<input type="number" autocomplete="off" min={0} max={255} id="a" value={c.a}
|
|
421
|
+
<input type="text" maxlength={7} id="hex" value={c.hex} oninput={hexInputChange} />
|
|
422
|
+
<input type="number" autocomplete="off" min={0} max={255} id="r" value={c.r} oninput={redInputChange} />
|
|
423
|
+
<input type="number" autocomplete="off" min={0} max={255} id="g" value={c.g} oninput={greenInputChange} />
|
|
424
|
+
<input type="number" autocomplete="off" min={0} max={255} id="b" value={c.b} oninput={blueInputChange} />
|
|
425
|
+
<input type="number" autocomplete="off" min={0} max={255} id="a" value={c.a} oninput={alphaInputChange} />
|
|
344
426
|
<label for="hex">HEX</label>
|
|
345
427
|
<label for="r">R</label>
|
|
346
428
|
<label for="g">G</label>
|
|
@@ -476,6 +558,7 @@ function alphaInputChange(e) {
|
|
|
476
558
|
|
|
477
559
|
input[type="number"] {
|
|
478
560
|
width: 2.75rem;
|
|
561
|
+
appearance: textfield;
|
|
479
562
|
-moz-appearance: textfield; /* Firefox */
|
|
480
563
|
}
|
|
481
564
|
|
|
@@ -1,37 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
hex: `#${string}`;
|
|
26
|
-
} & A>;
|
|
27
|
-
} & {
|
|
28
|
-
[evt: string]: CustomEvent<any>;
|
|
29
|
-
};
|
|
30
|
-
slots: {};
|
|
31
|
-
};
|
|
32
|
-
export type ColourPickerProps = typeof __propDef.props;
|
|
33
|
-
export type ColourPickerEvents = typeof __propDef.events;
|
|
34
|
-
export type ColourPickerSlots = typeof __propDef.slots;
|
|
35
|
-
export default class ColourPicker extends SvelteComponentTyped<ColourPickerProps, ColourPickerEvents, ColourPickerSlots> {
|
|
36
|
-
}
|
|
37
|
-
export {};
|
|
1
|
+
type RGB = {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
};
|
|
6
|
+
type HSV = {
|
|
7
|
+
h: number;
|
|
8
|
+
s: number;
|
|
9
|
+
v: number;
|
|
10
|
+
};
|
|
11
|
+
type A = {
|
|
12
|
+
a: number;
|
|
13
|
+
};
|
|
14
|
+
interface Props {
|
|
15
|
+
value?: string;
|
|
16
|
+
onchange?: (event: CustomEvent<{
|
|
17
|
+
rgb: RGB;
|
|
18
|
+
hsv: HSV;
|
|
19
|
+
hex: `#${string}`;
|
|
20
|
+
} & A>) => any;
|
|
21
|
+
}
|
|
22
|
+
declare const ColourPicker: import("svelte").Component<Props, {}, "value">;
|
|
23
|
+
type ColourPicker = ReturnType<typeof ColourPicker>;
|
|
24
|
+
export default ColourPicker;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import ColourPicker from "./ColourPicker.svelte";
|
|
2
|
-
export { ColourPicker };
|
|
1
|
+
import ColourPicker from "./ColourPicker.svelte";
|
|
2
|
+
export { ColourPicker };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export { ColourPicker };
|
|
1
|
+
import ColourPicker from "./ColourPicker.svelte";
|
|
2
|
+
export { ColourPicker };
|
package/package.json
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
|
|
56
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-colourpicker",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Svelte colour picker component",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"color",
|
|
7
|
+
"colour",
|
|
8
|
+
"picker",
|
|
9
|
+
"svelte"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/cooolbros/svelte-colourpicker#readme",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/cooolbros/svelte-colourpicker.git"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"svelte": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"svelte": "^5.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@sveltejs/adapter-auto": "^7.0.1",
|
|
31
|
+
"@sveltejs/kit": "^2.63.0",
|
|
32
|
+
"@sveltejs/package": "^2.5.8",
|
|
33
|
+
"@sveltejs/vite-plugin-svelte": "^7.1.2",
|
|
34
|
+
"prettier": "^3.8.3",
|
|
35
|
+
"prettier-plugin-svelte": "^4.1.0",
|
|
36
|
+
"publint": "^0.3.21",
|
|
37
|
+
"svelte": "^5.56.2",
|
|
38
|
+
"svelte-check": "^4.6.0",
|
|
39
|
+
"tslib": "^2.5.2",
|
|
40
|
+
"typescript": "^6.0.3",
|
|
41
|
+
"vite": "^8.0.16"
|
|
42
|
+
},
|
|
43
|
+
"svelte": "./dist/index.js",
|
|
44
|
+
"types": "./dist/index.d.ts",
|
|
45
|
+
"type": "module",
|
|
46
|
+
"scripts": {
|
|
47
|
+
"dev": "vite dev",
|
|
48
|
+
"build": "vite build && pnpm package",
|
|
49
|
+
"preview": "vite preview",
|
|
50
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
51
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
52
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
53
|
+
"lint": "prettier --plugin-search-dir . --check .",
|
|
54
|
+
"format": "prettier --plugin-search-dir . --write ."
|
|
55
|
+
}
|
|
56
|
+
}
|