beyond-rails 0.0.229 → 0.0.234

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: 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