New question ui
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
parent
2a14697a06
commit
0961fbfdd7
|
@ -1,25 +1,9 @@
|
|||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
# Set default charset
|
||||
charset = utf-8
|
||||
|
||||
# 4 space tab indentation
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
# Line length form NC coding guidelines
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
max_line_length = 80
|
||||
|
||||
# 2 space indentation for .yml files
|
||||
[.*.yml]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
|
|
@ -2,8 +2,11 @@ module.exports = {
|
|||
plugins: ['@babel/plugin-syntax-dynamic-import'],
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env'
|
||||
]
|
||||
]
|
||||
'@babel/preset-env',
|
||||
{
|
||||
corejs: 3,
|
||||
useBuiltIns: 'entry',
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -20,4 +20,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
@import 'icons'
|
||||
// Various variables used by this app
|
||||
:root {
|
||||
--header-height: $header-height;
|
||||
--top-bar-height: 60px;
|
||||
}
|
||||
|
||||
@import 'variables';
|
||||
@import 'icons';
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
@include icon-black-white('answer-short', 'forms', 1);
|
||||
@include icon-black-white('answer-long', 'forms', 1);
|
||||
@include icon-black-white('answer-checkbox', 'forms', 1);
|
||||
@include icon-black-white('answer-multiple', 'forms', 1);
|
||||
@include icon-black-white('drag-handle', 'forms', 1);
|
||||
|
||||
.icon-yes {
|
||||
@include icon-color('checkmark', 'actions', $color-success, 1, true);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16">
|
||||
<path d="M2.43 2a.43.43 0 00-.43.43V4.3l.86.86v-2.3h8.73l.86-.86H2.43zM14 5.86l-.86.86v6.42H2.86v-2.6L2 9.68v3.9a.43.43 0 00.43.42h11.14a.43.43 0 00.43-.43v-7.7z"/>
|
||||
<path d="M6.09 12.5L1.14 7.55l1.41-1.41L6.1 9.67l6.34-6.38 1.44 1.43z"/>
|
||||
<path d="M1.5 1c-.277 0-.5.223-.5.5v13c0 .277.223.5.5.5h13c.277 0 .5-.223.5-.5v-13c0-.277-.223-.5-.5-.5h-13zm10.756 3L13.5 5.242 6.773 12 2.5 7.701l1.217-1.226L6.783 9.54 12.256 4z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 257 B |
3
img/answer-multiple.svg
Normal file
3
img/answer-multiple.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
|
||||
<path d="M2 3v2h2V3zM6 3v2h8V3zM2 7v2h2V7zM6 7v2h8V7zM2 11v2h2v-2zM6 11v2h8v-2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 156 B |
3
img/drag-handle.svg
Normal file
3
img/drag-handle.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.7 10.7" width="16" height="16">
|
||||
<path d="M1.34 3.34h8.02v1.34H1.34zM1.34 6.02h8.02v1.34H1.34z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 162 B |
|
@ -107,7 +107,7 @@ class PageController extends Controller {
|
|||
*/
|
||||
public function index(): TemplateResponse {
|
||||
Util::addScript($this->appName, 'forms');
|
||||
Util::addStyle($this->appName, 'icons');
|
||||
Util::addStyle($this->appName, 'forms');
|
||||
return new TemplateResponse($this->appName, 'main');
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ class PageController extends Controller {
|
|||
*/
|
||||
public function createForm(): TemplateResponse {
|
||||
Util::addScript($this->appName, 'forms');
|
||||
Util::addStyle($this->appName, 'icons');
|
||||
Util::addStyle($this->appName, 'forms');
|
||||
return new TemplateResponse($this->appName, 'main');
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ class PageController extends Controller {
|
|||
*/
|
||||
public function cloneForm(): TemplateResponse {
|
||||
Util::addScript($this->appName, 'forms');
|
||||
Util::addStyle($this->appName, 'icons');
|
||||
Util::addStyle($this->appName, 'forms');
|
||||
return new TemplateResponse($this->appName, 'main');
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ class PageController extends Controller {
|
|||
*/
|
||||
public function editForm(): TemplateResponse {
|
||||
Util::addScript($this->appName, 'forms');
|
||||
Util::addStyle($this->appName, 'icons');
|
||||
Util::addStyle($this->appName, 'forms');
|
||||
return new TemplateResponse($this->appName, 'main');
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ class PageController extends Controller {
|
|||
*/
|
||||
public function getResult(): TemplateResponse {
|
||||
Util::addScript($this->appName, 'forms');
|
||||
Util::addStyle($this->appName, 'icons');
|
||||
Util::addStyle($this->appName, 'forms');
|
||||
return new TemplateResponse($this->appName, 'main');
|
||||
}
|
||||
|
||||
|
|
36
package-lock.json
generated
36
package-lock.json
generated
|
@ -1658,9 +1658,15 @@
|
|||
}
|
||||
},
|
||||
"core-js": {
|
||||
<<<<<<< HEAD
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
||||
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
|
||||
=======
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.1.tgz",
|
||||
"integrity": "sha512-186WjSik2iTGfDjfdCZAxv2ormxtKgemjC3SI6PL31qOA0j5LhTDVjHChccoc7brwLvpvLPiMyRlcO88C4l1QQ=="
|
||||
>>>>>>> f89f534... fixup! New question ui
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -3469,9 +3475,15 @@
|
|||
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="
|
||||
},
|
||||
"core-js": {
|
||||
<<<<<<< HEAD
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
|
||||
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
|
||||
=======
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
|
||||
"integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw=="
|
||||
>>>>>>> f89f534... fixup! New question ui
|
||||
},
|
||||
"core-js-compat": {
|
||||
"version": "3.6.5",
|
||||
|
@ -3591,6 +3603,11 @@
|
|||
"randomfill": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"crypto-js": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.0.0.tgz",
|
||||
"integrity": "sha512-bzHZN8Pn+gS7DQA6n+iUmBfl0hO5DJq++QP3U6uTucDtk/0iGpXd/Gg7CGR0p8tJhofJyaKoWBuJI4eAO00BBg=="
|
||||
},
|
||||
"css-loader": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.5.2.tgz",
|
||||
|
@ -9374,9 +9391,9 @@
|
|||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
"version": "0.13.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
|
||||
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.14.4",
|
||||
|
@ -10001,6 +10018,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"sortablejs": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
|
||||
"integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
|
||||
},
|
||||
"source-list-map": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
|
||||
|
@ -11866,6 +11888,14 @@
|
|||
"date-format-parse": "^0.2.5"
|
||||
}
|
||||
},
|
||||
"vuedraggable": {
|
||||
"version": "2.23.2",
|
||||
"resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.23.2.tgz",
|
||||
"integrity": "sha512-PgHCjUpxEAEZJq36ys49HfQmXglattf/7ofOzUrW2/rRdG7tu6fK84ir14t1jYv4kdXewTEa2ieKEAhhEMdwkQ==",
|
||||
"requires": {
|
||||
"sortablejs": "^1.10.1"
|
||||
}
|
||||
},
|
||||
"watchpack": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
|
||||
|
|
|
@ -77,11 +77,16 @@
|
|||
"@nextcloud/moment": "^1.1.1",
|
||||
"@nextcloud/router": "^1.0.2",
|
||||
"@nextcloud/vue": "^1.5.0",
|
||||
"core-js": "^3.6.4",
|
||||
"crypto-js": "^4.0.0",
|
||||
"debounce": "^1.2.0",
|
||||
"json2csv": "5.0.0",
|
||||
"regenerator-runtime": "^0.13.5",
|
||||
"v-click-outside": "^3.0.1",
|
||||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-router": "^3.1.6"
|
||||
"vue-router": "^3.1.6",
|
||||
"vuedraggable": "^2.23.2"
|
||||
},
|
||||
"browserslist": [
|
||||
"extends @nextcloud/browserslist-config"
|
||||
|
|
|
@ -21,9 +21,11 @@
|
|||
-->
|
||||
|
||||
<template>
|
||||
<div class="emptycontent" role="note">
|
||||
<div :class="icon" role="img" />
|
||||
<h2><slot /></h2>
|
||||
<div class="empty-content" role="note">
|
||||
<div class="empty-content__icon" :class="icon" role="img" />
|
||||
<h2 class="empty-content__title">
|
||||
<slot />
|
||||
</h2>
|
||||
<p v-show="$slots.desc">
|
||||
<slot name="desc" />
|
||||
</p>
|
||||
|
@ -44,10 +46,24 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.emptycontent {
|
||||
.empty-content {
|
||||
margin-top: 20vh;
|
||||
button {
|
||||
margin: 3px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&__icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin: 0 auto 15px;
|
||||
opacity: .4;
|
||||
background-size: 64px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
144
src/components/Questions/Question.vue
Normal file
144
src/components/Questions/Question.vue
Normal file
|
@ -0,0 +1,144 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<li v-click-outside="disableEdit"
|
||||
:class="{ 'question--edit': edit }"
|
||||
class="question"
|
||||
@click="enableEdit">
|
||||
<!-- TODO: implement arrow key mapping to reorder question -->
|
||||
<div class="question__drag-handle icon-drag-handle"
|
||||
:aria-label="t('forms', 'Drag to re-order the questions')" />
|
||||
<input v-if="edit"
|
||||
:value="title"
|
||||
class="question__title"
|
||||
type="text"
|
||||
minlength="1"
|
||||
maxlength="256"
|
||||
@input="onInput">
|
||||
<h3 v-else class="question__title" v-text="title" />
|
||||
<slot />
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { directive as ClickOutside } from 'v-click-outside'
|
||||
|
||||
export default {
|
||||
name: 'Question',
|
||||
|
||||
directives: {
|
||||
ClickOutside,
|
||||
},
|
||||
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
edit: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onInput({ target }) {
|
||||
this.$emit('update:title', target.value)
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable the edit mode
|
||||
*/
|
||||
enableEdit() {
|
||||
this.$emit('update:edit', true)
|
||||
},
|
||||
|
||||
/**
|
||||
* Disable the edit mode
|
||||
*/
|
||||
disableEdit() {
|
||||
this.$emit('update:edit', false)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.question {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
margin-bottom: 22px;
|
||||
padding-left: 44px;
|
||||
user-select: none;
|
||||
background-color: var(--color-main-background);
|
||||
|
||||
> * {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__drag-handle {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 44px;
|
||||
height: 100%;
|
||||
cursor: grab;
|
||||
&:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
&__title,
|
||||
&__content {
|
||||
flex: 1 1 100%;
|
||||
max-width: 100%;
|
||||
margin: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Using type to have a higher order than the input styling of server
|
||||
&__title,
|
||||
&__title[type=text] {
|
||||
flex: 1 1 100%;
|
||||
width: auto;
|
||||
max-width: calc(100% - 44px);
|
||||
min-height: 22px;
|
||||
margin: 20px;
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
padding-bottom: 6px;
|
||||
color: var(--color-text-light);
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
&__title[type=text] {
|
||||
border-bottom: 1px dotted var(--color-border-dark);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
91
src/components/Questions/QuestionLong.vue
Normal file
91
src/components/Questions/QuestionLong.vue
Normal file
|
@ -0,0 +1,91 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<Question :title="title" :edit.sync="edit" @update:title="onTitleChange">
|
||||
<div class="question__content">
|
||||
<!-- TODO: properly choose max length -->
|
||||
<textarea ref="textarea"
|
||||
:aria-label="t('forms', 'A long answer for the question “{title}”', { title })"
|
||||
:placeholder="t('forms', 'Long answer text')"
|
||||
:readonly="edit"
|
||||
:value="values[0]"
|
||||
class="question__text"
|
||||
maxlength="1024"
|
||||
minlength="1"
|
||||
@input="onInput"
|
||||
@keydown="autoSizeText" />
|
||||
</div>
|
||||
</Question>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QuestionMixin from '../../mixins/QuestionMixin'
|
||||
|
||||
export default {
|
||||
name: 'QuestionLong',
|
||||
|
||||
mixins: [QuestionMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
height: 1,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.autoSizeText()
|
||||
},
|
||||
|
||||
methods: {
|
||||
onInput() {
|
||||
const textarea = this.$refs.textarea
|
||||
this.$emit('update:values', [textarea.value])
|
||||
this.autoSizeText()
|
||||
},
|
||||
autoSizeText() {
|
||||
const textarea = this.$refs.textarea
|
||||
textarea.style.cssText = 'height:auto; padding:0'
|
||||
textarea.style.cssText = `height: ${textarea.scrollHeight + 20}px`
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// Using type to have a higher order than the input styling of server
|
||||
.question__text {
|
||||
// make sure height calculations are correct
|
||||
box-sizing: content-box !important;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 44px;
|
||||
max-height: 10rem;
|
||||
margin: 0;
|
||||
padding: 6px 0;
|
||||
border: 0;
|
||||
border-bottom: 1px dotted var(--color-border-dark);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
</style>
|
203
src/components/Questions/QuestionMultiple.vue
Normal file
203
src/components/Questions/QuestionMultiple.vue
Normal file
|
@ -0,0 +1,203 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<Question :title="title" :edit.sync="edit" @update:title="onTitleChange">
|
||||
<ul class="question__content">
|
||||
<template v-for="(answer, index) in values">
|
||||
<li :key="index" class="question__item">
|
||||
<input :id="`${id}-check-${index}`"
|
||||
ref="checkbox"
|
||||
:checked="false"
|
||||
:readonly="true"
|
||||
type="checkbox"
|
||||
class="checkbox question__checkbox">
|
||||
<label v-if="!edit"
|
||||
ref="label"
|
||||
:for="`${id}-check-${index}`"
|
||||
class="question__label">{{ answer }}</label>
|
||||
<!-- TODO: properly choose max length -->
|
||||
<input v-else
|
||||
ref="input"
|
||||
:aria-label="t('forms', 'An answer for checkbox {index}', { index: index + 1 })"
|
||||
:placeholder="t('forms', 'Answer for checkbox {index}', { index: index + 1 })"
|
||||
:value="answer"
|
||||
class="question__input"
|
||||
maxlength="256"
|
||||
minlength="1"
|
||||
type="text"
|
||||
@input="onInput(index)"
|
||||
@keydown.enter.prevent="addNewEntry"
|
||||
@keydown.delete="deleteEntry($event, index)">
|
||||
|
||||
<!-- Delete answer -->
|
||||
<Actions v-if="edit">
|
||||
<ActionButton icon="icon-close" @click="deleteEntry($event, index)">
|
||||
{{ t('forms', 'Delete answer') }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</li>
|
||||
</template>
|
||||
<li v-if="edit && !isLastEmpty" class="question__item">
|
||||
<!-- TODO: properly choose max length -->
|
||||
<input
|
||||
:aria-label="t('forms', 'Add a new checkbox')"
|
||||
:placeholder="t('forms', 'Add a new checkbox')"
|
||||
class="question__input"
|
||||
maxlength="256"
|
||||
minlength="1"
|
||||
type="text"
|
||||
@click="addNewEntry">
|
||||
</li>
|
||||
</ul>
|
||||
</Question>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Actions from '@nextcloud/vue/dist/Components/Actions'
|
||||
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
||||
|
||||
import QuestionMixin from '../../mixins/QuestionMixin'
|
||||
import GenRandomId from '../../utils/GenRandomId'
|
||||
|
||||
export default {
|
||||
name: 'QuestionMultiple',
|
||||
|
||||
components: {
|
||||
Actions,
|
||||
ActionButton,
|
||||
},
|
||||
|
||||
mixins: [QuestionMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
id: GenRandomId(),
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isLastEmpty() {
|
||||
const value = this.values[this.values.length - 1]
|
||||
return value && value.trim().length === 0
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
edit(edit) {
|
||||
if (!edit) {
|
||||
// Filter and update questions
|
||||
this.$emit('update:values', this.values.filter(answer => !!answer))
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onInput(index) {
|
||||
// Update values
|
||||
const input = this.$refs.input[index]
|
||||
const values = this.values.slice()
|
||||
values[index] = input.value
|
||||
|
||||
// Update question
|
||||
this.$emit('update:values', values)
|
||||
},
|
||||
|
||||
addNewEntry() {
|
||||
// Add entry
|
||||
const values = this.values.slice()
|
||||
values.push('')
|
||||
|
||||
// Update question
|
||||
this.$emit('update:values', values)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.focusIndex(values.length - 1)
|
||||
})
|
||||
},
|
||||
|
||||
deleteEntry(e, index) {
|
||||
const input = this.$refs.input[index]
|
||||
|
||||
if (input.value.length === 0) {
|
||||
// Dismiss delete action
|
||||
e.preventDefault()
|
||||
|
||||
// Remove entry
|
||||
const values = this.values.slice()
|
||||
values.splice(index, 1)
|
||||
|
||||
// Update question
|
||||
this.$emit('update:values', values)
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.focusNext(index)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the input matching the index
|
||||
*
|
||||
* @param {Number} index the value index
|
||||
*/
|
||||
focusIndex(index) {
|
||||
const input = this.$refs.input[index]
|
||||
if (input) {
|
||||
input.focus()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.question__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.question__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 44px;
|
||||
|
||||
.question__label {
|
||||
flex: 1 1 100%;
|
||||
&::before {
|
||||
margin: 14px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Using type to have a higher order than the input styling of server
|
||||
.question__input[type=text] {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
margin: 0;
|
||||
padding: 6px 0;
|
||||
border: 0;
|
||||
border-bottom: 1px dotted var(--color-border-dark);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
</style>
|
70
src/components/Questions/QuestionShort.vue
Normal file
70
src/components/Questions/QuestionShort.vue
Normal file
|
@ -0,0 +1,70 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<Question :title="title" :edit.sync="edit" @update:title="onTitleChange">
|
||||
<div class="question__content">
|
||||
<!-- TODO: properly choose max length -->
|
||||
<input ref="input"
|
||||
:aria-label="t('forms', 'A short answer for the question “{title}”', { title })"
|
||||
:placeholder="t('forms', 'Short answer text')"
|
||||
:readonly="edit"
|
||||
:value="values[0]"
|
||||
class="question__input"
|
||||
maxlength="256"
|
||||
minlength="1"
|
||||
type="text"
|
||||
@input="onInput">
|
||||
</div>
|
||||
</Question>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import QuestionMixin from '../../mixins/QuestionMixin'
|
||||
|
||||
export default {
|
||||
name: 'QuestionShort',
|
||||
|
||||
mixins: [QuestionMixin],
|
||||
|
||||
methods: {
|
||||
onInput() {
|
||||
const input = this.$refs.input
|
||||
this.$emit('update:values', [input.value])
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// Using type to have a higher order than the input styling of server
|
||||
.question__input[type=text] {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
margin: 0;
|
||||
padding: 6px 0;
|
||||
border: 0;
|
||||
border-bottom: 1px dotted var(--color-border-dark);
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -35,15 +35,18 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$top-bar-height: 60px;
|
||||
|
||||
.top-bar {
|
||||
position: absolute;
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
top: 0;
|
||||
right: 0;
|
||||
top: var(--header-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: flex-end;
|
||||
justify-content: flex-end;
|
||||
height: 60px;
|
||||
height: var(--top-bar-height);
|
||||
margin-top: calc(var(--top-bar-height) * -1);
|
||||
padding: 0 6px;
|
||||
|
||||
button {
|
||||
|
@ -51,8 +54,8 @@ export default {
|
|||
&:not(.primary) {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
&:hover, a:active, a:focus {
|
||||
background-color: var(--color-background-darker);
|
||||
}
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
/* global Vue, oc_userconfig */
|
||||
<template>
|
||||
<div class="cloud">
|
||||
<span v-if="options.expired" class="expired">
|
||||
{{ t('forms', 'Expired') }}
|
||||
</span>
|
||||
<span v-if="options.expires" class="open">
|
||||
{{ t('forms', 'Expires %n', 1, expirationDate) }}
|
||||
</span>
|
||||
<span v-else class="open">
|
||||
{{ t('forms', 'Expires never') }}
|
||||
</span>
|
||||
|
||||
<span class="information">
|
||||
{{ options.access }}
|
||||
</span>
|
||||
<span v-if="options.isAnonymous" class="information">
|
||||
{{ t('forms', 'Anonymous form') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from '@nextcloud/moment'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
expirationDate() {
|
||||
const date = moment(this.options.expirationDate, moment.localeData().longDateFormat('L')).fromNow()
|
||||
return date
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,114 +0,0 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="controls">
|
||||
<div class="breadcrumb">
|
||||
<button class="button btn primary" @click="helpPage">
|
||||
{{ "Help" }}
|
||||
</button>
|
||||
<div class="crumb svg crumbhome">
|
||||
<router-link :to="{ name: 'list'}" class="icon-home">
|
||||
Home
|
||||
</router-link>
|
||||
</div>
|
||||
<div v-show="intitle ===''" class="crumb svg last">
|
||||
<span v-text="intitle" />
|
||||
</div>
|
||||
<div class="action">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<slot name="after" class="after" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
props: {
|
||||
intitle: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imagePath: OC.imagePath('core', 'places/home.svg'),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
helpPage() {
|
||||
window.open('https://github.com/nextcloud/forms/blob/master/Forms_Support.md')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
position: fixed;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-main-background);
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
z-index: 1001;
|
||||
|
||||
.action {
|
||||
order: 999;
|
||||
}
|
||||
|
||||
.button, button {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
height: 36px;
|
||||
padding: 9px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 7px;
|
||||
&.symbol {
|
||||
width: 36px;
|
||||
}
|
||||
&.primary {
|
||||
background: var(--color-primary);
|
||||
color: var(--color-primary-text);
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
min-width: 35px;
|
||||
// div.crumb:last-child {
|
||||
// flex-shrink: 1;
|
||||
// overflow: hidden;
|
||||
// > span {
|
||||
// flex-shrink: 1;
|
||||
// text-overflow: ellipsis;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<div class="loading-overlay">
|
||||
<span class="icon-loading" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
opacity: 0.9;
|
||||
z-index: 1001;
|
||||
.icon-loading {
|
||||
position: fixed;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -35px;
|
||||
margin-top: -10px;
|
||||
&::after {
|
||||
border: 10px solid var(--color-loading-light);
|
||||
border-top-color: var(--color-primary-element);
|
||||
height: 70px;
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,495 +0,0 @@
|
|||
<!--
|
||||
- @copyright Copyright (c) 2018 René Gieling <github@dartcafe.de>
|
||||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
- @author Natalie Gilbert
|
||||
- @author Nick Gallo
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
- This program is free software: you can redistribute it and/or modify
|
||||
- it under the terms of the GNU Affero General Public License as
|
||||
- published by the Free Software Foundation, either version 3 of the
|
||||
- License, or (at your option) any later version.
|
||||
-
|
||||
- This program is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
- GNU Affero General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Affero General Public License
|
||||
- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div v-if="header" class="wrapper group-master table-row table-header">
|
||||
<div class="wrapper group-1">
|
||||
<div class="wrapper group-1-1">
|
||||
<div class="name">
|
||||
{{ t('forms', 'Title') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper group-2">
|
||||
<div class="wrapper-group-2-5">
|
||||
<div class="deletetwo">
|
||||
{{ t('forms',"Delete form") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper-group-2-5">
|
||||
<div class="copyL">
|
||||
{{ t('forms', "Copy link") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper-group-2-5">
|
||||
<div class="result">
|
||||
{{ t('forms', "View results") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper group-2-1">
|
||||
<div class="access">
|
||||
{{ t('forms', 'Access') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="owner">
|
||||
{{ t('forms', 'Owner') }}
|
||||
</div>
|
||||
<div class="wrapper group-2-2">
|
||||
<div class="created">
|
||||
{{ t('forms', 'Created') }}
|
||||
</div>
|
||||
<div class="expiry">
|
||||
{{ t('forms', 'Expires') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="wrapper table-row table-body group-master">
|
||||
<div class="wrapper group-1">
|
||||
<div>
|
||||
<img class="icontwo">
|
||||
</div>
|
||||
<div class="symbol icon-voted" />
|
||||
<a :href="submitUrl" class="wrapper group-1-1">
|
||||
<div class="name">
|
||||
{{ form.form.title }}
|
||||
</div>
|
||||
<div class="description">
|
||||
{{ form.form.description }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="wrapper group-2">
|
||||
<div class="wrapper group-2-8">
|
||||
<a class="icon icon-delete svg delete-form" @click="deleteForm" />
|
||||
<span class="hidden-visually">
|
||||
{{ t('forms', 'Delete form') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="wrapper group-2-9">
|
||||
<a class="icon icon-clippy svg delete-form" @click="copyLink" />
|
||||
<span class="hidden-visually">
|
||||
{{ t('forms', 'Copy link') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="list-results wrapper">
|
||||
<a class="icon icon-toggle svg delete-form" @click="viewResults" />
|
||||
<span class="hidden-visually">
|
||||
{{ t('forms', 'View results') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="wrapper group-2-1">
|
||||
<div v-tooltip="accessType" class="thumbnail access" :class="form.form.access">
|
||||
{{ accessType }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="owner">
|
||||
<UserDiv :user-id="form.form.ownerId" :display-name="form.form.ownerDisplayName" />
|
||||
</div>
|
||||
<div class="wrapper group-2-2">
|
||||
<div class="created ">
|
||||
{{ timeSpanCreated }}
|
||||
</div>
|
||||
<div class="expiry" :class="{ expired : form.form.expired }">
|
||||
{{ timeSpanExpiration }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from '@nextcloud/moment'
|
||||
import UserDiv from '../components/_base-UserDiv'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UserDiv,
|
||||
},
|
||||
props: {
|
||||
header: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
openedMenu: false,
|
||||
hostName: this.$route.query.page,
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
accessType() {
|
||||
if (this.form.form.access === 'public') {
|
||||
return t('forms', 'Public access')
|
||||
} else if (this.form.form.access === 'select') {
|
||||
return t('forms', 'Only shared')
|
||||
} else if (this.form.form.access === 'registered') {
|
||||
return t('forms', 'Registered users only')
|
||||
} else if (this.form.form.access === 'hidden') {
|
||||
return t('forms', 'Hidden form')
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
timeSpanCreated() {
|
||||
return moment(this.form.form.created, 'YYYY-MM-DD HH:mm')
|
||||
},
|
||||
|
||||
timeSpanExpiration() {
|
||||
if (this.form.form.expires) {
|
||||
return moment(this.form.form.expirationDate)
|
||||
} else {
|
||||
return t('forms', 'never')
|
||||
}
|
||||
},
|
||||
|
||||
countShares() {
|
||||
return this.form.shares.length
|
||||
},
|
||||
|
||||
submitUrl() {
|
||||
return OC.generateUrl('apps/forms/form/') + this.form.form.hash
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleMenu() {
|
||||
this.openedMenu = !this.openedMenu
|
||||
},
|
||||
|
||||
hideMenu() {
|
||||
this.openedMenu = false
|
||||
},
|
||||
|
||||
copyLink() {
|
||||
// this.$emit('copyLink')
|
||||
this.$copyText(window.location.origin + this.submitUrl).then(
|
||||
function(e) {
|
||||
OC.Notification.showTemporary(t('forms', 'Link copied to clipboard'))
|
||||
},
|
||||
function(e) {
|
||||
OC.Notification.showTemporary(t('forms', 'Error, while copying link to clipboard'))
|
||||
}
|
||||
)
|
||||
this.hideMenu()
|
||||
},
|
||||
|
||||
deleteForm() {
|
||||
this.$emit('deleteForm')
|
||||
},
|
||||
|
||||
viewResults() {
|
||||
this.$emit('viewResults')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
$row-padding: 15px;
|
||||
$table-padding: 4px;
|
||||
|
||||
$date-width: 130px;
|
||||
$participants-width: 95px;
|
||||
$group-2-2-width: max($date-width, $participants-width);
|
||||
|
||||
$owner-width: 140px;
|
||||
$access-width: 44px;
|
||||
$group-2-1-width: max($access-width, $date-width);
|
||||
$group-2-width: $owner-width + $group-2-1-width + $group-2-2-width;
|
||||
|
||||
$action-width: 44px;
|
||||
$thumbnail-width: 44px;
|
||||
$thumbnail-icon-width: 32px;
|
||||
$name-width: 150px;
|
||||
$description-width: 150px;
|
||||
$group-1-1-width: max($name-width, $description-width);
|
||||
$group-1-width: $thumbnail-width + $group-1-1-width + $action-width;
|
||||
|
||||
$group-master-width: max($group-1-width, $group-2-width);
|
||||
|
||||
$mediabreak-1: ($group-1-width + $owner-width + $access-width + $date-width + $date-width + $participants-width + $row-padding * 2);
|
||||
$mediabreak-2: ($group-1-width + $group-2-width + $row-padding * 2);
|
||||
$mediabreak-3: $group-1-width + $owner-width + max($group-2-1-width, $group-2-2-width) + $row-padding *2 ;
|
||||
|
||||
.table-row {
|
||||
width: 100%;
|
||||
padding-left: $row-padding;
|
||||
padding-right: $row-padding;
|
||||
|
||||
line-height: 2em;
|
||||
transition: background-color 0.3s ease;
|
||||
background-color: var(--color-main-background);
|
||||
min-height: 4em;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
|
||||
&.table-header {
|
||||
.name, .description {
|
||||
padding-left: ($thumbnail-width + $table-padding *2);
|
||||
}
|
||||
.owner {
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.table-body {
|
||||
&:hover, &:focus, &:active, &.mouseOver {
|
||||
transition: background-color 0.3s ease;
|
||||
background-color: var(--color-background-dark);
|
||||
}
|
||||
.icon-more {
|
||||
right: 14px;
|
||||
opacity: 0.3;
|
||||
cursor: pointer;
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.symbol {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.table-header {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.name {
|
||||
width: $name-width;
|
||||
}
|
||||
|
||||
.description {
|
||||
width: $description-width;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.name, .description {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.actions {
|
||||
width: $action-width;
|
||||
position: relative;
|
||||
overflow: initial;
|
||||
}
|
||||
|
||||
.result {
|
||||
width: 60px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.deletetwo {
|
||||
width: 60px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
.copyL {
|
||||
width: 60px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.access {
|
||||
width: $access-width;
|
||||
}
|
||||
|
||||
.owner {
|
||||
width: $owner-width;
|
||||
}
|
||||
|
||||
.created {
|
||||
width: $date-width;
|
||||
}
|
||||
|
||||
.expiry {
|
||||
width: $date-width;
|
||||
&.expired {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-form {
|
||||
padding-right:70px;
|
||||
}
|
||||
|
||||
.list-results {
|
||||
width: 65px;
|
||||
}
|
||||
|
||||
.group-1, .group-1-1 {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.group-1-1 {
|
||||
flex-direction: column;
|
||||
width: $group-1-1-width;
|
||||
> div {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: ($mediabreak-1) ) {
|
||||
.group-1 {
|
||||
width: $group-1-width;
|
||||
}
|
||||
.group-2-1, .group-2-2 {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.created {
|
||||
width: $group-2-1-width;;
|
||||
}
|
||||
.expiry, .participants {
|
||||
width: $group-2-2-width;;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: ($mediabreak-2) ) {
|
||||
.table-row {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.group-2-1 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: ($mediabreak-3) ) {
|
||||
.group-2 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.icontwo {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding-right: 4px;
|
||||
font-size: 0;
|
||||
background-color: var(--color-text-light);
|
||||
mask-image: var(--icon-organization-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-organization-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding-right: 4px;
|
||||
font-size: 0;
|
||||
background-color: var(--color-text-light);
|
||||
&.dateForm {
|
||||
mask-image: var(--icon-calendar-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-calendar-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
&.textForm {
|
||||
mask-image: var(--icon-organization-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-organization-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
&.expired {
|
||||
background-color: var(--color-background-darker);
|
||||
}
|
||||
&.access {
|
||||
display: inherit;
|
||||
&.hidden {
|
||||
mask-image: var(--icon-password-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-password-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
&.public {
|
||||
mask-image: var(--icon-link-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-link-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
&.select {
|
||||
mask-image: var(--icon-share-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-share-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
&.registered {
|
||||
mask-image: var(--icon-group-000) no-repeat 50% 50%;
|
||||
-webkit-mask: var(--icon-group-000) no-repeat 50% 50%;
|
||||
mask-size: 16px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.icon-voted {
|
||||
background-image: var(--icon-checkmark-fff);
|
||||
}
|
||||
|
||||
.app-navigation-entry-utils-counter {
|
||||
padding-right: 0 !important;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
font-size: 9pt;
|
||||
line-height: 44px;
|
||||
padding: 0 12px;
|
||||
// min-width: 25px;
|
||||
&.highlighted {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
span {
|
||||
padding: 2px 5px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-primary-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.symbol.icon-voted {
|
||||
position: absolute;
|
||||
left: 11px;
|
||||
top: 16px;
|
||||
background-size: 0;
|
||||
min-width: 8px;
|
||||
min-height: 8px;
|
||||
background-color: var(--color-success);
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
54
src/mixins/QuestionMixin.js
Normal file
54
src/mixins/QuestionMixin.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import Question from '../components/Questions/Question'
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
values: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
Question,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
edit: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onTitleChange(title) {
|
||||
this.$emit('update:title', title)
|
||||
},
|
||||
onValuesChange(values) {
|
||||
this.$emit('update:values', values)
|
||||
},
|
||||
},
|
||||
}
|
|
@ -24,7 +24,7 @@ export default [
|
|||
{
|
||||
label: t('forms', 'Multiple choice'),
|
||||
value: 'radiogroup',
|
||||
icon: 'icon-forms',
|
||||
icon: 'icon-answer-multiple',
|
||||
},
|
||||
{
|
||||
label: t('forms', 'Checkboxes'),
|
||||
|
|
31
src/utils/GenRandomId.js
Normal file
31
src/utils/GenRandomId.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
const GenRandomId = (length) => {
|
||||
return Math.random()
|
||||
.toString(36)
|
||||
.replace(/[^a-z]+/g, '')
|
||||
.substr(0, length || 5)
|
||||
}
|
||||
|
||||
export default GenRandomId
|
|
@ -3,6 +3,7 @@
|
|||
-
|
||||
- @author René Gieling <github@dartcafe.de>
|
||||
- @author Nick Gallo
|
||||
- @author John Molakvoæ <skjnldsv@protonmail.com>
|
||||
-
|
||||
- @license GNU AGPL version 3 or any later version
|
||||
-
|
||||
|
@ -33,7 +34,9 @@
|
|||
<span class="icon-forms-white" role="img" />
|
||||
{{ t('forms', 'Show results') }}
|
||||
</button>
|
||||
<button v-tooltip="t('forms', 'Toggle settings')" @click="toggleSidebar">
|
||||
<button v-tooltip="t('forms', 'Toggle settings')"
|
||||
:aria-label="t('forms', 'Toggle settings')"
|
||||
@click="toggleSidebar">
|
||||
<span class="icon-settings" role="img" />
|
||||
</button>
|
||||
</TopBar>
|
||||
|
@ -48,30 +51,37 @@
|
|||
:placeholder="t('forms', 'Title')"
|
||||
:required="true"
|
||||
autofocus
|
||||
type="text">
|
||||
type="text"
|
||||
@click="selectIfUnchanged">
|
||||
<label class="hidden-visually" for="form-desc">{{ t('forms', 'Description') }}</label>
|
||||
<textarea
|
||||
id="form-desc"
|
||||
ref="description"
|
||||
v-model="form.form.description"
|
||||
:placeholder="t('forms', 'Description')"
|
||||
@change="autoSizeDescription"
|
||||
@keydown="autoSizeDescription" />
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<!-- Add new questions toolbar -->
|
||||
<!-- <div class="question-toolbar" role="toolbar">
|
||||
<button v-for="type in answerTypes"
|
||||
:key="type.label"
|
||||
class="question-toolbar__question"
|
||||
@click="addQuestion">
|
||||
<span class="question-toolbar__icon" :class="type.icon" />
|
||||
{{ type.label }}
|
||||
</button>
|
||||
</div> -->
|
||||
<div class="question-toolbar" role="toolbar">
|
||||
<Actions ref="questionMenu"
|
||||
v-tooltip="t('forms', 'Add a question to this form')"
|
||||
:aria-label="t('forms', 'Add a question to this form')"
|
||||
:open.sync="questionMenuOpened"
|
||||
default-icon="icon-add-white">
|
||||
<ActionButton v-for="type in answerTypes"
|
||||
:key="type.label"
|
||||
class="question-toolbar__question"
|
||||
:icon="type.icon"
|
||||
@click="addQuestion">
|
||||
{{ type.label }}
|
||||
</ActionButton>
|
||||
</Actions>
|
||||
</div>
|
||||
|
||||
<div id="quiz-form-selector-text">
|
||||
<!--shows inputs for question types: drop down box to select the type, text box for question, and button to add-->
|
||||
<!-- <div id="quiz-form-selector-text">
|
||||
<label for="ans-type">Answer Type: </label>
|
||||
<select v-model="selected">
|
||||
<option value="" disabled>
|
||||
|
@ -90,20 +100,21 @@
|
|||
@click="addQuestion()">
|
||||
{{ t('forms', 'Add Question') }}
|
||||
</button>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- No questions -->
|
||||
<EmptyContent v-if="form.questions.length === 0">
|
||||
{{ t('forms', 'This form does not have any questions') }}
|
||||
<template #desc>
|
||||
<button class="primary" @click="openQuestionMenu">
|
||||
<button class="empty-content__button primary" @click="openQuestionMenu">
|
||||
<span class="icon-add-white" />
|
||||
{{ t('forms', 'Add a new one') }}
|
||||
</button>
|
||||
</template>
|
||||
</EmptyContent>
|
||||
|
||||
<!-- Questions list -->
|
||||
<transitionGroup
|
||||
<!-- <transitionGroup
|
||||
v-else
|
||||
id="form-list"
|
||||
name="list"
|
||||
|
@ -117,18 +128,29 @@
|
|||
@addOption="addOption"
|
||||
@deleteOption="deleteOption"
|
||||
@deleteQuestion="deleteQuestion(question, index)" />
|
||||
</transitionGroup>
|
||||
</transitionGroup> -->
|
||||
|
||||
<Draggable v-model="questions"
|
||||
:animation="200"
|
||||
tag="ul"
|
||||
@start="dragging = true"
|
||||
@end="dragging = false">
|
||||
<Questions :is="question.type"
|
||||
v-for="question in questions"
|
||||
:key="question.id"
|
||||
v-bind.sync="question" />
|
||||
</Draggable>
|
||||
</section>
|
||||
</AppContent>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import axios from '@nextcloud/axios'
|
||||
import moment from '@nextcloud/moment'
|
||||
import { emit } from '@nextcloud/event-bus'
|
||||
import { showError } from '@nextcloud/dialogs'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
import { showError, showSuccess } from '@nextcloud/dialogs'
|
||||
import axios from '@nextcloud/axios'
|
||||
import debounce from 'debounce'
|
||||
import Draggable from 'vuedraggable'
|
||||
|
||||
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
||||
import Actions from '@nextcloud/vue/dist/Components/Actions'
|
||||
|
@ -136,9 +158,12 @@ import AppContent from '@nextcloud/vue/dist/Components/AppContent'
|
|||
|
||||
import answerTypes from '../models/AnswerTypes'
|
||||
import EmptyContent from '../components/EmptyContent'
|
||||
import Question from '../components/Questions/Question'
|
||||
import QuestionLong from '../components/Questions/QuestionLong'
|
||||
import QuestionShort from '../components/Questions/QuestionShort'
|
||||
import QuestionMultiple from '../components/Questions/QuestionMultiple'
|
||||
import QuizFormItem from '../components/quizFormItem'
|
||||
import TopBar from '../components/TopBar'
|
||||
|
||||
import ViewsMixin from '../mixins/ViewsMixin'
|
||||
|
||||
export default {
|
||||
|
@ -147,7 +172,12 @@ export default {
|
|||
ActionButton,
|
||||
Actions,
|
||||
AppContent,
|
||||
Draggable,
|
||||
EmptyContent,
|
||||
Question,
|
||||
QuestionLong,
|
||||
QuestionShort,
|
||||
QuestionMultiple,
|
||||
QuizFormItem,
|
||||
TopBar,
|
||||
},
|
||||
|
@ -156,6 +186,7 @@ export default {
|
|||
|
||||
data() {
|
||||
return {
|
||||
questionMenuOpened: false,
|
||||
placeholder: '',
|
||||
newOption: '',
|
||||
newQuestion: '',
|
||||
|
@ -167,14 +198,28 @@ export default {
|
|||
uniqueQuestionText: false,
|
||||
uniqueOptionText: false,
|
||||
allHaveOpt: false,
|
||||
questionTypes: [
|
||||
{ text: 'Radio Buttons', value: 'radiogroup' },
|
||||
{ text: 'Checkboxes', value: 'checkbox' },
|
||||
{ text: 'Short Response', value: 'text' },
|
||||
{ text: 'Long Response', value: 'comment' },
|
||||
{ text: 'Drop Down', value: 'dropdown' },
|
||||
],
|
||||
answerTypes,
|
||||
questions: [
|
||||
{
|
||||
id: 1,
|
||||
type: QuestionShort,
|
||||
title: 'How old are you ?',
|
||||
values: ['I\'m 48 years old'],
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: QuestionLong,
|
||||
title: 'Your latest best memory ?',
|
||||
values: ['One day I was at the beach.\nIt was fun. The sun was shinning.\nThe water was warm'],
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: QuestionMultiple,
|
||||
title: 'Choose an answer ?',
|
||||
values: ['Answer 1', 'Answer 2', 'Answer 3', 'Answer 4'],
|
||||
},
|
||||
],
|
||||
dragging: false,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -197,6 +242,7 @@ export default {
|
|||
return t('forms', 'Done')
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -221,6 +267,10 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.autoSizeDescription()
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
switchSidebar() {
|
||||
|
@ -252,10 +302,10 @@ export default {
|
|||
order: respData.order,
|
||||
text: this.newQuestion,
|
||||
type: this.selected,
|
||||
options: [],
|
||||
answers: [],
|
||||
})
|
||||
}
|
||||
this.newQuestion = ''
|
||||
this.newQuizQuestion = ''
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -366,7 +416,21 @@ export default {
|
|||
* Add question methods
|
||||
*/
|
||||
openQuestionMenu() {
|
||||
this.$refs.questionMenu.opened = true
|
||||
// TODO: fix the vue components to allow external click triggers without
|
||||
// conflicting with the click outside directive
|
||||
setTimeout(() => {
|
||||
this.questionMenuOpened = true
|
||||
}, 100)
|
||||
},
|
||||
|
||||
/**
|
||||
* Select the text in the input if it is still set to 'New form'
|
||||
* @param {Event} e the click event
|
||||
*/
|
||||
selectIfUnchanged(e) {
|
||||
if (e.target && e.target.value === t('forms', 'New form')) {
|
||||
e.target.select()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -375,14 +439,16 @@ export default {
|
|||
<style lang="scss">
|
||||
#app-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
header,
|
||||
section {
|
||||
width: 100%;
|
||||
max-width: 900px;
|
||||
}
|
||||
|
||||
// Title & description header
|
||||
header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -391,30 +457,69 @@ export default {
|
|||
#form-title,
|
||||
#form-desc {
|
||||
width: 100%;
|
||||
border: none;
|
||||
margin: 10px; // aerate the header
|
||||
padding: 0; // makes alignment and desc height calc easier
|
||||
border: none;
|
||||
}
|
||||
#form-title {
|
||||
font-size: 2em;
|
||||
}
|
||||
#form-desc {
|
||||
// make sure height calculations are correct
|
||||
box-sizing: content-box !important;
|
||||
min-height: 60px;
|
||||
max-height: 200px;
|
||||
padding-left: 2px; // align with title (compensate font size diff)
|
||||
resize: none
|
||||
resize: none;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-content__button {
|
||||
margin: 5px;
|
||||
> span {
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Questions container
|
||||
section {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 250px;
|
||||
|
||||
.question-toolbar {
|
||||
position: sticky;
|
||||
z-index: 50;
|
||||
top: var(--header-height);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-self: flex-end;
|
||||
width: 44px;
|
||||
height: var(--top-bar-height);
|
||||
// make sure this doesn't take any space and appear floating
|
||||
margin-top: -44px;
|
||||
.icon-add-white {
|
||||
opacity: 1;
|
||||
border-radius: 50%;
|
||||
// TODO: standardize on components
|
||||
background-color: var(--color-primary-element);
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background-color: var(--color-primary-element-light) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Transitions for inserting and removing list items */
|
||||
.list-enter-active,
|
||||
.list-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
transition: all .5s ease;
|
||||
}
|
||||
|
||||
.list-enter,
|
||||
|
@ -423,9 +528,8 @@ export default {
|
|||
}
|
||||
|
||||
.list-move {
|
||||
transition: transform 0.5s;
|
||||
transition: transform .5s;
|
||||
}
|
||||
/* */
|
||||
|
||||
#form-item-selector-text {
|
||||
> input {
|
||||
|
@ -436,38 +540,37 @@ export default {
|
|||
.form-table {
|
||||
> li {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
line-height: 24px;
|
||||
min-height: 24px;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
overflow: hidden;
|
||||
align-items: baseline;
|
||||
min-height: 24px;
|
||||
padding-right: 8px;
|
||||
padding-left: 8px;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
line-height: 24px;
|
||||
|
||||
&:active,
|
||||
&:hover {
|
||||
transition: var(--background-dark) 0.3s ease;
|
||||
transition: var(--background-dark) .3s ease;
|
||||
background-color: var(--color-background-dark); //$hover-color;
|
||||
|
||||
}
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
font-size: 1.2em;
|
||||
opacity: 0.7;
|
||||
white-space: normal;
|
||||
padding-right: 4px;
|
||||
white-space: normal;
|
||||
opacity: .7;
|
||||
font-size: 1.2em;
|
||||
&.avatar {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> div:nth-last-child(1) {
|
||||
justify-content: center;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -493,13 +596,14 @@ button {
|
|||
}
|
||||
|
||||
#shiftDates {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 10px center;
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
padding-left: 34px;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 10px center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<div v-for="sum in stats" :key="sum">
|
||||
{{ sum }}
|
||||
</div>
|
||||
<div id="app-content">
|
||||
<div id="app-content" :class="{'icon-loading': loading}">
|
||||
<transition-group
|
||||
name="list"
|
||||
tag="div"
|
||||
|
@ -48,7 +48,6 @@
|
|||
:answer="answer"
|
||||
@viewResults="viewFormResults(index, form.form, 'results')" />
|
||||
</transition-group>
|
||||
<LoadingOverlay v-if="loading" />
|
||||
<modal-dialog />
|
||||
</div>
|
||||
</AppContent>
|
||||
|
@ -58,7 +57,6 @@
|
|||
import ResultItem from '../components/resultItem'
|
||||
import json2csvParser from 'json2csv'
|
||||
import axios from '@nextcloud/axios'
|
||||
import LoadingOverlay from '../components/_base-LoadingOverlay'
|
||||
import ViewsMixin from '../mixins/ViewsMixin'
|
||||
import { generateUrl } from '@nextcloud/router'
|
||||
|
||||
|
@ -69,7 +67,6 @@ export default {
|
|||
components: {
|
||||
AppContent,
|
||||
ResultItem,
|
||||
LoadingOverlay,
|
||||
},
|
||||
|
||||
mixins: [ViewsMixin],
|
||||
|
|
Loading…
Reference in a new issue