satis 2.1.53 → 2.1.54

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 358cd4b72fd41d7611be2d1c2ddae7d64ee55ad9b9d68cd10169d30b9d241697
4
- data.tar.gz: 538a47d63390c4c94c7497d73d5c95b9aaaa9e1dc22a22ace1d071f8a1350a24
3
+ metadata.gz: 8e4087c76633f9b2a02fc083fd95c60a4a28d9677dbe44ee11735e4fa0e29810
4
+ data.tar.gz: 2ab23a72ad341b331ef4c8ea1fc1780d46e32d1657194a4a8d1fb7ca0193db37
5
5
  SHA512:
6
- metadata.gz: c0c085c46aee29463647e80eb10bff6beb997aadb25e9464ee86f4758d969a574241659918342ad1b4ea06a42e36edcec171ef0209b30607bc070e4739963f17
7
- data.tar.gz: 1966166a699aa72b262b909325569083365c63919bc238204a9b1c048d08b769366611e6e7a200d6ea1dfa9a507e53e1f683b7173c7df1b031520bf652e6b11b
6
+ metadata.gz: 609e9ac871cbfb81c23b4bad5a60822671d1b4e26132fe2ac219ac6185ef459e7a69a5ca8f28ece3eb911588e505d93fd0206bbf0f07f338d20a0783f2f6a54e
7
+ data.tar.gz: b572b8fcfb7ee048e40b729b3f56fa5f3b61cd97515b9ddce60c35c1a5a77f01544ca4b53f5f0fdc2a5fb335b3fc2969b02d71997f2d325b080aed3da7ca11b6
@@ -6,22 +6,20 @@ div.satis-date-time-picker data-controller="satis-date-time-picker" data-satis-d
6
6
  button.cursor-pointer.w-6.h-full.flex.items-center.text-gray-400.outline-none.focus:outline-none data-satis-date-time-picker-target="clearButton" data-action="click->satis-date-time-picker#clear"
7
7
  i.fas.fa-xmark
8
8
 
9
- .container.z-10.shadow.bg-white.border.border-gray-300.dark:bg-gray-800.dark:border-gray-700.rounded.p-4.w-72 class="#{inline ? 'inline-block' : 'hidden'}" data-satis-date-time-picker-target="calendarView"
9
+ .container.z-10.shadow.bg-white.border.border-gray-300.dark:bg-gray-800.dark:border-gray-700.rounded.p-4.w-96 class="#{inline ? 'inline-block' : 'hidden'}" data-satis-date-time-picker-target="calendarView"
10
10
  .flex.justify-between.items-center.mb-2
11
11
  div
12
- button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#previousYear"
13
- i.text-gray-500.inline-flex.py-1 class=Satis.config.icons[:previous_year]
14
12
  button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#previousMonth"
15
13
  i.text-gray-500.inline-flex.px-1.py-1 class=Satis.config.icons[:previous_month]
16
14
  div.text-center
17
15
  span.text-lg.font-bold.text-gray-800.dark:text-gray-200 data-satis-date-time-picker-target="month"
18
- span.ml-1.text-lg.text-gray-600.dark:text-gray-200.font-normal data-satis-date-time-picker-target="year"
16
+ select.appearance-none.border-none.bg-transparent.text-lg.font-normal.ml-1.text-gray-600.dark:text-gray-200.w-32.focus:outline-none.focus:ring-0 style="max-height: 50px; overflow-y:auto;" data-satis-date-time-picker-target="select" data-action="change->satis-date-time-picker#selectYear"
17
+ - current_year = Time.current.year
18
+ - (current_year - 100..current_year + 100).each do |year|
19
+ option value=year data-satis-date-time-picker-target="year" = year
19
20
  div
20
21
  button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#nextMonth"
21
22
  i.text-gray-500.inline-flex.px-1.py-1 class=Satis.config.icons[:next_month]
22
- button type="button" class="transition ease-in-out duration-100 inline-flex cursor-pointer hover:bg-gray-200 rounded-full" data-action="satis-date-time-picker#nextYear"
23
- i.text-gray-500.inline-flex.py-1 class=Satis.config.icons[:next_year]
24
-
25
23
 
26
24
  .grid.grid-cols-7 data-satis-date-time-picker-target="weekDays"
27
25
  template data-satis-date-time-picker-target="weekDayTemplate"
@@ -164,21 +164,17 @@ export default class DateTimePickerComponentController extends ApplicationContro
164
164
 
165
165
  previousMonth(event) {
166
166
  this.displayValue = new Date(new Date(this.displayValue).setMonth(this.displayValue.getMonth() - 1))
167
+
168
+ this.updateYear()
169
+
167
170
  this.refreshCalendar(false)
168
171
  }
169
172
 
170
173
  nextMonth(event) {
171
174
  this.displayValue = new Date(new Date(this.displayValue).setMonth(this.displayValue.getMonth() + 1))
172
- this.refreshCalendar(false)
173
- }
174
175
 
175
- previousYear(event) {
176
- this.displayValue = new Date(new Date(this.displayValue).setFullYear(this.displayValue.getFullYear() - 1))
177
- this.refreshCalendar(false)
178
- }
176
+ this.updateYear()
179
177
 
180
- nextYear(event) {
181
- this.displayValue = new Date(new Date(this.displayValue).setFullYear(this.displayValue.getFullYear() + 1))
182
178
  this.refreshCalendar(false)
183
179
  }
184
180
 
@@ -328,13 +324,17 @@ export default class DateTimePickerComponentController extends ApplicationContro
328
324
  return false
329
325
  }
330
326
  selectedDate.setMonth(this.displayValue.getMonth() - 1)
327
+ this.displayValue = selectedDate
331
328
  } else if (dayType === "next") {
332
329
  if (this.rangeValue) {
333
330
  return false
334
331
  }
335
332
  selectedDate.setMonth(this.displayValue.getMonth() + 1)
333
+ this.displayValue = selectedDate
336
334
  }
337
335
 
336
+ this.updateYear()
337
+
338
338
  selectedDate.setDate(+event.target.innerText)
339
339
 
340
340
  if (!this.rangeValue && !this.multipleValue) {
@@ -559,7 +559,6 @@ export default class DateTimePickerComponentController extends ApplicationContro
559
559
  })
560
560
  }
561
561
  }
562
-
563
562
  // Format the given Date into an ISO8601 string whilst preserving the given timezone
564
563
  iso8601(date) {
565
564
  let tzo = -date.getTimezoneOffset(),
@@ -667,4 +666,25 @@ export default class DateTimePickerComponentController extends ApplicationContro
667
666
 
668
667
  return results
669
668
  }
669
+
670
+ selectYear(event) {
671
+ let selectedYear = Number(event.target.value)
672
+ this.displayValue.setFullYear(selectedYear)
673
+ this.refreshCalendar(false)
674
+
675
+ if (!this.rangeValue && !this.multipleValue) {
676
+ this.selectedValue[0] = new Date(this.displayValue)
677
+ } else if(this.rangeValue && this.selectedValue.length == 2){
678
+ return false
679
+ }
680
+
681
+ this.refreshInputs()
682
+ event.cancelBubble = true
683
+ }
684
+
685
+ updateYear() {
686
+ const yearSelect = document.querySelector('[data-satis-date-time-picker-target="select"]')
687
+ const newYear = this.displayValue.getFullYear()
688
+ yearSelect.value = newYear
689
+ }
670
690
  }
@@ -0,0 +1,8 @@
1
+ div data-controller="satis-signature" data-satis-signature-url-value=(form.object.send(attribute).attached? ? helpers.main_app.url_for(form.object.send(attribute)) : nil)
2
+ canvas.border-solid.border.rounded-md data-satis-signature-target="canvas" data-satis-signature-action="change->satis-signature#update" width="600" height="200"
3
+ .button
4
+ i.fal.fa-trash data-action='click->satis-signature#clear' class='cursor-pointer'
5
+ |<
6
+ .button
7
+ i.fal.fa-undo data-action='click->satis-signature#undo' class='cursor-pointer'
8
+ = form.file_field :signature, data: { "satis-signature-target" => 'file' }, class: 'hidden'
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Satis
4
+ module Signature
5
+ class Component < ViewComponent::Base
6
+ attr_reader :url, :form, :attribute, :title, :options
7
+
8
+ def initialize(form:, attribute:, **options, &block)
9
+ @form = form
10
+ @attribute = attribute
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,74 @@
1
+ import ApplicationController from "satis/controllers/application_controller"
2
+
3
+ import SignaturePad from "signature_pad"
4
+
5
+ export default class SignatureComponentController extends ApplicationController {
6
+ static targets = ["canvas", "file"]
7
+ static values = { url: String }
8
+
9
+ async connect() {
10
+ this.signaturePad = new SignaturePad(this.canvasTarget)
11
+ if (this.hasUrlValue) {
12
+ let blob = await fetch(this.urlValue)
13
+ .then(r => r.blob())
14
+
15
+
16
+ let dataUrl = await new Promise(resolve => {
17
+ let reader = new FileReader()
18
+ reader.onload = () => resolve(reader.result)
19
+ reader.readAsDataURL(blob)
20
+ })
21
+ const dpi = window.devicePixelRatio
22
+ window.devicePixelRatio = 1
23
+ this.signaturePad.fromDataURL(dataUrl)
24
+ const file = this.dataURLtoFile(dataUrl, "signature.svg")
25
+ window.devicePixelRatio = dpi
26
+ this.signaturePad.addEventListener("beginStroke", this.clear.bind(this), { once: true })
27
+ this.persistFile(file)
28
+ }
29
+ this.signaturePad.addEventListener("endStroke", this.persist.bind(this))
30
+ }
31
+
32
+ clear(event) {
33
+ this.signaturePad.clear()
34
+ this.fileTarget.files = new DataTransfer().files
35
+ }
36
+
37
+ undo(event) {
38
+ event.preventDefault()
39
+ const data = this.signaturePad.toData()
40
+ if (data) {
41
+ data.pop()
42
+ this.signaturePad.fromData(data)
43
+ }
44
+ }
45
+
46
+ persist(event) {
47
+ const dpi = window.devicePixelRatio
48
+ const dataUrl = this.signaturePad.toSVG()
49
+ window.devicePixelRatio = 1
50
+ const file = new File([this.signaturePad?.toSVG()], "signature.svg", { type: "image/svg+xml" })
51
+ window.devicePixelRatio = dpi
52
+ this.persistFile(file)
53
+ }
54
+
55
+ persistFile(file) {
56
+ const dt = new DataTransfer()
57
+ dt.items.add(file)
58
+ this.fileTarget.files = dt.files
59
+ }
60
+
61
+ // HELPER
62
+
63
+ dataURLtoFile(dataurl, filename) {
64
+ var arr = dataurl.split(","),
65
+ mime = arr[0].match(/:(.*?);/)[1],
66
+ bstr = atob(arr[arr.length - 1]),
67
+ n = bstr.length,
68
+ u8arr = new Uint8Array(n);
69
+ while (n--) {
70
+ u8arr[n] = bstr.charCodeAt(n);
71
+ }
72
+ return new File([u8arr], filename, { type: mime });
73
+ }
74
+ }
@@ -48,6 +48,9 @@ application.register("satis-dialog", DialogComponentController);
48
48
  import ColorPickerComponentController from "satis/components/color_picker/component_controller";
49
49
  application.register("satis-color-picker", ColorPickerComponentController);
50
50
 
51
+ import SignatureComponentController from "satis/components/signature/component_controller";
52
+ application.register("satis-signature", SignatureComponentController);
53
+
51
54
  // Controllers
52
55
 
53
56
  import LinkController from "satis/controllers/link_controller";
data/config/importmap.rb CHANGED
@@ -52,3 +52,4 @@ pin "dayjs" # @1.11.13
52
52
  pin "dayjs/plugin/customParseFormat", to: "dayjs--plugin--customParseFormat.js" # @1.11.13
53
53
  pin "dayjs/plugin/localizedFormat", to: "dayjs--plugin--localizedFormat.js" # @1.11.13
54
54
  pin "dayjs/plugin/utc", to: "dayjs--plugin--utc.js" # @1.11.13
55
+ pin "signature_pad" # @5.0.4
@@ -34,10 +34,8 @@ module Satis
34
34
  option :current_user, default: lambda {}
35
35
 
36
36
  option :icons, default: {
37
- previous_year: "fa-solid fa-angle-double-left",
38
37
  previous_month: "fa-solid fa-angle-left",
39
- next_month: "fa-solid fa-angle-right",
40
- next_year: "fa-solid fa-angle-double-right"
38
+ next_month: "fa-solid fa-angle-right"
41
39
  }
42
40
 
43
41
  option(:default_help_text, default: lambda do |template, object, key, additional_scope|
@@ -328,31 +328,15 @@ module Satis
328
328
  @template.render(Satis::Editor::Component.new(form: self, attribute: method, **options, &block))
329
329
  ]
330
330
  end
331
- # form_group(method, options) do
332
- # safe_join [
333
- # (custom_label(method, options[:label], options) unless options[:label] == false),
334
- # tag.div(text_area(method,
335
- # merge_input_options({
336
- # class: 'form-control hidden',
337
- # data: {
338
- # # controller: 'satis-editor',
339
- # 'satis-editor-target' => 'textarea',
340
- # 'satis-editor-read-only-value' => options.delete(:read_only) || false,
341
- # 'satis-editor-mode-value' => options.delete(:mode) || 'text/html',
342
- # 'satis-editor-height-value' => options.delete(:height) || '200px',
343
- # 'satis-editor-color-scheme-value' => options.delete(:color_scheme),
344
- # 'satis-editor-color-scheme-dark-value' => options.delete(:color_scheme_dark) || 'lucario'
345
- # }
346
- #
347
- # }, options[:input_html])), class: "editor #{
348
- # if has_error?(method)
349
- # 'is-invalid'
350
- # end}", data: {
351
- # controller: 'satis-editor'
352
- # }),
353
- # hint_text(options[:hint] || '⌘-F/⌃-f: search; ⌥-g: goto line, ⌃-space: autocomplete')
354
- # ]
355
- # end
331
+ end
332
+
333
+ def signature(method, options = {}, &block)
334
+ form_group(method, options) do
335
+ safe_join [
336
+ (custom_label(method, options[:label], options) unless options[:label] == false),
337
+ @template.render(Satis::Signature::Component.new(form: self, attribute: method, **options, &block))
338
+ ]
339
+ end
356
340
  end
357
341
 
358
342
  def boolean_input(method, options = {})
data/lib/satis/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Satis
2
- VERSION = "2.1.53"
2
+ VERSION = "2.1.54"
3
3
  end
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
3
+ * Original file: /npm/signature_pad@5.0.4/dist/signature_pad.js
4
+ *
5
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
+ */
7
+ /*!
8
+ * Signature Pad v5.0.4 | https://github.com/szimek/signature_pad
9
+ * (c) 2024 Szymon Nowak | Released under the MIT license
10
+ */
11
+ class t { constructor(t, e, i, n) { if (isNaN(t) || isNaN(e)) throw new Error(`Point is invalid: (${t}, ${e})`); this.x = +t, this.y = +e, this.pressure = i || 0, this.time = n || Date.now() } distanceTo(t) { return Math.sqrt(Math.pow(this.x - t.x, 2) + Math.pow(this.y - t.y, 2)) } equals(t) { return this.x === t.x && this.y === t.y && this.pressure === t.pressure && this.time === t.time } velocityFrom(t) { return this.time !== t.time ? this.distanceTo(t) / (this.time - t.time) : 0 } } class e { static fromPoints(t, i) { const n = this.calculateControlPoints(t[0], t[1], t[2]).c2, s = this.calculateControlPoints(t[1], t[2], t[3]).c1; return new e(t[1], n, s, t[2], i.start, i.end) } static calculateControlPoints(e, i, n) { const s = e.x - i.x, o = e.y - i.y, r = i.x - n.x, h = i.y - n.y, a = (e.x + i.x) / 2, c = (e.y + i.y) / 2, d = (i.x + n.x) / 2, l = (i.y + n.y) / 2, u = Math.sqrt(s * s + o * o), v = Math.sqrt(r * r + h * h), _ = u + v == 0 ? 0 : v / (u + v), p = d + (a - d) * _, m = l + (c - l) * _, g = i.x - p, w = i.y - m; return { c1: new t(a + g, c + w), c2: new t(d + g, l + w) } } constructor(t, e, i, n, s, o) { this.startPoint = t, this.control2 = e, this.control1 = i, this.endPoint = n, this.startWidth = s, this.endWidth = o } length() { let t, e, i = 0; for (let n = 0; n <= 10; n += 1) { const s = n / 10, o = this.point(s, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x), r = this.point(s, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y); if (n > 0) { const n = o - t, s = r - e; i += Math.sqrt(n * n + s * s) } t = o, e = r } return i } point(t, e, i, n, s) { return e * (1 - t) * (1 - t) * (1 - t) + 3 * i * (1 - t) * (1 - t) * t + 3 * n * (1 - t) * t * t + s * t * t * t } } class i { constructor() { try { this._et = new EventTarget } catch (t) { this._et = document } } addEventListener(t, e, i) { this._et.addEventListener(t, e, i) } dispatchEvent(t) { return this._et.dispatchEvent(t) } removeEventListener(t, e, i) { this._et.removeEventListener(t, e, i) } } class n extends i { constructor(t, e = {}) { var i, s, o; super(), this.canvas = t, this._drawingStroke = !1, this._isEmpty = !0, this._lastPoints = [], this._data = [], this._lastVelocity = 0, this._lastWidth = 0, this._handleMouseDown = t => { this._isLeftButtonPressed(t, !0) && !this._drawingStroke && this._strokeBegin(this._pointerEventToSignatureEvent(t)) }, this._handleMouseMove = t => { this._isLeftButtonPressed(t, !0) && this._drawingStroke ? this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t)) : this._strokeEnd(this._pointerEventToSignatureEvent(t), !1) }, this._handleMouseUp = t => { this._isLeftButtonPressed(t) || this._strokeEnd(this._pointerEventToSignatureEvent(t)) }, this._handleTouchStart = t => { 1 !== t.targetTouches.length || this._drawingStroke || (t.cancelable && t.preventDefault(), this._strokeBegin(this._touchEventToSignatureEvent(t))) }, this._handleTouchMove = t => { 1 === t.targetTouches.length && (t.cancelable && t.preventDefault(), this._drawingStroke ? this._strokeMoveUpdate(this._touchEventToSignatureEvent(t)) : this._strokeEnd(this._touchEventToSignatureEvent(t), !1)) }, this._handleTouchEnd = t => { 0 === t.targetTouches.length && (t.cancelable && t.preventDefault(), this.canvas.removeEventListener("touchmove", this._handleTouchMove), this._strokeEnd(this._touchEventToSignatureEvent(t))) }, this._handlePointerDown = t => { t.isPrimary && this._isLeftButtonPressed(t) && !this._drawingStroke && (t.preventDefault(), this._strokeBegin(this._pointerEventToSignatureEvent(t))) }, this._handlePointerMove = t => { t.isPrimary && (this._isLeftButtonPressed(t, !0) && this._drawingStroke ? (t.preventDefault(), this._strokeMoveUpdate(this._pointerEventToSignatureEvent(t))) : this._strokeEnd(this._pointerEventToSignatureEvent(t), !1)) }, this._handlePointerUp = t => { t.isPrimary && !this._isLeftButtonPressed(t) && (t.preventDefault(), this._strokeEnd(this._pointerEventToSignatureEvent(t))) }, this.velocityFilterWeight = e.velocityFilterWeight || .7, this.minWidth = e.minWidth || .5, this.maxWidth = e.maxWidth || 2.5, this.throttle = null !== (i = e.throttle) && void 0 !== i ? i : 16, this.minDistance = null !== (s = e.minDistance) && void 0 !== s ? s : 5, this.dotSize = e.dotSize || 0, this.penColor = e.penColor || "black", this.backgroundColor = e.backgroundColor || "rgba(0,0,0,0)", this.compositeOperation = e.compositeOperation || "source-over", this.canvasContextOptions = null !== (o = e.canvasContextOptions) && void 0 !== o ? o : {}, this._strokeMoveUpdate = this.throttle ? function (t, e = 250) { let i, n, s, o = 0, r = null; const h = () => { o = Date.now(), r = null, i = t.apply(n, s), r || (n = null, s = []) }; return function (...a) { const c = Date.now(), d = e - (c - o); return n = this, s = a, d <= 0 || d > e ? (r && (clearTimeout(r), r = null), o = c, i = t.apply(n, s), r || (n = null, s = [])) : r || (r = window.setTimeout(h, d)), i } }(n.prototype._strokeUpdate, this.throttle) : n.prototype._strokeUpdate, this._ctx = t.getContext("2d", this.canvasContextOptions), this.clear(), this.on() } clear() { const { _ctx: t, canvas: e } = this; t.fillStyle = this.backgroundColor, t.clearRect(0, 0, e.width, e.height), t.fillRect(0, 0, e.width, e.height), this._data = [], this._reset(this._getPointGroupOptions()), this._isEmpty = !0 } fromDataURL(t, e = {}) { return new Promise(((i, n) => { const s = new Image, o = e.ratio || window.devicePixelRatio || 1, r = e.width || this.canvas.width / o, h = e.height || this.canvas.height / o, a = e.xOffset || 0, c = e.yOffset || 0; this._reset(this._getPointGroupOptions()), s.onload = () => { this._ctx.drawImage(s, a, c, r, h), i() }, s.onerror = t => { n(t) }, s.crossOrigin = "anonymous", s.src = t, this._isEmpty = !1 })) } toDataURL(t = "image/png", e) { return "image/svg+xml" === t ? ("object" != typeof e && (e = void 0), `data:image/svg+xml;base64,${btoa(this.toSVG(e))}`) : ("number" != typeof e && (e = void 0), this.canvas.toDataURL(t, e)) } on() { this.canvas.style.touchAction = "none", this.canvas.style.msTouchAction = "none", this.canvas.style.userSelect = "none"; const t = /Macintosh/.test(navigator.userAgent) && "ontouchstart" in document; window.PointerEvent && !t ? this._handlePointerEvents() : (this._handleMouseEvents(), "ontouchstart" in window && this._handleTouchEvents()) } off() { this.canvas.style.touchAction = "auto", this.canvas.style.msTouchAction = "auto", this.canvas.style.userSelect = "auto", this.canvas.removeEventListener("pointerdown", this._handlePointerDown), this.canvas.removeEventListener("mousedown", this._handleMouseDown), this.canvas.removeEventListener("touchstart", this._handleTouchStart), this._removeMoveUpEventListeners() } _getListenerFunctions() { var t; const e = window.document === this.canvas.ownerDocument ? window : null !== (t = this.canvas.ownerDocument.defaultView) && void 0 !== t ? t : this.canvas.ownerDocument; return { addEventListener: e.addEventListener.bind(e), removeEventListener: e.removeEventListener.bind(e) } } _removeMoveUpEventListeners() { const { removeEventListener: t } = this._getListenerFunctions(); t("pointermove", this._handlePointerMove), t("pointerup", this._handlePointerUp), t("mousemove", this._handleMouseMove), t("mouseup", this._handleMouseUp), t("touchmove", this._handleTouchMove), t("touchend", this._handleTouchEnd) } isEmpty() { return this._isEmpty } fromData(t, { clear: e = !0 } = {}) { e && this.clear(), this._fromData(t, this._drawCurve.bind(this), this._drawDot.bind(this)), this._data = this._data.concat(t) } toData() { return this._data } _isLeftButtonPressed(t, e) { return e ? 1 === t.buttons : 1 == (1 & t.buttons) } _pointerEventToSignatureEvent(t) { return { event: t, type: t.type, x: t.clientX, y: t.clientY, pressure: "pressure" in t ? t.pressure : 0 } } _touchEventToSignatureEvent(t) { const e = t.changedTouches[0]; return { event: t, type: t.type, x: e.clientX, y: e.clientY, pressure: e.force } } _getPointGroupOptions(t) { return { penColor: t && "penColor" in t ? t.penColor : this.penColor, dotSize: t && "dotSize" in t ? t.dotSize : this.dotSize, minWidth: t && "minWidth" in t ? t.minWidth : this.minWidth, maxWidth: t && "maxWidth" in t ? t.maxWidth : this.maxWidth, velocityFilterWeight: t && "velocityFilterWeight" in t ? t.velocityFilterWeight : this.velocityFilterWeight, compositeOperation: t && "compositeOperation" in t ? t.compositeOperation : this.compositeOperation } } _strokeBegin(t) { if (!this.dispatchEvent(new CustomEvent("beginStroke", { detail: t, cancelable: !0 }))) return; const { addEventListener: e } = this._getListenerFunctions(); switch (t.event.type) { case "mousedown": e("mousemove", this._handleMouseMove), e("mouseup", this._handleMouseUp); break; case "touchstart": e("touchmove", this._handleTouchMove), e("touchend", this._handleTouchEnd); break; case "pointerdown": e("pointermove", this._handlePointerMove), e("pointerup", this._handlePointerUp) }this._drawingStroke = !0; const i = this._getPointGroupOptions(), n = Object.assign(Object.assign({}, i), { points: [] }); this._data.push(n), this._reset(i), this._strokeUpdate(t) } _strokeUpdate(t) { if (!this._drawingStroke) return; if (0 === this._data.length) return void this._strokeBegin(t); this.dispatchEvent(new CustomEvent("beforeUpdateStroke", { detail: t })); const e = this._createPoint(t.x, t.y, t.pressure), i = this._data[this._data.length - 1], n = i.points, s = n.length > 0 && n[n.length - 1], o = !!s && e.distanceTo(s) <= this.minDistance, r = this._getPointGroupOptions(i); if (!s || !s || !o) { const t = this._addPoint(e, r); s ? t && this._drawCurve(t, r) : this._drawDot(e, r), n.push({ time: e.time, x: e.x, y: e.y, pressure: e.pressure }) } this.dispatchEvent(new CustomEvent("afterUpdateStroke", { detail: t })) } _strokeEnd(t, e = !0) { this._removeMoveUpEventListeners(), this._drawingStroke && (e && this._strokeUpdate(t), this._drawingStroke = !1, this.dispatchEvent(new CustomEvent("endStroke", { detail: t }))) } _handlePointerEvents() { this._drawingStroke = !1, this.canvas.addEventListener("pointerdown", this._handlePointerDown) } _handleMouseEvents() { this._drawingStroke = !1, this.canvas.addEventListener("mousedown", this._handleMouseDown) } _handleTouchEvents() { this.canvas.addEventListener("touchstart", this._handleTouchStart) } _reset(t) { this._lastPoints = [], this._lastVelocity = 0, this._lastWidth = (t.minWidth + t.maxWidth) / 2, this._ctx.fillStyle = t.penColor, this._ctx.globalCompositeOperation = t.compositeOperation } _createPoint(e, i, n) { const s = this.canvas.getBoundingClientRect(); return new t(e - s.left, i - s.top, n, (new Date).getTime()) } _addPoint(t, i) { const { _lastPoints: n } = this; if (n.push(t), n.length > 2) { 3 === n.length && n.unshift(n[0]); const t = this._calculateCurveWidths(n[1], n[2], i), s = e.fromPoints(n, t); return n.shift(), s } return null } _calculateCurveWidths(t, e, i) { const n = i.velocityFilterWeight * e.velocityFrom(t) + (1 - i.velocityFilterWeight) * this._lastVelocity, s = this._strokeWidth(n, i), o = { end: s, start: this._lastWidth }; return this._lastVelocity = n, this._lastWidth = s, o } _strokeWidth(t, e) { return Math.max(e.maxWidth / (t + 1), e.minWidth) } _drawCurveSegment(t, e, i) { const n = this._ctx; n.moveTo(t, e), n.arc(t, e, i, 0, 2 * Math.PI, !1), this._isEmpty = !1 } _drawCurve(t, e) { const i = this._ctx, n = t.endWidth - t.startWidth, s = 2 * Math.ceil(t.length()); i.beginPath(), i.fillStyle = e.penColor; for (let i = 0; i < s; i += 1) { const o = i / s, r = o * o, h = r * o, a = 1 - o, c = a * a, d = c * a; let l = d * t.startPoint.x; l += 3 * c * o * t.control1.x, l += 3 * a * r * t.control2.x, l += h * t.endPoint.x; let u = d * t.startPoint.y; u += 3 * c * o * t.control1.y, u += 3 * a * r * t.control2.y, u += h * t.endPoint.y; const v = Math.min(t.startWidth + h * n, e.maxWidth); this._drawCurveSegment(l, u, v) } i.closePath(), i.fill() } _drawDot(t, e) { const i = this._ctx, n = e.dotSize > 0 ? e.dotSize : (e.minWidth + e.maxWidth) / 2; i.beginPath(), this._drawCurveSegment(t.x, t.y, n), i.closePath(), i.fillStyle = e.penColor, i.fill() } _fromData(e, i, n) { for (const s of e) { const { points: e } = s, o = this._getPointGroupOptions(s); if (e.length > 1) for (let n = 0; n < e.length; n += 1) { const s = e[n], r = new t(s.x, s.y, s.pressure, s.time); 0 === n && this._reset(o); const h = this._addPoint(r, o); h && i(h, o) } else this._reset(o), n(e[0], o) } } toSVG({ includeBackgroundColor: t = !1 } = {}) { const e = this._data, i = Math.max(window.devicePixelRatio || 1, 1), n = this.canvas.width / i, s = this.canvas.height / i, o = document.createElementNS("http://www.w3.org/2000/svg", "svg"); if (o.setAttribute("xmlns", "http://www.w3.org/2000/svg"), o.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"), o.setAttribute("viewBox", `0 0 ${n} ${s}`), o.setAttribute("width", n.toString()), o.setAttribute("height", s.toString()), t && this.backgroundColor) { const t = document.createElement("rect"); t.setAttribute("width", "100%"), t.setAttribute("height", "100%"), t.setAttribute("fill", this.backgroundColor), o.appendChild(t) } return this._fromData(e, ((t, { penColor: e }) => { const i = document.createElement("path"); if (!(isNaN(t.control1.x) || isNaN(t.control1.y) || isNaN(t.control2.x) || isNaN(t.control2.y))) { const n = `M ${t.startPoint.x.toFixed(3)},${t.startPoint.y.toFixed(3)} C ${t.control1.x.toFixed(3)},${t.control1.y.toFixed(3)} ${t.control2.x.toFixed(3)},${t.control2.y.toFixed(3)} ${t.endPoint.x.toFixed(3)},${t.endPoint.y.toFixed(3)}`; i.setAttribute("d", n), i.setAttribute("stroke-width", (2.25 * t.endWidth).toFixed(3)), i.setAttribute("stroke", e), i.setAttribute("fill", "none"), i.setAttribute("stroke-linecap", "round"), o.appendChild(i) } }), ((t, { penColor: e, dotSize: i, minWidth: n, maxWidth: s }) => { const r = document.createElement("circle"), h = i > 0 ? i : (n + s) / 2; r.setAttribute("r", h.toString()), r.setAttribute("cx", t.x.toString()), r.setAttribute("cy", t.y.toString()), r.setAttribute("fill", e), o.appendChild(r) })), o.outerHTML } } export { n as default };
12
+ //# sourceMappingURL=/sm/fcb20b2f86ed10615b8b8c6a0c684f42d4284af51d740bacacf53d4ccdbe767b.map
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: satis
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.53
4
+ version: 2.1.54
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom de Grunt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-24 00:00:00.000000000 Z
11
+ date: 2025-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: browser
@@ -847,6 +847,9 @@ files:
847
847
  - app/components/satis/sidebar_menu_item/component_controller.js
848
848
  - app/components/satis/sidebar_menu_item/mobile/component.html.slim
849
849
  - app/components/satis/sidebar_menu_item/mobile/component.rb
850
+ - app/components/satis/signature/component.html.slim
851
+ - app/components/satis/signature/component.rb
852
+ - app/components/satis/signature/component_controller.js
850
853
  - app/components/satis/switch/component.html.slim
851
854
  - app/components/satis/switch/component.rb
852
855
  - app/components/satis/switch/component_controller.js
@@ -968,6 +971,7 @@ files:
968
971
  - vendor/javascript/leaflet.js
969
972
  - vendor/javascript/pickr.es5.min.js
970
973
  - vendor/javascript/popper.js.js
974
+ - vendor/javascript/signature_pad.js
971
975
  - vendor/javascript/sortablejs.js
972
976
  - vendor/javascript/style-mod.js
973
977
  - vendor/javascript/tippy.js.js