@websy/websy-designs 1.10.7 → 1.11.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.
@@ -6,6 +6,7 @@
6
6
  WebsyPubSub
7
7
  WebsyForm
8
8
  MultiForm
9
+ MediaUpload
9
10
  WebsyDatePicker
10
11
  WebsyDropdown
11
12
  WebsyRouter
@@ -1835,6 +1836,7 @@ class WebsyDropdown {
1835
1836
  const headerPos = WebsyUtils.getElementPos(headerEl)
1836
1837
  const contentPos = WebsyUtils.getElementPos(contentEl)
1837
1838
  if (this.options.style === 'plain' && headerPos.width > 0 && headerPos.height > 0) {
1839
+ contentEl.style.left = 'unset'
1838
1840
  contentEl.style.right = `calc(100vw - ${headerPos.right}px)`
1839
1841
  contentEl.style.width = `${Math.max(this.options.minWidth, headerEl.clientWidth)}px`
1840
1842
  if (headerPos.bottom + contentPos.height > window.innerHeight) {
@@ -1847,6 +1849,7 @@ class WebsyDropdown {
1847
1849
  }
1848
1850
  else if (this.options.style === 'plain' && headerPos.width === 0 && headerPos.height === 0) {
1849
1851
  const targetPos = WebsyUtils.getElementPos(event.target)
1852
+ contentEl.style.left = 'unset'
1850
1853
  contentEl.style.right = `calc(100vw - ${targetPos.right}px)`
1851
1854
  contentEl.style.width = `${Math.max(this.options.minWidth, targetPos.width)}px`
1852
1855
  }
@@ -2690,7 +2693,7 @@ class WebsyForm {
2690
2693
  if (f.component) {
2691
2694
  componentsToProcess.push(f)
2692
2695
  html += `
2693
- ${i > 0 ? '-->' : ''}<div id='${this.elementId}_${f.field}_inputContainer' style='${f.style || ''}' class='websy-input-container ${f.classes ? f.classes.join(' ') : ''}'>
2696
+ ${i > 0 ? '-->' : ''}<div id='${this.elementId}_${f.field}_inputContainer' style='${f.style || ''}' class='websy-input-container ${f.classes ? f.classes.join(' ') : ''} ${f.component === 'MediaUpload' ? 'media-upload' : ''}'>
2694
2697
  ${f.label ? `<label for="${f.field}">${f.label}</label>` : ''}${f.required === true ? '<span class="websy-form-required-value">*</span>' : ''}
2695
2698
  <div id='${this.elementId}_input_${f.field}_component' class='form-component'></div>
2696
2699
  <span id='${this.elementId}_${f.field}_error' class='websy-form-validation-error'></span>
@@ -2710,7 +2713,7 @@ class WebsyForm {
2710
2713
  name="${f.field}"
2711
2714
  ${(f.attributes || []).join(' ')}
2712
2715
  class="websy-input websy-textarea"
2713
- ></textarea>
2716
+ >${f.value || ''}</textarea>
2714
2717
  <span id='${this.elementId}_${f.field}_error' class='websy-form-validation-error'></span>
2715
2718
  </div><!--
2716
2719
  `
@@ -2729,7 +2732,7 @@ class WebsyForm {
2729
2732
  ${(f.attributes || []).join(' ')}
2730
2733
  name="${f.field}"
2731
2734
  placeholder="${f.placeholder || ''}"
2732
- value="${f.value || ''}"
2735
+ value="${f.type === 'date' ? '' : f.value || ''}"
2733
2736
  valueAsDate="${f.type === 'date' ? f.value : ''}"
2734
2737
  oninvalidx="this.setCustomValidity('${f.invalidMessage || 'Please fill in this field.'}')"
2735
2738
  />
@@ -2777,6 +2780,9 @@ class WebsyForm {
2777
2780
  if (this.fieldMap[field].type === 'checkbox') {
2778
2781
  el.checked = value
2779
2782
  }
2783
+ if (this.fieldMap[field].type === 'date') {
2784
+ el.valueAsDate = value
2785
+ }
2780
2786
  }
2781
2787
  else {
2782
2788
  console.error(`Input for ${field} does not exist in form.`)
@@ -2921,10 +2927,12 @@ class MultiForm {
2921
2927
  constructor (elementId, options) {
2922
2928
  this.elementId = elementId
2923
2929
  const DEFAULTS = {
2924
- addButton: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="256" y1="112" x2="256" y2="400" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="400" y1="256" x2="112" y2="256" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2925
- deleteButton: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="368" y1="368" x2="144" y2="144" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="368" y1="144" x2="144" y2="368" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2930
+ addIcon: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="256" y1="112" x2="256" y2="400" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="400" y1="256" x2="112" y2="256" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2931
+ deleteIcon: `<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 512 512"><line x1="368" y1="368" x2="144" y2="144" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/><line x1="368" y1="144" x2="144" y2="368" style="fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:32px"/></svg>`,
2926
2932
  allowAdd: true,
2927
- allowDelete: true
2933
+ allowDelete: true,
2934
+ addLabel: '',
2935
+ deleteLabel: ''
2928
2936
  }
2929
2937
  this.options = Object.assign({}, DEFAULTS, options)
2930
2938
  this.formData = []
@@ -2933,7 +2941,12 @@ class MultiForm {
2933
2941
  const el = document.getElementById(elementId)
2934
2942
  if (el) {
2935
2943
  el.addEventListener('click', this.handleClick.bind(this))
2936
- el.innerHTML = `<div id='${elementId}_container' class='websy-multi-form-container'></div>`
2944
+ el.innerHTML = `
2945
+ <div id='${elementId}_container' class='websy-multi-form-container'></div>
2946
+ <button id='${this.elementId}_addButton' class='websy-multi-form-add'>
2947
+ ${this.options.addIcon}${this.options.addLabel}
2948
+ </button>
2949
+ `
2937
2950
  }
2938
2951
  this.render()
2939
2952
  }
@@ -2942,24 +2955,28 @@ class MultiForm {
2942
2955
  this.render()
2943
2956
  }
2944
2957
  addEntry () {
2945
- const el = document.getElementById(`${this.elementId}_container`)
2946
- let newId = WebsyDesigns.Utils.createIdentity()
2947
- const newFormEl = document.createElement('div')
2948
- newFormEl.id = `${this.elementId}_${newId}_formContainer`
2949
- newFormEl.classList.add('websy-multi-form-form-container')
2950
- newFormEl.innerHTML = `
2951
- <div id='${this.elementId}_${newId}_form' class='websy-multi-form-form'>
2952
- </div>
2953
- <button id='${this.elementId}_${newId}_deleteButton' data-formid='${newId}' class='hidden websy-multi-form-delete'>
2954
- ${this.options.deleteButton}
2955
- </button>
2956
- <button id='${this.elementId}_${newId}_addButton' data-formid='${newId}' class='websy-multi-form-add'>
2957
- ${this.options.addButton}
2958
- </button>
2959
- `
2960
- el.appendChild(newFormEl)
2961
- let formOptions = Object.assign({}, this.options, { fields: [...this.options.fields.map(f => Object.assign({}, f))] })
2962
- this.forms.push(new WebsyDesigns.Form(`${this.elementId}_${newId}_form`, formOptions))
2958
+ const addEl = document.getElementById(`${this.elementId}_addButton`)
2959
+ if (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) {
2960
+ const el = document.getElementById(`${this.elementId}_container`)
2961
+ let newId = WebsyDesigns.Utils.createIdentity()
2962
+ const newFormEl = document.createElement('div')
2963
+ newFormEl.id = `${this.elementId}_${newId}_formContainer`
2964
+ newFormEl.classList.add('websy-multi-form-form-container')
2965
+ let html = `
2966
+ <div id='${this.elementId}_${newId}_form' class='websy-multi-form-form'>
2967
+ </div>
2968
+ <button id='${this.elementId}_${newId}_deleteButton' data-formid='${newId}' class='websy-multi-form-delete'>
2969
+ ${this.options.deleteIcon}${this.options.deleteLabel}
2970
+ </button>
2971
+ `
2972
+ newFormEl.innerHTML = html
2973
+ el.appendChild(newFormEl)
2974
+ let formOptions = Object.assign({}, this.options, { fields: [...this.options.fields.map(f => Object.assign({}, f))] })
2975
+ this.forms.push(new WebsyDesigns.Form(`${this.elementId}_${newId}_form`, formOptions))
2976
+ if (addEl) {
2977
+ addEl.style.display = this.forms.length < this.options.maxRows ? 'flex' : 'none'
2978
+ }
2979
+ }
2963
2980
  }
2964
2981
  clear () {
2965
2982
  this.formData = []
@@ -2971,12 +2988,7 @@ class MultiForm {
2971
2988
  }
2972
2989
  }
2973
2990
  get data () {
2974
- const d = this.forms.map(f => (f.data))
2975
- console.log('forms data', d)
2976
- if (this.options.allowAdd !== false) {
2977
- // we don't return the last form
2978
- d.pop()
2979
- }
2991
+ const d = this.forms.map(f => (f.data))
2980
2992
  return d
2981
2993
  }
2982
2994
  set data (d) {
@@ -2988,16 +3000,6 @@ class MultiForm {
2988
3000
  }
2989
3001
  handleClick (event) {
2990
3002
  if (event.target.classList.contains('websy-multi-form-add')) {
2991
- let id = event.target.getAttribute('data-formid')
2992
- // hide add button and show delete button
2993
- const addButtonEl = document.getElementById(`${this.elementId}_${id}_addButton`)
2994
- if (addButtonEl) {
2995
- addButtonEl.classList.add('hidden')
2996
- }
2997
- const deleteButtonEl = document.getElementById(`${this.elementId}_${id}_deleteButton`)
2998
- if (deleteButtonEl) {
2999
- deleteButtonEl.classList.remove('hidden')
3000
- }
3001
3003
  // add new form
3002
3004
  if (this.options.allowAdd === true) {
3003
3005
  this.addEntry()
@@ -3022,6 +3024,10 @@ class MultiForm {
3022
3024
  if (el) {
3023
3025
  el.remove()
3024
3026
  }
3027
+ const addEl = document.getElementById(`${this.elementId}_addButton`)
3028
+ if (addEl) {
3029
+ addEl.style.display = (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) ? 'flex' : 'none'
3030
+ }
3025
3031
  // delete form element based on id
3026
3032
  }
3027
3033
  }
@@ -3041,7 +3047,7 @@ class MultiForm {
3041
3047
  if (this.options.allowDelete === true) {
3042
3048
  html += `
3043
3049
  <button id='${this.elementId}_${d.formId}_deleteButton' data-formid='${d.formId}' data-rowid='${d.id}' class='websy-multi-form-delete'>
3044
- ${this.options.deleteButton}
3050
+ ${this.options.deleteIcon}${this.options.deleteLabel}
3045
3051
  </button>
3046
3052
  `
3047
3053
  }
@@ -3050,20 +3056,6 @@ class MultiForm {
3050
3056
  `
3051
3057
  })
3052
3058
  let id = WebsyDesigns.Utils.createIdentity()
3053
- if (this.options.allowAdd === true) {
3054
- html += `
3055
- <div id='${this.elementId}_${id}_formContainer' class='websy-multi-form-form-container'>
3056
- <div id='${this.elementId}_${id}_form' class='websy-multi-form-form'>
3057
- </div>
3058
- <button id='${this.elementId}_${id}_deleteButton' data-formid='${id}' class='hidden websy-multi-form-delete'>
3059
- ${this.options.deleteButton}
3060
- </button>
3061
- <button id='${this.elementId}_${id}_addButton' data-formid='${id}' class='websy-multi-form-add'>
3062
- ${this.options.addButton}
3063
- </button>
3064
- </div>
3065
- `
3066
- }
3067
3059
  el.innerHTML = html
3068
3060
  this.forms = new Array(this.formData.length)
3069
3061
  this.formData.forEach((d, i) => {
@@ -3072,11 +3064,15 @@ class MultiForm {
3072
3064
  formObject.data = d
3073
3065
  this.forms[i] = formObject
3074
3066
  })
3075
- if (this.options.allowAdd === true) {
3076
- let formOptions = Object.assign({}, this.options, { fields: [...this.options.fields.map(f => Object.assign({}, f))] })
3077
- let formObject = new WebsyDesigns.Form(`${this.elementId}_${id}_form`, formOptions)
3078
- this.forms.push(formObject)
3079
- }
3067
+ const addEl = document.getElementById(`${this.elementId}_addButton`)
3068
+ if (addEl) {
3069
+ if (this.options.allowAdd === true) {
3070
+ addEl.style.display = (typeof this.options.maxRows === 'undefined' || this.forms.length < this.options.maxRows) ? 'flex' : 'none'
3071
+ }
3072
+ else {
3073
+ addEl.style.display = 'none'
3074
+ }
3075
+ }
3080
3076
  }
3081
3077
  }
3082
3078
  validateForm () {
@@ -3714,6 +3710,181 @@ class WebsyNavigationMenu {
3714
3710
  }
3715
3711
  }
3716
3712
 
3713
+ /*
3714
+ global
3715
+ FormData
3716
+ FileReader
3717
+ Image
3718
+ */
3719
+ class MediaUpload {
3720
+ constructor (elementId, options) {
3721
+ const defaults = {
3722
+ allowMultiple: false,
3723
+ createThumbnail: false,
3724
+ thumbSize: 300,
3725
+ supportedTypes: ['image/png', 'image/jpg']
3726
+ }
3727
+ this.media = []
3728
+ this.options = Object.assign({}, defaults, options)
3729
+ this.elementId = elementId
3730
+ if (!elementId) {
3731
+ console.log('No element Id provided')
3732
+ return
3733
+ }
3734
+ const el = document.getElementById(elementId)
3735
+ if (el) {
3736
+ el.addEventListener('change', this.handleChange.bind(this))
3737
+ el.innerHTML = `
3738
+ <div class='websy-upload-form-container'>
3739
+ <span>Drag and drop a file or click to browse.</span>
3740
+ <form id="${this.elementId}_form" enctype="multipart/form-data">
3741
+ <input id="${this.elementId}_file" type="file" name="${this.options.name || 'media'}" accept="${this.options.supportedTypes.join(' ')}" multiple>
3742
+ </form>
3743
+ </div>
3744
+ <div id='${this.elementId}_uploaded' class='websy-uploaded-media'></div>
3745
+ `
3746
+ this.render()
3747
+ }
3748
+ }
3749
+ set data (d = []) {
3750
+ if (Array.isArray(d)) {
3751
+ this.media = d
3752
+ }
3753
+ else {
3754
+ this.media = [d]
3755
+ }
3756
+ this.render()
3757
+ }
3758
+ get data () {
3759
+ return this.media
3760
+ }
3761
+ createHtml (count) {
3762
+ let html = ''
3763
+ for (let i = 0; i < count; i++) {
3764
+ html += `
3765
+ <div>
3766
+ <img id='${this.elementId}_media_${i}'/>
3767
+ </div>
3768
+ `
3769
+ }
3770
+ return html
3771
+ }
3772
+ getForm () {
3773
+ let formFound = false
3774
+ let el = document.getElementById(`${this.elementId}_file`)
3775
+ if (el) {
3776
+ while (formFound === false && el.tagName !== 'BODY') {
3777
+ el = el.parentElement
3778
+ if (el.tagName === 'FORM') {
3779
+ formFound = true
3780
+ return el
3781
+ }
3782
+ }
3783
+ }
3784
+ return null
3785
+ }
3786
+ handleChange (event) {
3787
+ this.fileList = []
3788
+ let uploadForm = document.getElementById(`${this.elementId}_form`)
3789
+ if (!uploadForm) {
3790
+ uploadForm = this.getForm()
3791
+ }
3792
+ if (!uploadForm) {
3793
+ console.error(`The element ${this.elementId}_file does not belong to a form.`)
3794
+ return
3795
+ }
3796
+ const formData = new FormData(uploadForm)
3797
+ const html = this.createHtml(formData.length)
3798
+ formData.forEach((value, key) => {
3799
+ if (key === (this.options.name || 'media')) {
3800
+ this.fileList.push({ name: key, file: value })
3801
+ }
3802
+ })
3803
+ const resultEl = document.getElementById(`${this.elementId}_uploaded`)
3804
+ resultEl.innerHTML = html
3805
+ this.uploadItem(0, this.fileList)
3806
+ }
3807
+ render () {
3808
+ if (this.media.length > 0) {
3809
+ const resultEl = document.getElementById(`${this.elementId}_uploaded`)
3810
+ resultEl.innerHTML = this.createHtml(this.media.length)
3811
+ this.media.forEach((m, i) => {
3812
+ const imgEl = document.getElementById(`${this.elementId}_media_${i}`)
3813
+ imgEl.setAttribute('src', `data:${m.type};base64,` + m.data)
3814
+ })
3815
+ }
3816
+ }
3817
+ uploadItem (index, items, callbackFn) {
3818
+ if (!items[index]) {
3819
+ callbackFn()
3820
+ }
3821
+ else {
3822
+ const r = new FileReader()
3823
+ const mediaData = {
3824
+ type: items[index].file.type,
3825
+ name: items[index].file.name,
3826
+ size: items[index].file.size
3827
+ }
3828
+ r.onloadend = () => {
3829
+ let imgCanvas = document.createElement('canvas')
3830
+ let imgContext = imgCanvas.getContext('2d')
3831
+ let thumbCanvas = document.createElement('canvas')
3832
+ let thumbContext = thumbCanvas.getContext('2d')
3833
+ let img = new Image()
3834
+ img.onload = () => {
3835
+ let width = img.width
3836
+ let height = img.height
3837
+ if (this.options.resize === true && this.options.imgSize) {
3838
+ let ratio = 1
3839
+ if (width > height) {
3840
+ ratio = width / height
3841
+ width = this.options.imgSize
3842
+ height = this.options.imgSize / ratio
3843
+ }
3844
+ else if (height > width) {
3845
+ ratio = height / width
3846
+ width = this.options.imgSize / ratio
3847
+ height = this.options.imgSize
3848
+ }
3849
+ }
3850
+ const fullResEl = document.getElementById(`${this.elementId}_media_${index}_fullres`)
3851
+ if (fullResEl) {
3852
+ fullResEl.innerHTML = `${width} x ${height}`
3853
+ }
3854
+ imgCanvas.width = width
3855
+ imgCanvas.height = height
3856
+ imgContext.drawImage(img, 0, 0, width, height)
3857
+ let ratio = 1
3858
+ if (width > height) {
3859
+ ratio = width / height
3860
+ thumbCanvas.width = this.options.thumbSize
3861
+ thumbCanvas.height = this.options.thumbSize / ratio
3862
+ }
3863
+ else if (height > width) {
3864
+ ratio = height / width
3865
+ thumbCanvas.width = this.options.thumbSize / ratio
3866
+ thumbCanvas.height = this.options.thumbSize
3867
+ }
3868
+ thumbContext.drawImage(img, 0, 0, thumbCanvas.width, thumbCanvas.height)
3869
+ mediaData.name = items[index].name
3870
+ mediaData.fullWidth = imgCanvas.width
3871
+ mediaData.fullHeight = imgCanvas.height
3872
+ mediaData.thumbWidth = thumbCanvas.width
3873
+ mediaData.thumbHeight = thumbCanvas.height
3874
+ mediaData.data = imgCanvas.toDataURL(mediaData.type).replace(/^data:image\/(png|jpg);base64,/, '')
3875
+ mediaData.thumbData = thumbCanvas.toDataURL(mediaData.type).replace(/^data:image\/(png|jpg);base64,/, '')
3876
+ this.media.push(mediaData)
3877
+ const imgEl = document.getElementById(`${this.elementId}_media_${index}`)
3878
+ imgEl.setAttribute('src', imgCanvas.toDataURL(mediaData.type))
3879
+ this.uploadItem(++index, items, callbackFn)
3880
+ }
3881
+ img.src = r.result
3882
+ }
3883
+ r.readAsDataURL(items[index].file)
3884
+ }
3885
+ }
3886
+ }
3887
+
3717
3888
  /* global WebsyDesigns */
3718
3889
  class Pager {
3719
3890
  constructor (elementId, options) {
@@ -11438,6 +11609,7 @@ const WebsyDesigns = {
11438
11609
  DatePicker: WebsyDatePicker,
11439
11610
  WebsyDropdown,
11440
11611
  Dropdown: WebsyDropdown,
11612
+ MediaUpload: MediaUpload,
11441
11613
  WebsyResultList,
11442
11614
  ResultList: WebsyResultList,
11443
11615
  WebsyTemplate,