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 +4 -4
- data/src/js/components/SearchDropdown.js +27 -15
- data/src/js/components/TagInput.js +178 -0
- data/src/js/decorators/chartCommon.js +6 -8
- data/src/js/index.js +2 -0
- data/src/js/utils/getKey.js +1 -0
- data/src/js/utils/isStr.js +3 -0
- data/src/js/utils/noop.js +2 -0
- data/src/js/utils/raf.js +8 -0
- data/src/sass/_animations.scss +13 -0
- data/src/sass/_beyond.scss +2 -0
- data/src/sass/_main.scss +4 -0
- data/src/sass/components/_form.scss +3 -0
- data/src/sass/components/_search-dropdown.scss +1 -3
- data/src/sass/components/_tag-input.scss +40 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcf9966ac572b5cb235b53411d2d89f3d271d88eec0b49bcba44592ed3d8157a
|
4
|
+
data.tar.gz: 77ecacca81f2b28ae7afe335a8f88f38c999856226b79c51202e93c89d29a9f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
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
|
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 => {
|
data/src/js/index.js
CHANGED
@@ -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,
|
data/src/js/utils/getKey.js
CHANGED
data/src/js/utils/raf.js
ADDED
@@ -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
|
+
}
|
data/src/sass/_beyond.scss
CHANGED
@@ -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';
|
data/src/sass/_main.scss
CHANGED
@@ -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.
|
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-
|
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
|