@websy/websy-designs 1.10.2 → 1.10.4

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.
@@ -1826,7 +1826,7 @@ class WebsyDropdown {
1826
1826
  }
1827
1827
  }
1828
1828
  }
1829
- open (event, override = false) {
1829
+ open (event, override = false) {
1830
1830
  const maskEl = document.getElementById(`${this.elementId}_mask`)
1831
1831
  const contentEl = document.getElementById(`${this.elementId}_content`)
1832
1832
  const headerEl = document.getElementById(`${this.elementId}_header`)
@@ -5296,30 +5296,86 @@ class WebsySearch {
5296
5296
  clearAlwaysOn: false,
5297
5297
  placeholder: 'Search',
5298
5298
  searchTimeout: 500,
5299
+ suggestTimeout: 100,
5300
+ suggestingTimeout: 3000,
5301
+ suggestLimit: 5,
5299
5302
  minLength: 2
5300
5303
  }
5301
5304
  this.options = Object.assign({}, DEFAULTS, options)
5302
5305
  this.searchTimeoutFn = null
5306
+ this.suggestTimeoutFn = null
5307
+ this.suggestingTimeoutFn = null
5308
+ this.isSuggesting = false
5309
+ this.inSuggestions = false
5310
+ this.cursorPosition = 0
5311
+ this.terms = []
5312
+ this.Key = {
5313
+ BACKSPACE: 8,
5314
+ ESCAPE: 27,
5315
+ CONTROL: 17,
5316
+ COMMAND: 91,
5317
+ PASTE: 86,
5318
+ TAB: 9,
5319
+ ENTER: 13,
5320
+ SHIFT: 16,
5321
+ UP: 38,
5322
+ DOWN: 40,
5323
+ RIGHT: 39,
5324
+ LEFT: 37,
5325
+ DELETE: 46,
5326
+ SPACE: 32
5327
+ }
5303
5328
  const el = document.getElementById(elementId)
5304
5329
  if (el) {
5305
5330
  // el.addEventListener('click', this.handleClick.bind(this))
5306
5331
  el.addEventListener('click', this.handleClick.bind(this))
5307
5332
  el.addEventListener('keyup', this.handleKeyUp.bind(this))
5308
- el.addEventListener('keyup', this.handleKeyDown.bind(this))
5333
+ el.addEventListener('keydown', this.handleKeyDown.bind(this))
5334
+ el.addEventListener('mouseover', this.handleMouseOver.bind(this))
5335
+ // el.innerHTML = `
5336
+ // <div class='websy-search-input-container'>
5337
+ // ${this.options.searchIcon}
5338
+ // <input id='${this.elementId}_search' class='websy-search-input' placeholder='${this.options.placeholder || 'Search'}' value='${this.options.initialValue || ''}'>
5339
+ // <div class='clear ${this.options.clearAlwaysOn === true ? '' : 'websy-hidden'}' id='${this.elementId}_clear'>
5340
+ // ${this.options.clearIcon}
5341
+ // </div>
5342
+ // </div>
5343
+ // `
5309
5344
  el.innerHTML = `
5310
- <div class='websy-search-input-container'>
5311
- ${this.options.searchIcon}
5312
- <input id='${this.elementId}_search' class='websy-search-input' placeholder='${this.options.placeholder || 'Search'}' value='${this.options.initialValue || ''}'>
5313
- <div class='clear ${this.options.clearAlwaysOn === true ? '' : 'websy-hidden'}' id='${this.elementId}_clear'>
5314
- ${this.options.clearIcon}
5315
- </div>
5345
+ <div class='websy-search-input-container'>
5346
+ ${this.options.searchIcon}
5347
+ <div id='${this.elementId}_ghost' class='websy-search-input-ghost'></div>
5348
+ <div id='${this.elementId}_lozenges' class='websy-search-lozenge-container'></div>
5349
+ <input id='${this.elementId}_search' class='websy-search-input' placeholder='${this.options.placeholder || 'Search'}' value='${this.options.initialValue || ''}' autocorrect='off' autocomplete='off' autocapitalize='off' spellcheck='false'>
5350
+ <div id='${this.elementId}_ambiguities' class='websy-search-ambiguity-container'></div>
5351
+ <div class='clear ${this.options.clearAlwaysOn === true ? '' : 'websy-hidden'}' id='${this.elementId}_clear'>
5352
+ ${this.options.clearIcon}
5316
5353
  </div>
5317
- `
5354
+ <div id='${this.elementId}_suggestions' class='websy-search-suggestion-container'>
5355
+ <ul id='${this.elementId}_suggestionList'></ul>
5356
+ </div>
5357
+ <div id='${this.elementId}_associations' class='websy-search-association-container'>
5358
+ <ul id='${this.elementId}_associationsList'></ul>
5359
+ </div>
5360
+ </div>
5361
+ `
5318
5362
  }
5319
5363
  else {
5320
5364
  console.log('No element found with Id', elementId)
5321
5365
  }
5322
5366
  }
5367
+ acceptSuggestion () {
5368
+ this.searchText = this.ghostQuery
5369
+ this.suggestions = []
5370
+ this.hideSuggestions()
5371
+ const inputEl = document.getElementById(`${this.elementId}_search`)
5372
+ if (inputEl) {
5373
+ inputEl.value = this.searchText
5374
+ }
5375
+ if (this.options.onSearch) {
5376
+ this.options.onSearch(this.searchText)
5377
+ }
5378
+ }
5323
5379
  handleClick (event) {
5324
5380
  if (event.target.classList.contains('clear')) {
5325
5381
  const inputEl = document.getElementById(`${this.elementId}_search`)
@@ -5331,6 +5387,9 @@ class WebsySearch {
5331
5387
  this.options.onClear()
5332
5388
  }
5333
5389
  }
5390
+ else if (event.target.classList.contains('websy-search-suggestion-item')) {
5391
+ this.acceptSuggestion()
5392
+ }
5334
5393
  }
5335
5394
  handleKeyDown (event) {
5336
5395
  if (event.key === 'Enter') {
@@ -5340,13 +5399,71 @@ class WebsySearch {
5340
5399
  return false
5341
5400
  }
5342
5401
  }
5402
+ else if (event.keyCode === this.Key.ESCAPE) {
5403
+ this.hideSuggestions()
5404
+ }
5405
+ else if (event.keyCode === this.Key.CONTROL || event.keyCode === this.Key.COMMAND) {
5406
+ // show the suggestions again
5407
+ this.isCutCopyPaste = true
5408
+ }
5409
+ else if (event.keyCode === this.Key.PASTE && this.isCutCopyPaste) {
5410
+ // show the suggestions again
5411
+ this.isPaste = true
5412
+ }
5413
+ else if (event.keyCode === this.Key.DOWN) {
5414
+ // show the suggestions again
5415
+ this.inSuggestions = true
5416
+ this.showSuggestions()
5417
+ }
5418
+ else if (event.keyCode === this.Key.UP) {
5419
+ // show the suggestions again
5420
+ if (this.inSuggestions) {
5421
+ event.preventDefault()
5422
+ }
5423
+ this.inSuggestions = false
5424
+ }
5425
+ else if (event.keyCode === this.Key.RIGHT) {
5426
+ if (this.suggesting && this.inSuggestions) {
5427
+ // activate the next suggestion
5428
+ event.preventDefault()
5429
+ this.nextSuggestion()
5430
+ }
5431
+ }
5432
+ else if (event.keyCode === this.Key.LEFT) {
5433
+ if (this.suggesting && this.inSuggestions) {
5434
+ // activate the previous suggestion
5435
+ event.preventDefault()
5436
+ this.prevSuggestion()
5437
+ }
5438
+ }
5439
+ else if (event.keyCode === this.Key.ENTER || event.keyCode === this.Key.TAB) {
5440
+ if (this.suggesting) {
5441
+ event.preventDefault()
5442
+ this.acceptSuggestion()
5443
+ }
5444
+ else if (this.associating && event.keyCode === this.Key.ENTER) {
5445
+ event.preventDefault()
5446
+ // this.searchEntity.selectAssociations(this.searchFields || [], 0);
5447
+ this.hideAssociations()
5448
+ }
5449
+ }
5450
+ else if (event.keyCode === this.Key.SPACE) {
5451
+ this.hideSuggestions()
5452
+ // this.hideAssociations()
5453
+ }
5454
+ // else{
5455
+ // this.hideSuggestions();
5456
+ // this.hideAssociations();
5457
+ // }
5343
5458
  }
5344
5459
  handleKeyUp (event) {
5345
5460
  if (event.target.classList.contains('websy-search-input')) {
5461
+ this.cursorPosition = event.target.selectionStart
5462
+ this.searchText = event.target.value
5346
5463
  if (this.searchTimeoutFn) {
5347
5464
  clearTimeout(this.searchTimeoutFn)
5348
5465
  }
5349
- if (event.key === 'Enter') {
5466
+ if (event.key === 'Enter' || event.key === 'Tab') {
5350
5467
  return false
5351
5468
  }
5352
5469
  const clearEl = document.getElementById(`${this.elementId}_clear`)
@@ -5357,7 +5474,10 @@ class WebsySearch {
5357
5474
  else {
5358
5475
  clearEl.classList.add('websy-hidden')
5359
5476
  }
5360
- }
5477
+ }
5478
+ if (this.options.onKeyUp) {
5479
+ this.options.onKeyUp(event.target.value, event)
5480
+ }
5361
5481
  if (event.target.value.length >= this.options.minLength) {
5362
5482
  this.searchTimeoutFn = setTimeout(() => {
5363
5483
  if (this.options.onSearch) {
@@ -5373,6 +5493,154 @@ class WebsySearch {
5373
5493
  }
5374
5494
  }
5375
5495
  }
5496
+ this.renderLozenges()
5497
+ }
5498
+ handleMouseOver (event) {
5499
+ if (event.target.classList.contains('websy-search-suggestion-item')) {
5500
+ this.startSuggestionTimeout()
5501
+ const index = event.target.getAttribute('data-index')
5502
+ this.activeSuggestion = +index
5503
+ this.renderGhost()
5504
+ this.highlightActiveSuggestion()
5505
+ }
5506
+ }
5507
+ hideSuggestions () {
5508
+ this.suggesting = false
5509
+ this.activeSuggestion = 0
5510
+ this.inSuggestions = false
5511
+ this.ghostPart = ''
5512
+ this.ghostQuery = ''
5513
+ this.ghostDisplay = ''
5514
+ this.hideGhost()
5515
+ const suggestEl = document.getElementById(`${this.elementId}_suggestions`)
5516
+ if (suggestEl) {
5517
+ suggestEl.classList.remove('active')
5518
+ }
5519
+ }
5520
+ hideGhost () {
5521
+ const ghostEl = document.getElementById(`${this.elementId}_ghost`)
5522
+ if (ghostEl) {
5523
+ ghostEl.innerHTML = ''
5524
+ }
5525
+ }
5526
+ highlightActiveSuggestion () {
5527
+ // remove all previous highlights
5528
+ // const parent = document.getElementById(`${this.elementId}_suggestionList`)
5529
+ // if (parent) {
5530
+ // for (let c = 0; c < parent.childElementCount; c++) {
5531
+ // parent.childNodes[c].classList.remove('active')
5532
+ // }
5533
+ // }
5534
+ // // add the 'active' class to the current suggestion
5535
+ // const activeSuggEl = document.getElementById(`${this.elementId}_suggestion_${this.activeSuggestion}`)
5536
+ // if (activeSuggEl) {
5537
+ // activeSuggEl.classList.add('active')
5538
+ // }
5539
+ const el = document.getElementById(this.elementId)
5540
+ if (el) {
5541
+ const els = document.querySelectorAll('.websy-search-suggestion-item')
5542
+ Array.from(els).forEach(e => {
5543
+ e.classList.remove('active')
5544
+ const index = e.getAttribute('data-index')
5545
+ if (+index === this.activeSuggestion) {
5546
+ e.classList.add('active')
5547
+ }
5548
+ })
5549
+ }
5550
+ }
5551
+ nextSuggestion () {
5552
+ this.startSuggestionTimeout()
5553
+ if (this.activeSuggestion === this.suggestions.length - 1) {
5554
+ this.activeSuggestion = 0
5555
+ }
5556
+ else {
5557
+ this.activeSuggestion++
5558
+ }
5559
+ this.renderGhost()
5560
+ this.highlightActiveSuggestion()
5561
+ }
5562
+ prevSuggestion () {
5563
+ this.startSuggestionTimeout()
5564
+ if (this.activeSuggestion === 0) {
5565
+ this.activeSuggestion = this.suggestions.length - 1
5566
+ }
5567
+ else {
5568
+ this.activeSuggestion--
5569
+ }
5570
+ this.renderGhost()
5571
+ this.highlightActiveSuggestion()
5572
+ }
5573
+ renderGhost () {
5574
+ this.ghostPart = getGhostString(this.searchText, this.suggestions[this.activeSuggestion].label)
5575
+ this.ghostQuery = this.searchText + this.ghostPart
5576
+ const ghostDisplay = `<span style='color: transparent;'>${this.searchText}</span>${this.ghostPart}`
5577
+ const ghostEl = document.getElementById(`${this.elementId}_ghost`)
5578
+ if (ghostEl) {
5579
+ ghostEl.innerHTML = ghostDisplay
5580
+ }
5581
+ function getGhostString (query, suggestion) {
5582
+ let suggestBase = query.toLowerCase()
5583
+ suggestion = suggestion.toLowerCase()
5584
+ while (suggestion.indexOf(suggestBase) === -1) {
5585
+ suggestBase = suggestBase.split(' ')
5586
+ suggestBase.splice(0, 1)
5587
+ suggestBase = suggestBase.join(' ')
5588
+ }
5589
+ const re = new RegExp(suggestBase, 'i')
5590
+ return suggestion.replace(re, '')
5591
+ }
5592
+ }
5593
+ renderLozenges () {
5594
+ let items = this.searchText.split('').map(d => (`<div>${d.replace(/ /g, '&nbsp;')}</div>`))
5595
+ const el = document.getElementById(`${this.elementId}_lozenges`)
5596
+ el.innerHTML = items.join('')
5597
+ }
5598
+ renderSuggestion () {
5599
+ let suggestionsHtml = ''
5600
+ for (let i = 0; i < this.suggestions.length; i++) {
5601
+ suggestionsHtml += `
5602
+ <li id='${this.elementId}_suggestion_${i}' class='websy-search-suggestion-item' data-index='${i}'>
5603
+ ${this.suggestions[i].label}
5604
+ </li>
5605
+ `
5606
+ }
5607
+ const suggListEl = document.getElementById(`${this.elementId}_suggestionList`)
5608
+ if (suggListEl) {
5609
+ suggListEl.innerHTML = suggestionsHtml
5610
+ }
5611
+ this.highlightActiveSuggestion()
5612
+ }
5613
+ showSuggestions (items = []) {
5614
+ this.suggestions = items.splice(0, this.options.suggestLimit)
5615
+ this.startSuggestionTimeout()
5616
+ if (this.searchText && this.searchText.length > 1 && this.cursorPosition === this.searchText.length && this.suggestions.length > 0) {
5617
+ if (!this.suggesting) {
5618
+ this.activeSuggestion = 0
5619
+ this.suggesting = true
5620
+ }
5621
+ // render the suggested completion
5622
+ this.renderGhost()
5623
+ // render the suggestions
5624
+ const suggestEl = document.getElementById(`${this.elementId}_suggestions`)
5625
+ if (suggestEl) {
5626
+ suggestEl.classList.add('active')
5627
+ }
5628
+ this.renderSuggestion()
5629
+ }
5630
+ else {
5631
+ this.suggesting = false
5632
+ this.hideGhost()
5633
+ this.hideSuggestions()
5634
+ }
5635
+ }
5636
+ startSuggestionTimeout () {
5637
+ if (this.suggestingTimeoutFn) {
5638
+ clearTimeout(this.suggestingTimeoutFn)
5639
+ }
5640
+ this.suggestingTimeoutFn = setTimeout(() => {
5641
+ // close the suggestions after inactivity for [suggestingTimeout] milliseconds
5642
+ this.hideSuggestions(this)
5643
+ }, this.options.suggestingTimeout)
5376
5644
  }
5377
5645
  get text () {
5378
5646
  const el = document.getElementById(`${this.elementId}_search`)
@@ -7157,11 +7425,11 @@ class WebsyTable3 {
7157
7425
  headerHtml += '</colgroup>'
7158
7426
  }
7159
7427
  this.options.columns.forEach((row, rowIndex) => {
7160
- if (useWidths === false && rowIndex !== this.options.columns.length - 1) {
7161
- // if we're calculating the size we only want to render the last row of column headers
7162
- return
7163
- }
7164
- headerHtml += `<tr class="websy-table-row websy-table-header-row">`
7428
+ // if (useWidths === false && rowIndex !== this.options.columns.length - 1) {
7429
+ // // if we're calculating the size we only want to render the last row of column headers
7430
+ // return
7431
+ // }
7432
+ headerHtml += `<tr class="websy-table-row websy-table-header-row ${rowIndex !== this.options.columns.length - 1 ? 'websy-table-parent-header' : ''}">`
7165
7433
  row.filter(c => c.show !== false).forEach((col, colIndex) => {
7166
7434
  if (typeof sizingColumns[colIndex] === 'undefined' || sizingColumns[colIndex].show === false) {
7167
7435
  return // need to revisit this logic
@@ -7289,7 +7557,7 @@ class WebsyTable3 {
7289
7557
  let footerEl = document.getElementById(`${this.elementId}_tableFooter`)
7290
7558
  footerEl.innerHTML = this.buildTotalHtml()
7291
7559
  this.sizes.total = footerEl.getBoundingClientRect()
7292
- const rows = Array.from(tableEl.querySelectorAll('.websy-table-row'))
7560
+ const rows = Array.from(tableEl.querySelectorAll('.websy-table-row:not(.websy-table-parent-header)'))
7293
7561
  let totalWidth = 0
7294
7562
  this.sizes.scrollableWidth = this.sizes.table.width
7295
7563
  let firstNonPinnedColumnWidth = 0