beyond-rails 0.0.229 → 0.0.234

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6eb3a6d0140c228f4cf167707a98fcc338f005de8f44fdfc160f8f0b31cc95fe
4
- data.tar.gz: 7bf45b85d53d4bfbb6856687d31e68fd23f53addaceb8e8c7783bc0847c60f93
3
+ metadata.gz: bcf9966ac572b5cb235b53411d2d89f3d271d88eec0b49bcba44592ed3d8157a
4
+ data.tar.gz: 77ecacca81f2b28ae7afe335a8f88f38c999856226b79c51202e93c89d29a9f0
5
5
  SHA512:
6
- metadata.gz: 67f40335950e4e6874d3bd6e7869610d67f9fffffe7708647703036522403f754c733f74bb6e20824255ee408bc40224a1784d56916bcfcea1b9213faaec5e02
7
- data.tar.gz: dbd66629f50809e95d2cb86308ce40601bdccfb0681eb5167982c94a76e10a3bb217b12caa4f5484784a973ccb5dc844f25edb9f3e11042ca7c53f1d2a61884e
6
+ metadata.gz: bccb21dc7b61956e318b4906638f441db32564ac98cc5711b9cb24d7ac07d0dbe9adcf23b99ecf01696a9d06ffa7dd5919d2c6820ee4f006ec2effbe1298f9e9
7
+ data.tar.gz: aa6e944f0916c3e968a6d55def21242a386abf30511c71c64dd7386cbfece40662bc5422265e6d7849ff4a37a601a240d71e74c821123ef64c978e8c99460a63
@@ -20,8 +20,11 @@ export default class SearchDropdown {
20
20
  this.options.itemClick = options.itemClick || itemClick
21
21
  this.options.change = options.change || noop
22
22
  this.options.wait = options.wait || 50
23
- this.place = 'bottom'
24
- this.align = 'left'
23
+ this.place = options.place || 'bottom'
24
+ this.align = options.align || 'left'
25
+ this.offset = options.offset || 14
26
+ this.offsetTop = options.offsetTop || 0
27
+ this.offsetLeft = options.offsetLeft || 0
25
28
  this.isMenuVisible = false
26
29
  this.lastKeyword = null
27
30
  this.selectedIndex = 0
@@ -53,7 +56,16 @@ export default class SearchDropdown {
53
56
  }
54
57
 
55
58
  appendMenu() {
59
+
56
60
  const menu = document.createElement('div')
61
+ const { dataset } = menu
62
+
63
+ dataset.place = this.place
64
+ dataset.align = this.align
65
+ dataset.offset = this.offset
66
+ dataset.offsetTop = this.offsetTop
67
+ dataset.offsetLeft = this.offsetLeft
68
+
57
69
  menu.className = 'search-dropdown dropdown-menu'
58
70
 
59
71
  const inputWrap = document.createElement('div')
@@ -100,15 +112,19 @@ export default class SearchDropdown {
100
112
  showMenu() {
101
113
  const { input, menu } = this
102
114
  this.getData(input.value)
115
+
103
116
  menu.style.display = 'block'
104
117
  menu.style.opacity = 0
105
118
  menu.style.transform = 'scale(.8)'
106
119
  document.body.appendChild(menu)
107
- this.adjustMenuPos()
108
- menu.style.transform = 'scale(1)'
109
- menu.style.opacity = 1
110
- this.isMenuVisible = true
111
- this.input.focus()
120
+
121
+ setTimeout(() => {
122
+ this.adjustMenuPos()
123
+ menu.style.transform = 'scale(1)'
124
+ menu.style.opacity = 1
125
+ this.isMenuVisible = true
126
+ this.input.focus()
127
+ }, 0)
112
128
  }
113
129
 
114
130
  toggleMenu() {
@@ -116,22 +132,18 @@ export default class SearchDropdown {
116
132
  }
117
133
 
118
134
  adjustMenuPos() {
119
- const { menu, dom } = this
120
- const { dataset } = menu
121
- const offsetLeft = ('offsetLeft' in dataset) ? parseInt(dataset.offsetLeft, 10) : 0
122
- const offsetTop = ('offsetTop' in dataset) ? parseInt(dataset.offsetTop, 10) : 0
123
-
135
+ const { menu, dom, offset, offsetLeft, offsetTop } = this
124
136
  const { pos, place, align } = getFloatedTargetPos({
125
137
  src: dom,
126
138
  target: menu,
127
139
  place: this.place,
128
140
  align: this.align,
129
- offset: parseInt(dom.dataset.offset, 10) || 14,
141
+ offset,
130
142
  offsetLeft,
131
143
  offsetTop
132
144
  })
133
- dataset.place = place
134
- dataset.align = align
145
+ menu.dataset.place = place
146
+ menu.dataset.align = align
135
147
  menu.style.left = toPixel(pos.left)
136
148
  menu.style.top = toPixel(pos.top)
137
149
  }
@@ -0,0 +1,178 @@
1
+ import raf from '../utils/raf'
2
+ import supportDom from '../decorators/supportDom'
3
+ import getKey from '../utils/getKey'
4
+ import noop from '../utils/noop'
5
+
6
+ @supportDom
7
+ export default class TagInput {
8
+
9
+ constructor(dom, options = {}) {
10
+ this.dom = dom
11
+ this.defaultInputWidth = 128
12
+ this.validate = options.validate || (() => ({ isTag: true }))
13
+ this.suggest = options.suggest || noop
14
+ this.change = options.change || noop
15
+ this.isComposing = false
16
+ this.raf = raf
17
+ this.tags = []
18
+ this.init()
19
+ }
20
+
21
+ init() {
22
+ this.setup()
23
+ this.addEvents()
24
+ }
25
+
26
+ setup() {
27
+ const { defaultInputWidth } = this
28
+ const inputDiv = document.createElement('div')
29
+ inputDiv.className = 'tag-input-box'
30
+
31
+ const suggestInput = document.createElement('input')
32
+ suggestInput.type = 'text'
33
+ suggestInput.style.width = defaultInputWidth + 'px'
34
+ suggestInput.className = 'tag-suggest-input'
35
+
36
+ const input = document.createElement('input')
37
+ input.type = 'text'
38
+ input.style.width = defaultInputWidth + 'px'
39
+ input.className = 'tag-main-input'
40
+
41
+ inputDiv.appendChild(input)
42
+ inputDiv.appendChild(suggestInput)
43
+
44
+ this.input = input
45
+ this.suggestInput = suggestInput
46
+ this.canvas = document.createElement('canvas')
47
+ this.inputDiv = inputDiv
48
+
49
+ this.dom.append(inputDiv)
50
+ }
51
+
52
+ getTextWidth(text, font) {
53
+ const context = this.canvas.getContext('2d')
54
+ context.font = font
55
+ const metrics = context.measureText(text)
56
+ return metrics.width
57
+ }
58
+
59
+ getNextInputWidth(textWidth) {
60
+ const { defaultInputWidth } = this
61
+ // spaces before text and after text
62
+ const delta = 5
63
+ const nextWidth = textWidth + delta
64
+ if (nextWidth < defaultInputWidth) {
65
+ return defaultInputWidth
66
+ }
67
+ return nextWidth
68
+ }
69
+
70
+ shake() {
71
+ this.dom.classList.add('shake')
72
+ setTimeout(() => {
73
+ this.dom.classList.remove('shake')
74
+ }, 500)
75
+ }
76
+
77
+ async addTagIfNeeded() {
78
+ const { input, suggestInput, inputDiv } = this
79
+ const inputValue = suggestInput.value || input.value
80
+ const res = await this.validate(inputValue)
81
+ if (! res.isTag) {
82
+ return this.shake()
83
+ }
84
+ const classname = res.classname ? ` ${res.classname}` : ''
85
+ const tag = document.createElement('div')
86
+
87
+ tag.className = 'tag' + classname
88
+ tag.textContent = inputValue
89
+
90
+ const btn = document.createElement('button')
91
+ btn.type = 'button'
92
+
93
+ // https://wesbos.com/times-html-entity-close-button
94
+ btn.textContent = '×'
95
+ const handleBtnClick = () => {
96
+ this.tags = this.tags.filter(row => row.elem !== tag)
97
+ btn.removeEventListener('click', handleBtnClick)
98
+ tag.remove()
99
+ this.change(this.tags.slice())
100
+ }
101
+ btn.addEventListener('click', handleBtnClick)
102
+ tag.appendChild(btn)
103
+
104
+ this.tags.push({ elem: tag, remove: handleBtnClick, res })
105
+ this.dom.insertBefore(tag, inputDiv)
106
+ input.value = ''
107
+ suggestInput.value = ''
108
+
109
+ this.change(this.tags.slice())
110
+ }
111
+
112
+ removeTagIfNeeded() {
113
+ const lastTag = this.tags[this.tags.length - 1]
114
+ if ((this.input.value === '') && lastTag) {
115
+ lastTag.remove()
116
+ }
117
+ }
118
+
119
+ addEvents() {
120
+ const { input } = this
121
+ const font = window.getComputedStyle(input)
122
+ .getPropertyValue('font')
123
+
124
+ this.addEvent(this.dom, 'click', event => {
125
+ if (event.target === this.dom) {
126
+ this.input.focus()
127
+ }
128
+ })
129
+
130
+ this.addEvent(input, 'compositionstart', event => {
131
+ this.isComposing = true
132
+ })
133
+
134
+ this.addEvent(input, 'compositionend', event => {
135
+ this.isComposing = false
136
+ })
137
+
138
+ let lastValue = ''
139
+
140
+ this.addEvent(input, 'keydown', async event => {
141
+ const key = getKey(event)
142
+ if ((key === 'enter') && (! this.isComposing)) {
143
+ await this.addTagIfNeeded()
144
+ }
145
+ else if ((key === 'backspace') && (lastValue === '')) {
146
+ this.removeTagIfNeeded()
147
+ }
148
+ lastValue = input.value
149
+ })
150
+
151
+ this.addEvent(input, 'input', event => {
152
+ this.suggestInputIfNeeded(input.value)
153
+ this.raf(() => {
154
+ const textWidth = this.getTextWidth(input.value, font)
155
+ const nextWidth = this.getNextInputWidth(textWidth)
156
+ input.style.width = nextWidth + 'px'
157
+ })
158
+ })
159
+ }
160
+
161
+ async suggestInputIfNeeded(value) {
162
+ const suggestValue = await this.suggest(value)
163
+ this.raf(() => {
164
+ if (this.input.value === value) {
165
+ this.suggestInput.value = (suggestValue || '')
166
+ }
167
+ })
168
+ }
169
+
170
+ destroy() {
171
+ this.tags.forEach(tag => tag.remove())
172
+ this.inputDiv.remove()
173
+ this.canvas = null
174
+ this.input = null
175
+ this.suggestInput = null
176
+ this.inputDiv = null
177
+ }
178
+ }
@@ -1,4 +1,4 @@
1
- import isDef from '../utils/isDef'
1
+ import raf from '../utils/raf'
2
2
  import isUndef from '../utils/isUndef'
3
3
  import { getDomPos, range, toPixel, isFunction } from '../utils'
4
4
 
@@ -6,6 +6,11 @@ export default function chartCommon(target) {
6
6
 
7
7
  return class extends target {
8
8
 
9
+ constructor(...args) {
10
+ super(...args)
11
+ this.raf = raf
12
+ }
13
+
9
14
  init() {
10
15
  this.layers = []
11
16
  if (isFunction(super.init)) {
@@ -159,13 +164,6 @@ export default function chartCommon(target) {
159
164
  return this.ctx.measureText(value).width
160
165
  }
161
166
 
162
- raf(fn) {
163
- if (isDef(window.requestAnimationFrame)) {
164
- return window.requestAnimationFrame(fn)
165
- }
166
- return fn()
167
- }
168
-
169
167
  removeAllLayers() {
170
168
  const { dom } = this
171
169
  this.layers.forEach(layer => {
@@ -20,6 +20,7 @@ import Navbar from './components/Navbar'
20
20
  import Radio from './components/Radio'
21
21
  import SearchDropdown from './components/SearchDropdown'
22
22
  import Sidebar from './components/Sidebar'
23
+ import TagInput from './components/TagInput'
23
24
  import Tabbox from './components/Tabbox'
24
25
  import Timepicker from './components/Timepicker'
25
26
  import Toast from './components/Toast'
@@ -55,6 +56,7 @@ export {
55
56
  Radio,
56
57
  SearchDropdown,
57
58
  Sidebar,
59
+ TagInput,
58
60
  Tabbox,
59
61
  Timepicker,
60
62
  Toast,
@@ -1,4 +1,5 @@
1
1
  const keyMap = {
2
+ 8: 'backspace',
2
3
  13: 'enter',
3
4
  17: 'ctrl',
4
5
  27: 'esc',
@@ -0,0 +1,3 @@
1
+ export default function isStr(value) {
2
+ return (typeof value === 'string')
3
+ }
@@ -0,0 +1,2 @@
1
+ export default function noop() {
2
+ }
@@ -0,0 +1,8 @@
1
+ import isDef from './isDef'
2
+
3
+ export default function raf(fn) {
4
+ if (isDef(window.requestAnimationFrame)) {
5
+ return window.requestAnimationFrame(fn)
6
+ }
7
+ return fn()
8
+ }
@@ -0,0 +1,13 @@
1
+ @keyframes shake {
2
+ 0% { transform: translate(1px, 1px) rotate(0deg); }
3
+ 10% { transform: translate(-1px, -2px) rotate(-1deg); }
4
+ 20% { transform: translate(-3px, 0px) rotate(1deg); }
5
+ 30% { transform: translate(3px, 2px) rotate(0deg); }
6
+ 40% { transform: translate(1px, -1px) rotate(1deg); }
7
+ 50% { transform: translate(-1px, 2px) rotate(-1deg); }
8
+ 60% { transform: translate(-3px, 1px) rotate(0deg); }
9
+ 70% { transform: translate(3px, 1px) rotate(-1deg); }
10
+ 80% { transform: translate(-1px, -1px) rotate(1deg); }
11
+ 90% { transform: translate(1px, 2px) rotate(0deg); }
12
+ 100% { transform: translate(1px, -2px) rotate(-1deg); }
13
+ }
@@ -5,6 +5,7 @@
5
5
  @import './vendor/_turbolink';
6
6
  @import './base/_background';
7
7
  @import './base/_typography';
8
+ @import './_animations';
8
9
  @import './_main';
9
10
  @import './layout/_border-util';
10
11
  @import './layout/_col';
@@ -44,6 +45,7 @@
44
45
  @import './components/_tabbox';
45
46
  @import './components/_table';
46
47
  @import './components/_tag';
48
+ @import './components/_tag-input';
47
49
  @import './components/_time-input';
48
50
  @import './components/_time-menu';
49
51
  @import './components/_toast';
@@ -153,6 +153,10 @@ $color-active: #5469d4;
153
153
  border: 0;
154
154
  }
155
155
 
156
+ .full-width {
157
+ width: 100% !important;
158
+ }
159
+
156
160
  hr {
157
161
  margin-top: 20px;
158
162
  margin-bottom: 20px;
@@ -345,6 +345,9 @@ input[type="file"] {
345
345
  > .form-control {
346
346
  flex: 1 1 0%;
347
347
  }
348
+ .invalid-feedback {
349
+ flex-basis: 100%;
350
+ }
348
351
  }
349
352
 
350
353
  .input-group-text {
@@ -1,4 +1,5 @@
1
1
  .search-dropdown.dropdown-menu {
2
+ width: 420px;
2
3
  padding: 0;
3
4
  .search-dropdown-input-wrap {
4
5
  padding: 14px;
@@ -26,8 +27,5 @@
26
27
  color: #6772e5;
27
28
  margin-right: .7em;
28
29
  }
29
- > span {
30
- max-width: 420px;
31
- }
32
30
  }
33
31
  }
@@ -0,0 +1,40 @@
1
+ .tag-input {
2
+ cursor: text;
3
+ background-color: #fff;
4
+ padding: .4em .4em 0 .4em;
5
+ min-height: 40px;
6
+
7
+ .tag-suggest-input {
8
+ color: rgba(60, 66, 87, .41);
9
+ }
10
+
11
+ .tag-main-input {
12
+ position: absolute;
13
+ background-color: transparent;
14
+ }
15
+
16
+ .tag-input-box {
17
+ display: inline-block;
18
+ position: relative;
19
+ > input {
20
+ border: 0;
21
+ &:focus {
22
+ outline: none;
23
+ }
24
+ }
25
+ }
26
+ .tag {
27
+ padding-right: 0;
28
+ display: inline-flex;
29
+ align-items: center;
30
+ margin-right: .7em;
31
+ margin-bottom: .7em;
32
+ > button {
33
+ border: 0;
34
+ background-color: transparent;
35
+ }
36
+ }
37
+ &.shake {
38
+ animation: shake .5s;
39
+ }
40
+ }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beyond-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.229
4
+ version: 0.0.234
5
5
  platform: ruby
6
6
  authors:
7
7
  - kmsheng
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-10-13 00:00:00.000000000 Z
12
+ date: 2020-10-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sassc
@@ -144,6 +144,7 @@ files:
144
144
  - src/js/components/SearchDropdown.js
145
145
  - src/js/components/Sidebar.js
146
146
  - src/js/components/Tabbox.js
147
+ - src/js/components/TagInput.js
147
148
  - src/js/components/TimeInput.js
148
149
  - src/js/components/TimeMenu.js
149
150
  - src/js/components/Timepicker.js
@@ -194,11 +195,15 @@ files:
194
195
  - src/js/utils/index.js
195
196
  - src/js/utils/isDef.js
196
197
  - src/js/utils/isInt.js
198
+ - src/js/utils/isStr.js
197
199
  - src/js/utils/isTouchDevice.js
198
200
  - src/js/utils/isUndef.js
199
201
  - src/js/utils/msToS.js
202
+ - src/js/utils/noop.js
200
203
  - src/js/utils/promisify.js
204
+ - src/js/utils/raf.js
201
205
  - src/js/utils/unbindAll.js
206
+ - src/sass/_animations.scss
202
207
  - src/sass/_beyond-sprockets.scss
203
208
  - src/sass/_beyond.scss
204
209
  - src/sass/_main.scss
@@ -239,6 +244,7 @@ files:
239
244
  - src/sass/components/_switch.scss
240
245
  - src/sass/components/_tabbox.scss
241
246
  - src/sass/components/_table.scss
247
+ - src/sass/components/_tag-input.scss
242
248
  - src/sass/components/_tag.scss
243
249
  - src/sass/components/_time-input.scss
244
250
  - src/sass/components/_time-menu.scss