Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117923727
D1120.1775440694.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
10 KB
Referenced Files
None
Subscribers
None
D1120.1775440694.diff
View Options
diff --git a/src/resources/js/app.js b/src/resources/js/app.js
--- a/src/resources/js/app.js
+++ b/src/resources/js/app.js
@@ -82,18 +82,16 @@
let feedback = $('<div class="invalid-feedback">').text(msg_text)
- if (input.is('.listinput')) {
+ if (input.is('.list-input')) {
// List input widget
- let list = input.next('.listinput-widget')
-
- list.children(':not(:first-child)').each((index, element) => {
+ input.children(':not(:first-child)').each((index, element) => {
if (msg[index]) {
$(element).find('input').addClass('is-invalid')
}
})
- list.addClass('is-invalid').next('.invalid-feedback').remove()
- list.after(feedback)
+ input.addClass('is-invalid').next('.invalid-feedback').remove()
+ input.after(feedback)
}
else {
// Standard form element
diff --git a/src/resources/js/fontawesome.js b/src/resources/js/fontawesome.js
--- a/src/resources/js/fontawesome.js
+++ b/src/resources/js/fontawesome.js
@@ -13,6 +13,7 @@
faInfoCircle,
faLock,
faKey,
+ faPlus,
faSignInAlt,
faSyncAlt,
faTrashAlt,
@@ -30,6 +31,7 @@
faInfoCircle,
faLock,
faKey,
+ faPlus,
faSignInAlt,
faSquare,
faSyncAlt,
diff --git a/src/resources/sass/app.scss b/src/resources/sass/app.scss
--- a/src/resources/sass/app.scss
+++ b/src/resources/sass/app.scss
@@ -79,11 +79,7 @@
font-weight: bold;
}
-.listinput {
- display: none;
-}
-
-.listinput-widget {
+.list-input {
& > div {
&:not(:last-child) {
margin-bottom: -1px;
@@ -103,6 +99,10 @@
}
}
}
+
+ input.is-invalid {
+ z-index: 2;
+ }
}
.range-input {
diff --git a/src/resources/vue/User/Info.vue b/src/resources/vue/User/Info.vue
--- a/src/resources/vue/User/Info.vue
+++ b/src/resources/vue/User/Info.vue
@@ -31,9 +31,9 @@
</div>
</div>
<div class="form-group row">
- <label for="aliases" class="col-sm-4 col-form-label">Email aliases</label>
+ <label for="aliases-input" class="col-sm-4 col-form-label">Email aliases</label>
<div class="col-sm-8">
- <textarea class="form-control listinput" id="aliases"></textarea>
+ <list-input id="aliases" v-bind:list="user.aliases"></list-input>
</div>
</div>
<div class="form-group row">
@@ -147,7 +147,12 @@
</template>
<script>
+ import ListInput from '../Widgets/ListInput'
+
export default {
+ components: {
+ ListInput,
+ },
data() {
return {
discount: 0,
@@ -190,8 +195,6 @@
this.user.last_name = response.data.settings.last_name
this.discount = this.user.wallet.discount
this.discount_description = this.user.wallet.discount_description
- $('#aliases').val(response.data.aliases.join("\n"))
- listinput('#aliases')
axios.get('/api/v4/skus')
.then(response => {
@@ -220,18 +223,12 @@
}
},
mounted() {
- if (this.user_id === 'new') {
- listinput('#aliases')
- }
-
$('#first_name').focus()
},
methods: {
submit() {
this.$root.clearFormValidation($('#user-info form'))
- this.user.aliases = $('#aliases').val().split("\n")
-
let method = 'post'
let location = '/api/v4/users'
@@ -360,76 +357,4 @@
}
}
}
-
- // List widget
- // TODO: move it to a separate component file when needed
- function listinput(elem)
- {
- elem = $(elem).addClass('listinput');
-
- let widget = $('<div class="listinput-widget">')
- let main_row = $('<div class="input-group">')
- let wrap = $('<div class="input-group-append">')
- let input = $('<input type="text" class="form-control main-input">')
- let add_btn = $('<a href="#" class="btn btn-outline-secondary">').text('Add')
-
- let update = () => {
- let value = []
-
- widget.find('input:not(.main-input)').each((index, input) => {
- if (input.value) {
- value.push(input.value)
- }
- })
-
- elem.val(value.join("\n"))
- }
-
- let add_func = (value) => {
- let row = $('<div class="input-group">')
- let rinput = $('<input type="text" class="form-control">').val(value)
- let rwrap = $('<div class="input-group-append">')
- let del_btn = $('<a href="#" class="btn btn-outline-secondary">')
- .text('Remove')
- .on('click', e => {
- row.remove()
- input.focus()
- update()
- })
-
- widget.append(row.append(rinput).append(rwrap.append(del_btn)))
- }
-
- // Create the widget and add to DOM
- widget.append(main_row.append(input).append(wrap.append(add_btn)))
- .insertAfter(elem)
-
- // Add rows for every line in the original textarea
- let value = $.trim(elem.val())
- if (value.length) {
- value.split("\n").forEach(add_func)
- }
-
- // Click handler on the Add button
- add_btn.on('click', e => {
- let value = input.val()
-
- if (!value) {
- return;
- }
-
- input.val('').focus();
- add_func(value)
- update()
- })
-
- // Enter key handler on main input
- input.on('keydown', function(e) {
- if (e.which == 13 && this.value) {
- add_btn.click()
- return false
- }
- })
- }
-
</script>
diff --git a/src/resources/vue/Widgets/ListInput.vue b/src/resources/vue/Widgets/ListInput.vue
new file mode 100644
--- /dev/null
+++ b/src/resources/vue/Widgets/ListInput.vue
@@ -0,0 +1,55 @@
+<template>
+ <div class="list-input" :id="id">
+ <div class="input-group">
+ <input :id="id + '-input'" type="text" class="form-control main-input" @keydown="keyDown">
+ <div class="input-group-append">
+ <a href="#" class="btn btn-outline-secondary" @click="addItem">
+ <svg-icon icon="plus"></svg-icon>
+ <span class="sr-only">Add</span>
+ </a>
+ </div>
+ </div>
+ <div class="input-group" v-for="(item, index) in list" :key="index">
+ <input type="text" class="form-control" :value="item">
+ <div class="input-group-append">
+ <a href="#" class="btn btn-outline-secondary" @click="deleteItem(index)">
+ <svg-icon icon="trash-alt"></svg-icon>
+ <span class="sr-only">Delete</span>
+ </a>
+ </div>
+ </div>
+ </div>
+</template>
+
+<script>
+ export default {
+ props: {
+ 'list': { type: Array, default: () => [] },
+ 'id': { type: String, default: '' }
+ },
+ methods: {
+ addItem() {
+ let input = $(this.$el).find('.main-input')
+ let value = input.val()
+
+ if (value) {
+ this.list.push(value)
+ input.val('').focus()
+ }
+ },
+ deleteItem(index) {
+ this.$delete(this.list, index)
+
+ if (this.list.length == 1) {
+ $(this.$el).removeClass('is-invalid')
+ }
+ },
+ keyDown(e) {
+ if (e.which == 13 && e.target.value) {
+ this.addItem()
+ e.preventDefault()
+ }
+ }
+ }
+ }
+</script>
diff --git a/src/tests/Browser/Components/ListInput.php b/src/tests/Browser/Components/ListInput.php
--- a/src/tests/Browser/Components/ListInput.php
+++ b/src/tests/Browser/Components/ListInput.php
@@ -22,7 +22,7 @@
*/
public function selector()
{
- return $this->selector . ' + .listinput-widget';
+ return $this->selector;
}
/**
@@ -34,13 +34,9 @@
*/
public function assert($browser)
{
-// $list = explode("\n", $browser->value($this->selector));
-
- $browser->waitFor($this->selector())
- ->assertMissing($this->selector)
+ $browser->assertVisible($this->selector())
->assertVisible('@input')
->assertVisible('@add-btn');
-// ->assertListInputValue($list);
}
/**
diff --git a/src/tests/Browser/UsersTest.php b/src/tests/Browser/UsersTest.php
--- a/src/tests/Browser/UsersTest.php
+++ b/src/tests/Browser/UsersTest.php
@@ -142,7 +142,7 @@
->assertValue('div.row:nth-child(4) input[type=text]', 'john@kolab.org')
->assertDisabled('div.row:nth-child(4) input[type=text]')
->assertSeeIn('div.row:nth-child(5) label', 'Email aliases')
- ->assertVisible('div.row:nth-child(5) .listinput-widget')
+ ->assertVisible('div.row:nth-child(5) .list-input')
->with(new ListInput('#aliases'), function (Browser $browser) {
$browser->assertListInputValue(['john.doe@kolab.org'])
->assertValue('@input', '');
@@ -343,7 +343,7 @@
->assertValue('div.row:nth-child(3) input[type=text]', '')
->assertEnabled('div.row:nth-child(3) input[type=text]')
->assertSeeIn('div.row:nth-child(4) label', 'Email aliases')
- ->assertVisible('div.row:nth-child(4) .listinput-widget')
+ ->assertVisible('div.row:nth-child(4) .list-input')
->with(new ListInput('#aliases'), function (Browser $browser) {
$browser->assertListInputValue([])
->assertValue('@input', '');
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Apr 6, 1:58 AM (5 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18775695
Default Alt Text
D1120.1775440694.diff (10 KB)
Attached To
Mode
D1120: Improve list-input widget
Attached
Detach File
Event Timeline