@afeefa/vue-app 0.0.54 → 0.0.57
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.
- package/.afeefa/package/release/version.txt +1 -1
- package/package.json +1 -1
- package/src/components/AContextMenu.vue +1 -1
- package/src/components/AContextMenuItem.vue +1 -1
- package/src/components/ADialog.vue +2 -0
- package/src/components/ARichTextArea.vue +257 -0
- package/src/components/ASearchSelect.vue +12 -12
- package/src/components/form/EditForm.vue +1 -5
- package/src/components/form/EditModal.vue +22 -17
- package/src/components/form/FormFieldMixin.js +2 -3
- package/src/components/form/fields/FormFieldRichTextArea.vue +14 -0
- package/src/components/index.js +2 -0
- package/src/components/list/ListViewMixin.js +4 -0
- package/src/components/list/filters/ListFilterSelect.vue +4 -2
- package/src/components/search-select/SearchSelectList.vue +5 -1
- package/src/index.js +2 -0
- package/src/plugins/api-resources/ApiResourcesPlugin.js +12 -0
- package/src/styles/forms.scss +8 -0
- package/src/styles/vue-app.scss +1 -0
- package/src-admin/bootstrap.js +2 -5
- package/src-admin/components/controls/SearchSelectFormField.vue +223 -0
- package/src-admin/components/detail/DetailProperty.vue +1 -3
- package/src-admin/components/list/ListColumnHeader.vue +4 -3
- package/src-admin/components/list/ListView.vue +15 -5
- package/src-admin/components/pages/CreatePage.vue +1 -2
- package/src-admin/components/pages/DetailPage.vue +3 -3
- package/src-admin/components/pages/EditPage.vue +4 -5
- package/src-admin/components/pages/ListPage.vue +2 -3
- package/src-admin/components/routes/DetailRoute.vue +3 -3
- package/src-admin/components/routes/EditRoute.vue +3 -3
- package/src-admin/components/routes/ListRoute.vue +3 -3
- package/src-admin/config/vuetify.js +3 -1
- package/src-admin/models/Model.js +13 -0
- package/src-admin/models/ModelAdminConfig.js +20 -0
- package/src-components/AMdiIcon.vue +18 -0
| @@ -1 +1 @@ | |
| 1 | 
            -
            0.0. | 
| 1 | 
            +
            0.0.57
         | 
    
        package/package.json
    CHANGED
    
    
| @@ -29,7 +29,7 @@ export default class AContextMenuItem extends Vue { | |
| 29 29 | 
             
                this.contextMenu.close()
         | 
| 30 30 | 
             
                if (this.to) {
         | 
| 31 31 | 
             
                  this.$router.push(this.to)
         | 
| 32 | 
            -
                    .catch(() => null) // prevent duplicated navigation
         | 
| 32 | 
            +
                    .catch(() => null) // prevent duplicated navigation warning
         | 
| 33 33 | 
             
                } else {
         | 
| 34 34 | 
             
                  this.$emit('click')
         | 
| 35 35 | 
             
                }
         | 
| @@ -132,6 +132,8 @@ export default class ADialog extends Mixins(UsesPositionServiceMixin) { | |
| 132 132 | 
             
                if (!Array.isArray(anchor)) {
         | 
| 133 133 | 
             
                  if (typeof anchor === 'string') {
         | 
| 134 134 | 
             
                    anchor = [document.documentElement, anchor]
         | 
| 135 | 
            +
                  } else if (typeof anchor === 'object') { // dom element or vue ref
         | 
| 136 | 
            +
                    anchor = [anchor]
         | 
| 135 137 | 
             
                  } else {
         | 
| 136 138 | 
             
                    anchor = [document.documentElement]
         | 
| 137 139 | 
             
                  }
         | 
| @@ -0,0 +1,257 @@ | |
| 1 | 
            +
            <template>
         | 
| 2 | 
            +
              <div :class="['a-rich-text-editor a-text-input', {'a-text-input-focused': focus}]">
         | 
| 3 | 
            +
                <div
         | 
| 4 | 
            +
                  v-if="editor"
         | 
| 5 | 
            +
                  class="menu-bar"
         | 
| 6 | 
            +
                >
         | 
| 7 | 
            +
                  <v-btn
         | 
| 8 | 
            +
                    small
         | 
| 9 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('bold')}]"
         | 
| 10 | 
            +
                    @click="editor.chain().focus().toggleBold().run()"
         | 
| 11 | 
            +
                  >
         | 
| 12 | 
            +
                    <v-icon>{{ boldIcon }}</v-icon>
         | 
| 13 | 
            +
                  </v-btn>
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  <v-btn
         | 
| 16 | 
            +
                    small
         | 
| 17 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('italic')}]"
         | 
| 18 | 
            +
                    @click="editor.chain().focus().toggleItalic().run()"
         | 
| 19 | 
            +
                  >
         | 
| 20 | 
            +
                    <v-icon>{{ italicIcon }}</v-icon>
         | 
| 21 | 
            +
                  </v-btn>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  <v-btn
         | 
| 24 | 
            +
                    small
         | 
| 25 | 
            +
                    :class="['menu-button', 'strike', {'is-active': focus && editor.isActive('strike')}]"
         | 
| 26 | 
            +
                    @click="editor.chain().focus().toggleStrike().run()"
         | 
| 27 | 
            +
                  >
         | 
| 28 | 
            +
                    <v-icon>{{ strikeIcon }}</v-icon>
         | 
| 29 | 
            +
                  </v-btn>
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  <v-btn
         | 
| 32 | 
            +
                    small
         | 
| 33 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('heading', {level: 1})}]"
         | 
| 34 | 
            +
                    @click="editor.chain().focus().toggleHeading({level: 1}).run()"
         | 
| 35 | 
            +
                  >
         | 
| 36 | 
            +
                    <v-icon>{{ h1Icon }}</v-icon>
         | 
| 37 | 
            +
                  </v-btn>
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  <v-btn
         | 
| 40 | 
            +
                    small
         | 
| 41 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('heading', {level: 2})}]"
         | 
| 42 | 
            +
                    @click="editor.chain().focus().toggleHeading({level: 2}).run()"
         | 
| 43 | 
            +
                  >
         | 
| 44 | 
            +
                    <v-icon>{{ h2Icon }}</v-icon>
         | 
| 45 | 
            +
                  </v-btn>
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  <v-btn
         | 
| 48 | 
            +
                    small
         | 
| 49 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('bulletList')}]"
         | 
| 50 | 
            +
                    @click="editor.chain().focus().toggleBulletList().run()"
         | 
| 51 | 
            +
                  >
         | 
| 52 | 
            +
                    <v-icon>{{ ulIcon }}</v-icon>
         | 
| 53 | 
            +
                  </v-btn>
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  <v-btn
         | 
| 56 | 
            +
                    small
         | 
| 57 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('orderedList')}]"
         | 
| 58 | 
            +
                    @click="editor.chain().focus().toggleOrderedList().run()"
         | 
| 59 | 
            +
                  >
         | 
| 60 | 
            +
                    <v-icon>{{ olIcon }}</v-icon>
         | 
| 61 | 
            +
                  </v-btn>
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  <v-btn
         | 
| 64 | 
            +
                    small
         | 
| 65 | 
            +
                    :class="['menu-button', {'is-active': focus && editor.isActive('blockquote')}]"
         | 
| 66 | 
            +
                    @click="editor.chain().focus().toggleBlockquote().run()"
         | 
| 67 | 
            +
                  >
         | 
| 68 | 
            +
                    <v-icon>{{ commentIcon }}</v-icon>
         | 
| 69 | 
            +
                  </v-btn>
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  <v-btn
         | 
| 72 | 
            +
                    small
         | 
| 73 | 
            +
                    class="menu-button"
         | 
| 74 | 
            +
                    @click="editor.chain().focus().undo().run()"
         | 
| 75 | 
            +
                  >
         | 
| 76 | 
            +
                    <v-icon>{{ undoIcon }}</v-icon>
         | 
| 77 | 
            +
                  </v-btn>
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  <v-btn
         | 
| 80 | 
            +
                    small
         | 
| 81 | 
            +
                    class="menu-button"
         | 
| 82 | 
            +
                    @click="editor.chain().focus().redo().run()"
         | 
| 83 | 
            +
                  >
         | 
| 84 | 
            +
                    <v-icon>{{ redoIcon }}</v-icon>
         | 
| 85 | 
            +
                  </v-btn>
         | 
| 86 | 
            +
                </div>
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                <editor-content
         | 
| 89 | 
            +
                  :editor="editor"
         | 
| 90 | 
            +
                  :class="['a-rich-text-editor', {focus}]"
         | 
| 91 | 
            +
                />
         | 
| 92 | 
            +
              </div>
         | 
| 93 | 
            +
            </template>
         | 
| 94 | 
            +
             | 
| 95 | 
            +
             | 
| 96 | 
            +
            <script>
         | 
| 97 | 
            +
            import { Component, Vue, Watch } from '@a-vue'
         | 
| 98 | 
            +
            import { Editor, EditorContent } from '@tiptap/vue-2'
         | 
| 99 | 
            +
            import StarterKit from '@tiptap/starter-kit'
         | 
| 100 | 
            +
            import {
         | 
| 101 | 
            +
              mdiFormatBold,
         | 
| 102 | 
            +
              mdiFormatItalic,
         | 
| 103 | 
            +
              mdiFormatStrikethroughVariant,
         | 
| 104 | 
            +
              mdiFormatHeader1,
         | 
| 105 | 
            +
              mdiFormatHeader2,
         | 
| 106 | 
            +
              mdiFormatListBulleted,
         | 
| 107 | 
            +
              mdiFormatListNumbered,
         | 
| 108 | 
            +
              mdiFormatQuoteClose,
         | 
| 109 | 
            +
              mdiRotateLeft,
         | 
| 110 | 
            +
              mdiRotateRight
         | 
| 111 | 
            +
            } from '@mdi/js'
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            @Component({
         | 
| 114 | 
            +
              props: ['value', 'validator'],
         | 
| 115 | 
            +
              components: {
         | 
| 116 | 
            +
                EditorContent
         | 
| 117 | 
            +
              }
         | 
| 118 | 
            +
            })
         | 
| 119 | 
            +
            export default class ARichTextArea extends Vue {
         | 
| 120 | 
            +
              editor = null
         | 
| 121 | 
            +
              internalValue = null
         | 
| 122 | 
            +
              focus = false
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              boldIcon = mdiFormatBold
         | 
| 125 | 
            +
              italicIcon = mdiFormatItalic
         | 
| 126 | 
            +
              strikeIcon = mdiFormatStrikethroughVariant
         | 
| 127 | 
            +
              h1Icon = mdiFormatHeader1
         | 
| 128 | 
            +
              h2Icon = mdiFormatHeader2
         | 
| 129 | 
            +
              ulIcon = mdiFormatListBulleted
         | 
| 130 | 
            +
              olIcon = mdiFormatListNumbered
         | 
| 131 | 
            +
              commentIcon = mdiFormatQuoteClose
         | 
| 132 | 
            +
              undoIcon = mdiRotateLeft
         | 
| 133 | 
            +
              redoIcon = mdiRotateRight
         | 
| 134 | 
            +
             | 
| 135 | 
            +
              created () {
         | 
| 136 | 
            +
                this.internalValue = this.value
         | 
| 137 | 
            +
              }
         | 
| 138 | 
            +
             | 
| 139 | 
            +
              mounted () {
         | 
| 140 | 
            +
                if (this.validator) {
         | 
| 141 | 
            +
                  this.$refs.input.validate(true)
         | 
| 142 | 
            +
                }
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                this.editor = new Editor({
         | 
| 145 | 
            +
                  content: this.internalValue,
         | 
| 146 | 
            +
                  extensions: [
         | 
| 147 | 
            +
                    StarterKit
         | 
| 148 | 
            +
                  ],
         | 
| 149 | 
            +
                  onUpdate: () => {
         | 
| 150 | 
            +
                    this.$emit('input', this.editor.getHTML())
         | 
| 151 | 
            +
                  },
         | 
| 152 | 
            +
                  onFocus: ({ editor, event }) => {
         | 
| 153 | 
            +
                    this.focus = true
         | 
| 154 | 
            +
                  },
         | 
| 155 | 
            +
                  onBlur: ({ editor, event }) => {
         | 
| 156 | 
            +
                    this.focus = false
         | 
| 157 | 
            +
                  }
         | 
| 158 | 
            +
                })
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                this.editor.commands.setContent(this.internalValue, false)
         | 
| 161 | 
            +
              }
         | 
| 162 | 
            +
             | 
| 163 | 
            +
              beforeDestroy () {
         | 
| 164 | 
            +
                this.editor.destroy()
         | 
| 165 | 
            +
              }
         | 
| 166 | 
            +
             | 
| 167 | 
            +
              @Watch('value')
         | 
| 168 | 
            +
              valueChanged () {
         | 
| 169 | 
            +
                this.internalValue = this.value
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                const isSame = this.editor.getHTML() === this.internalValue
         | 
| 172 | 
            +
                if (!isSame) {
         | 
| 173 | 
            +
                  this.editor.commands.setContent(this.internalValue, false)
         | 
| 174 | 
            +
                }
         | 
| 175 | 
            +
              }
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              get validationRules () {
         | 
| 178 | 
            +
                const label = this.$attrs.label
         | 
| 179 | 
            +
                return (this.validator && this.validator.getRules(label)) || []
         | 
| 180 | 
            +
              }
         | 
| 181 | 
            +
             | 
| 182 | 
            +
              get counter () {
         | 
| 183 | 
            +
                if (!this.validator) {
         | 
| 184 | 
            +
                  return false
         | 
| 185 | 
            +
                }
         | 
| 186 | 
            +
                return this.validator.getParams().max || false
         | 
| 187 | 
            +
              }
         | 
| 188 | 
            +
            }
         | 
| 189 | 
            +
            </script>
         | 
| 190 | 
            +
             | 
| 191 | 
            +
             | 
| 192 | 
            +
            <style lang="scss" scoped>
         | 
| 193 | 
            +
            .v-input:not(.v-input--is-focused) ::v-deep .v-counter {
         | 
| 194 | 
            +
              display: none;
         | 
| 195 | 
            +
            }
         | 
| 196 | 
            +
             | 
| 197 | 
            +
            .a-rich-text-editor {
         | 
| 198 | 
            +
              ::v-deep .ProseMirror {
         | 
| 199 | 
            +
                &-focused {
         | 
| 200 | 
            +
                  outline: none;
         | 
| 201 | 
            +
                }
         | 
| 202 | 
            +
              }
         | 
| 203 | 
            +
            }
         | 
| 204 | 
            +
             | 
| 205 | 
            +
            .menu-bar {
         | 
| 206 | 
            +
              margin: -.2rem 0 .5rem -.2rem;
         | 
| 207 | 
            +
            }
         | 
| 208 | 
            +
             | 
| 209 | 
            +
            .menu-button {
         | 
| 210 | 
            +
              padding: 0 !important;
         | 
| 211 | 
            +
              width: 30px !important;
         | 
| 212 | 
            +
              height: 32px !important;
         | 
| 213 | 
            +
              min-width: unset !important;
         | 
| 214 | 
            +
              text-align: center;
         | 
| 215 | 
            +
              font-size: 1rem;
         | 
| 216 | 
            +
              background: white !important;
         | 
| 217 | 
            +
              border: none;
         | 
| 218 | 
            +
              box-shadow: none;
         | 
| 219 | 
            +
              border-radius: 0;
         | 
| 220 | 
            +
             | 
| 221 | 
            +
              ::v-deep .v-icon {
         | 
| 222 | 
            +
                font-size: 20px;
         | 
| 223 | 
            +
                width: 20px;
         | 
| 224 | 
            +
                height: 20px;
         | 
| 225 | 
            +
              }
         | 
| 226 | 
            +
             | 
| 227 | 
            +
              &.strike {
         | 
| 228 | 
            +
                ::v-deep .v-icon {
         | 
| 229 | 
            +
                  width: 15px;
         | 
| 230 | 
            +
                }
         | 
| 231 | 
            +
              }
         | 
| 232 | 
            +
             | 
| 233 | 
            +
              svg {
         | 
| 234 | 
            +
                width: unset;
         | 
| 235 | 
            +
              }
         | 
| 236 | 
            +
             | 
| 237 | 
            +
              &.is-active {
         | 
| 238 | 
            +
                background: #ECECEC !important;
         | 
| 239 | 
            +
              }
         | 
| 240 | 
            +
            }
         | 
| 241 | 
            +
             | 
| 242 | 
            +
            ::v-deep .ProseMirror {
         | 
| 243 | 
            +
              min-height: 200px;
         | 
| 244 | 
            +
             | 
| 245 | 
            +
              > :last-child {
         | 
| 246 | 
            +
                margin: 0;
         | 
| 247 | 
            +
              }
         | 
| 248 | 
            +
             | 
| 249 | 
            +
              li p {
         | 
| 250 | 
            +
                margin: 0;
         | 
| 251 | 
            +
              }
         | 
| 252 | 
            +
             | 
| 253 | 
            +
              ul {
         | 
| 254 | 
            +
                margin: 16px 0;
         | 
| 255 | 
            +
              }
         | 
| 256 | 
            +
            }
         | 
| 257 | 
            +
            </style>
         | 
| @@ -16,6 +16,7 @@ | |
| 16 16 | 
             
                  <div
         | 
| 17 17 | 
             
                    v-if="isOpen"
         | 
| 18 18 | 
             
                    class="controls"
         | 
| 19 | 
            +
                    :style="cwm_widthStyle"
         | 
| 19 20 | 
             
                  >
         | 
| 20 21 | 
             
                    <div class="background elevation-6" />
         | 
| 21 22 |  | 
| @@ -54,6 +55,7 @@ | |
| 54 55 | 
             
                    :filters.sync="filters"
         | 
| 55 56 | 
             
                    :count.sync="count"
         | 
| 56 57 | 
             
                    :isLoading.sync="isLoading"
         | 
| 58 | 
            +
                    :style="cwm_widthStyle"
         | 
| 57 59 | 
             
                  >
         | 
| 58 60 | 
             
                    <template #header>
         | 
| 59 61 | 
             
                      <div />
         | 
| @@ -62,19 +64,11 @@ | |
| 62 64 | 
             
                    </template>
         | 
| 63 65 |  | 
| 64 66 | 
             
                    <template #row="{ model }">
         | 
| 65 | 
            -
                      <v-icon
         | 
| 66 | 
            -
                        :color="model.getIcon().color"
         | 
| 67 | 
            -
                        size="1.5rem"
         | 
| 68 | 
            -
                        v-text="model.getIcon().icon"
         | 
| 69 | 
            -
                      />
         | 
| 70 | 
            -
             | 
| 71 67 | 
             
                      <slot
         | 
| 72 68 | 
             
                        name="row"
         | 
| 73 69 | 
             
                        :model="model"
         | 
| 74 70 | 
             
                        :on="{ click: selectHandler(model) }"
         | 
| 75 71 | 
             
                      />
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                      <div class="lastColumn" />
         | 
| 78 72 | 
             
                    </template>
         | 
| 79 73 |  | 
| 80 74 | 
             
                    <template #not-found="{ filters }">
         | 
| @@ -98,6 +92,7 @@ import { FilterSourceType } from '@a-vue/components/list/FilterSourceType' | |
| 98 92 | 
             
            import SearchSelectFilters from './search-select/SearchSelectFilters'
         | 
| 99 93 | 
             
            import SearchSelectList from './search-select/SearchSelectList'
         | 
| 100 94 | 
             
            import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin'
         | 
| 95 | 
            +
            import { ComponentWidthMixin } from './mixins/ComponentWidthMixin'
         | 
| 101 96 |  | 
| 102 97 | 
             
            @Component({
         | 
| 103 98 | 
             
              props: [
         | 
| @@ -115,7 +110,7 @@ import { CancelOnEscMixin } from '@a-vue/services/escape/CancelOnEscMixin' | |
| 115 110 | 
             
                SearchSelectList
         | 
| 116 111 | 
             
              }
         | 
| 117 112 | 
             
            })
         | 
| 118 | 
            -
            export default class ASearchSelect extends Mixins(UsesPositionServiceMixin, CancelOnEscMixin) {
         | 
| 113 | 
            +
            export default class ASearchSelect extends Mixins(ComponentWidthMixin, UsesPositionServiceMixin, CancelOnEscMixin) {
         | 
| 119 114 | 
             
              selectId = randomCssClass(10)
         | 
| 120 115 | 
             
              isOpen = false
         | 
| 121 116 | 
             
              items_ = []
         | 
| @@ -318,20 +313,25 @@ export default class ASearchSelect extends Mixins(UsesPositionServiceMixin, Canc | |
| 318 313 | 
             
            }
         | 
| 319 314 |  | 
| 320 315 | 
             
            .controls {
         | 
| 321 | 
            -
               | 
| 316 | 
            +
              width: 400px;
         | 
| 322 317 | 
             
              position: absolute;
         | 
| 323 318 | 
             
              z-index: 300;
         | 
| 324 319 | 
             
              display: block;
         | 
| 325 | 
            -
              padding: 0  | 
| 320 | 
            +
              padding: 0 .5rem;
         | 
| 321 | 
            +
             | 
| 322 | 
            +
              ::v-deep .a-row {
         | 
| 323 | 
            +
                overflow: unset;
         | 
| 324 | 
            +
              }
         | 
| 326 325 | 
             
            }
         | 
| 327 326 |  | 
| 328 327 | 
             
            .searchSelectList {
         | 
| 329 | 
            -
               | 
| 328 | 
            +
              width: 400px;
         | 
| 330 329 | 
             
              position: absolute;
         | 
| 331 330 | 
             
              z-index: 301;
         | 
| 332 331 |  | 
| 333 332 | 
             
              max-height: 40vh;
         | 
| 334 333 | 
             
              overflow-y: auto;
         | 
| 334 | 
            +
              overflow-x: hidden;
         | 
| 335 335 | 
             
              overscroll-behavior: contain;
         | 
| 336 336 |  | 
| 337 337 | 
             
              ::v-deep .a-table-row {
         | 
| @@ -8,6 +8,7 @@ | |
| 8 8 | 
             
                <slot
         | 
| 9 9 | 
             
                  :changed="changed"
         | 
| 10 10 | 
             
                  :valid="valid"
         | 
| 11 | 
            +
                  :model="model"
         | 
| 11 12 | 
             
                />
         | 
| 12 13 | 
             
              </v-form>
         | 
| 13 14 | 
             
            </template>
         | 
| @@ -15,7 +16,6 @@ | |
| 15 16 |  | 
| 16 17 | 
             
            <script>
         | 
| 17 18 | 
             
            import { Component, Vue, Watch } from '@a-vue'
         | 
| 18 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 19 19 |  | 
| 20 20 | 
             
            @Component({
         | 
| 21 21 | 
             
              props: ['model']
         | 
| @@ -52,9 +52,5 @@ export default class EditForm extends Vue { | |
| 52 52 | 
             
              changedChanged () {
         | 
| 53 53 | 
             
                this.$emit('update:changed', this.changed)
         | 
| 54 54 | 
             
              }
         | 
| 55 | 
            -
             | 
| 56 | 
            -
              get type () {
         | 
| 57 | 
            -
                return apiResources.getType(this.model.type)
         | 
| 58 | 
            -
              }
         | 
| 59 55 | 
             
            }
         | 
| 60 56 | 
             
            </script>
         | 
| @@ -21,7 +21,7 @@ | |
| 21 21 |  | 
| 22 22 | 
             
                  <template #default="{changed, valid}">
         | 
| 23 23 | 
             
                    <a-row
         | 
| 24 | 
            -
                      class="mt-8 mb- | 
| 24 | 
            +
                      class="mt-8 mb-1 pb-1 gap-4"
         | 
| 25 25 | 
             
                      right
         | 
| 26 26 | 
             
                    >
         | 
| 27 27 | 
             
                      <v-btn
         | 
| @@ -31,23 +31,25 @@ | |
| 31 31 | 
             
                        Schließen
         | 
| 32 32 | 
             
                      </v-btn>
         | 
| 33 33 |  | 
| 34 | 
            -
                      < | 
| 35 | 
            -
                         | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                         | 
| 41 | 
            -
             | 
| 34 | 
            +
                      <a-row gap="2">
         | 
| 35 | 
            +
                        <v-btn
         | 
| 36 | 
            +
                          small
         | 
| 37 | 
            +
                          :disabled="!changed || !valid"
         | 
| 38 | 
            +
                          color="green white--text"
         | 
| 39 | 
            +
                          @click="save"
         | 
| 40 | 
            +
                        >
         | 
| 41 | 
            +
                          Speichern
         | 
| 42 | 
            +
                        </v-btn>
         | 
| 42 43 |  | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 44 | 
            +
                        <v-icon
         | 
| 45 | 
            +
                          v-if="changed"
         | 
| 46 | 
            +
                          small
         | 
| 47 | 
            +
                          text
         | 
| 48 | 
            +
                          @click="reset"
         | 
| 49 | 
            +
                        >
         | 
| 50 | 
            +
                          {{ undoIcon }}
         | 
| 51 | 
            +
                        </v-icon>
         | 
| 52 | 
            +
                      </a-row>
         | 
| 51 53 | 
             
                    </a-row>
         | 
| 52 54 | 
             
                  </template>
         | 
| 53 55 | 
             
                </edit-form>
         | 
| @@ -57,6 +59,7 @@ | |
| 57 59 |  | 
| 58 60 | 
             
            <script>
         | 
| 59 61 | 
             
            import { Component, Vue, Watch } from '@a-vue'
         | 
| 62 | 
            +
            import { mdiRotateLeft} from '@mdi/js'
         | 
| 60 63 |  | 
| 61 64 | 
             
            @Component({
         | 
| 62 65 | 
             
              props: ['model', 'title', 'show']
         | 
| @@ -64,6 +67,8 @@ import { Component, Vue, Watch } from '@a-vue' | |
| 64 67 | 
             
            export default class EditModal extends Vue {
         | 
| 65 68 | 
             
              show_ = false
         | 
| 66 69 |  | 
| 70 | 
            +
              undoIcon = mdiRotateLeft
         | 
| 71 | 
            +
             | 
| 67 72 | 
             
              /**
         | 
| 68 73 | 
             
               * visiblility changes from outside
         | 
| 69 74 | 
             
               * this will trigger the show_ watcher,
         | 
| @@ -1,6 +1,5 @@ | |
| 1 | 
            -
            import { ListAction } from '@a-vue/api-resources/ApiActions'
         | 
| 2 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 3 1 | 
             
            import { Component, Vue } from '@a-vue'
         | 
| 2 | 
            +
            import { ListAction } from '@a-vue/api-resources/ApiActions'
         | 
| 4 3 |  | 
| 5 4 | 
             
            @Component({
         | 
| 6 5 | 
             
              props: ['name', 'label']
         | 
| @@ -17,7 +16,7 @@ export class FormFieldMixin extends Vue { | |
| 17 16 | 
             
              }
         | 
| 18 17 |  | 
| 19 18 | 
             
              get modelType () {
         | 
| 20 | 
            -
                return apiResources.getType(this.model.type)
         | 
| 19 | 
            +
                return this.$apiResources.getType(this.model.type)
         | 
| 21 20 | 
             
              }
         | 
| 22 21 |  | 
| 23 22 | 
             
              get field () {
         | 
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            <template>
         | 
| 2 | 
            +
              <a-rich-text-area
         | 
| 3 | 
            +
                v-model="model[name]"
         | 
| 4 | 
            +
              />
         | 
| 5 | 
            +
            </template>
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            <script>
         | 
| 8 | 
            +
            import { Component, Mixins } from '@a-vue'
         | 
| 9 | 
            +
            import { FormFieldMixin } from '../FormFieldMixin'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            @Component
         | 
| 12 | 
            +
            export default class FormFieldRichTextArea extends Mixins(FormFieldMixin) {
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
            </script>
         | 
    
        package/src/components/index.js
    CHANGED
    
    | @@ -5,6 +5,7 @@ import EditModal from './form/EditModal' | |
| 5 5 | 
             
            import FormFieldCheckbox from './form/fields/FormFieldCheckbox'
         | 
| 6 6 | 
             
            import FormFieldDate from './form/fields/FormFieldDate'
         | 
| 7 7 | 
             
            import FormFieldRadioGroup from './form/fields/FormFieldRadioGroup'
         | 
| 8 | 
            +
            import FormFieldRichTextArea from './form/fields/FormFieldRichTextArea'
         | 
| 8 9 | 
             
            import FormFieldSearchSelect from './form/fields/FormFieldSearchSelect'
         | 
| 9 10 | 
             
            import FormFieldSelect from './form/fields/FormFieldSelect'
         | 
| 10 11 | 
             
            import FormFieldSelect2 from './form/fields/FormFieldSelect2'
         | 
| @@ -19,6 +20,7 @@ Vue.component('EditForm', EditForm) | |
| 19 20 | 
             
            Vue.component('EditModal', EditModal)
         | 
| 20 21 | 
             
            Vue.component('FormFieldText', FormFieldText)
         | 
| 21 22 | 
             
            Vue.component('FormFieldTextArea', FormFieldTextArea)
         | 
| 23 | 
            +
            Vue.component('FormFieldRichTextArea', FormFieldRichTextArea)
         | 
| 22 24 | 
             
            Vue.component('FormFieldRadioGroup', FormFieldRadioGroup)
         | 
| 23 25 | 
             
            Vue.component('FormFieldCheckbox', FormFieldCheckbox)
         | 
| 24 26 | 
             
            Vue.component('FormFieldDate', FormFieldDate)
         | 
| @@ -17,7 +17,9 @@ import { Component, Mixins } from '@a-vue' | |
| 17 17 | 
             
            import { ListFilterMixin } from '../ListFilterMixin'
         | 
| 18 18 | 
             
            import { ListAction } from '@a-vue/api-resources/ApiActions'
         | 
| 19 19 |  | 
| 20 | 
            -
            @Component
         | 
| 20 | 
            +
            @Component({
         | 
| 21 | 
            +
              props: ['itemTitle']
         | 
| 22 | 
            +
            })
         | 
| 21 23 | 
             
            export default class ListFilterSelect extends Mixins(ListFilterMixin) {
         | 
| 22 24 | 
             
              items = null
         | 
| 23 25 |  | 
| @@ -61,7 +63,7 @@ export default class ListFilterSelect extends Mixins(ListFilterMixin) { | |
| 61 63 | 
             
                return [
         | 
| 62 64 | 
             
                  ...this.createOptions(),
         | 
| 63 65 | 
             
                  ...models.map(model => ({
         | 
| 64 | 
            -
                    itemTitle: model.name,
         | 
| 66 | 
            +
                    itemTitle: (this.itemTitle && this.itemTitle(model)) || model.name || model.title,
         | 
| 65 67 | 
             
                    itemValue: model.id
         | 
| 66 68 | 
             
                  }))
         | 
| 67 69 | 
             
                ]
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            <template>
         | 
| 2 | 
            -
              <div class="searchSelectList">
         | 
| 2 | 
            +
              <div :class="['searchSelectList', {isLoading}]">
         | 
| 3 3 | 
             
                <template v-if="models_.length">
         | 
| 4 4 | 
             
                  <a-table v-bind="$attrs">
         | 
| 5 5 | 
             
                    <a-table-header
         | 
| @@ -74,6 +74,10 @@ export default class SearchSelectList extends Mixins(ListViewMixin) { | |
| 74 74 |  | 
| 75 75 |  | 
| 76 76 | 
             
            <style scoped lang="scss">
         | 
| 77 | 
            +
            .isLoading {
         | 
| 78 | 
            +
              opacity: .6;
         | 
| 79 | 
            +
            }
         | 
| 80 | 
            +
             | 
| 77 81 | 
             
            .notFound {
         | 
| 78 82 | 
             
              padding: 0 .5rem .3rem;
         | 
| 79 83 | 
             
            }
         | 
    
        package/src/index.js
    CHANGED
    
    
| @@ -1,6 +1,18 @@ | |
| 1 1 | 
             
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 2 2 |  | 
| 3 3 | 
             
            class ApiResourcesPlugin {
         | 
| 4 | 
            +
              apiResources = apiResources
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              register (models, apis) {
         | 
| 7 | 
            +
                apiResources
         | 
| 8 | 
            +
                  .registerModels(models)
         | 
| 9 | 
            +
                  .registerApis(apis)
         | 
| 10 | 
            +
              }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              schemasLoaded () {
         | 
| 13 | 
            +
                return apiResources.schemasLoaded()
         | 
| 14 | 
            +
              }
         | 
| 15 | 
            +
             | 
| 4 16 | 
             
              install (Vue) {
         | 
| 5 17 | 
             
                Object.defineProperty(Vue.prototype, '$apiResources', {
         | 
| 6 18 | 
             
                  get () {
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            @import "forms";
         | 
    
        package/src-admin/bootstrap.js
    CHANGED
    
    | @@ -4,7 +4,6 @@ import './config/components' | |
| 4 4 | 
             
            import { apiResourcesPlugin } from '@a-vue/plugins/api-resources/ApiResourcesPlugin'
         | 
| 5 5 | 
             
            import { hasOptionsPlugin } from '@a-vue/plugins/has-options/HasOptionsPlugin'
         | 
| 6 6 | 
             
            import { timeout } from '@a-vue/utils/timeout'
         | 
| 7 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 8 7 | 
             
            import Vue from 'vue'
         | 
| 9 8 |  | 
| 10 9 | 
             
            import { appConfig } from './config/AppConfig'
         | 
| @@ -17,9 +16,7 @@ Vue.use(hasOptionsPlugin) | |
| 17 16 | 
             
            Vue.config.productionTip = false
         | 
| 18 17 |  | 
| 19 18 | 
             
            export async function bootstrap ({ apis, models, routing, authService, app, components }) {
         | 
| 20 | 
            -
               | 
| 21 | 
            -
                .registerModels(models)
         | 
| 22 | 
            -
                .registerApis(apis)
         | 
| 19 | 
            +
              apiResourcesPlugin.register(models, apis)
         | 
| 23 20 |  | 
| 24 21 | 
             
              appConfig.authService = authService
         | 
| 25 22 | 
             
              appConfig.app = app
         | 
| @@ -36,7 +33,7 @@ export async function bootstrap ({ apis, models, routing, authService, app, comp | |
| 36 33 |  | 
| 37 34 | 
             
              routing(routeConfigPlugin)
         | 
| 38 35 | 
             
              const router = await routeConfigPlugin.getRouter()
         | 
| 39 | 
            -
              await  | 
| 36 | 
            +
              await apiResourcesPlugin.schemasLoaded()
         | 
| 40 37 |  | 
| 41 38 | 
             
              if (authService) {
         | 
| 42 39 | 
             
                authService.initApp(router)
         | 
| @@ -0,0 +1,223 @@ | |
| 1 | 
            +
            <template>
         | 
| 2 | 
            +
              <div>
         | 
| 3 | 
            +
                <a-table
         | 
| 4 | 
            +
                  v-if="selectedItems.length"
         | 
| 5 | 
            +
                  :style="selectedWidthStyle"
         | 
| 6 | 
            +
                >
         | 
| 7 | 
            +
                  <a-table-header
         | 
| 8 | 
            +
                    v-if="hasHeader"
         | 
| 9 | 
            +
                    small
         | 
| 10 | 
            +
                  >
         | 
| 11 | 
            +
                    <div
         | 
| 12 | 
            +
                      v-for="(column, index) in selectedColumns"
         | 
| 13 | 
            +
                      :key="index"
         | 
| 14 | 
            +
                    >
         | 
| 15 | 
            +
                      {{ column.header }}
         | 
| 16 | 
            +
                    </div>
         | 
| 17 | 
            +
                  </a-table-header>
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  <a-table-row
         | 
| 20 | 
            +
                    v-for="(item, index) in selectedItems"
         | 
| 21 | 
            +
                    :key="item.id"
         | 
| 22 | 
            +
                    :selected="isClicked(item)"
         | 
| 23 | 
            +
                  >
         | 
| 24 | 
            +
                    <template v-for="(column, index2) in selectedColumns">
         | 
| 25 | 
            +
                      <v-icon
         | 
| 26 | 
            +
                        v-if="column.icon"
         | 
| 27 | 
            +
                        :key="'icon' + index2"
         | 
| 28 | 
            +
                        :color="column.icon.color"
         | 
| 29 | 
            +
                        class="selectedIcon"
         | 
| 30 | 
            +
                        size="1.5rem"
         | 
| 31 | 
            +
                        v-text="column.icon.icon"
         | 
| 32 | 
            +
                      />
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      <div
         | 
| 35 | 
            +
                        v-else
         | 
| 36 | 
            +
                        :key="'text' + index2"
         | 
| 37 | 
            +
                        class="selectedContent"
         | 
| 38 | 
            +
                      >
         | 
| 39 | 
            +
                        {{ column.text(item) }}
         | 
| 40 | 
            +
                      </div>
         | 
| 41 | 
            +
                    </template>
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    <div>
         | 
| 44 | 
            +
                      <a-context-menu
         | 
| 45 | 
            +
                        :ref="'editItem' + index"
         | 
| 46 | 
            +
                        @open="clickedItem = item"
         | 
| 47 | 
            +
                        @close="clickedItem = null"
         | 
| 48 | 
            +
                      >
         | 
| 49 | 
            +
                        <a-context-menu-item @click="removeItem(item, index)">
         | 
| 50 | 
            +
                          <v-icon>$trashCanIcon</v-icon>
         | 
| 51 | 
            +
                          Löschen
         | 
| 52 | 
            +
                        </a-context-menu-item>
         | 
| 53 | 
            +
                      </a-context-menu>
         | 
| 54 | 
            +
                    </div>
         | 
| 55 | 
            +
                  </a-table-row>
         | 
| 56 | 
            +
                </a-table>
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                <div v-else>
         | 
| 59 | 
            +
                  {{ isEmptyText }}
         | 
| 60 | 
            +
                </div>
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                <a-search-select
         | 
| 63 | 
            +
                  :listViewConfig="selectableConfig.listViewConfig"
         | 
| 64 | 
            +
                  :loadOnlyIfKeyword="false"
         | 
| 65 | 
            +
                  :selectedItems="selectableSelectedItems"
         | 
| 66 | 
            +
                  :width="selectableWidth"
         | 
| 67 | 
            +
                  @select="addItem"
         | 
| 68 | 
            +
                >
         | 
| 69 | 
            +
                  <template #activator>
         | 
| 70 | 
            +
                    <a-icon-button
         | 
| 71 | 
            +
                      icon="$plusIcon"
         | 
| 72 | 
            +
                      :text="selectableConfig.addButtonTitle || 'Hinzufügen'"
         | 
| 73 | 
            +
                      class="mt-4"
         | 
| 74 | 
            +
                    />
         | 
| 75 | 
            +
                  </template>
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  <template #filters>
         | 
| 78 | 
            +
                    <list-filter-row>
         | 
| 79 | 
            +
                      <list-filter-search
         | 
| 80 | 
            +
                        :focus="true"
         | 
| 81 | 
            +
                        maxWidth="100%"
         | 
| 82 | 
            +
                        label="Suche Berater:in"
         | 
| 83 | 
            +
                      />
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      <list-filter-page
         | 
| 86 | 
            +
                        :has="{page_size: false, page_number: true}"
         | 
| 87 | 
            +
                        :totalVisible="0"
         | 
| 88 | 
            +
                      />
         | 
| 89 | 
            +
                    </list-filter-row>
         | 
| 90 | 
            +
                  </template>
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  <template #row="{ model, on }">
         | 
| 93 | 
            +
                    <template
         | 
| 94 | 
            +
                      v-for="(column, index) in selectableColumns"
         | 
| 95 | 
            +
                      v-on="on"
         | 
| 96 | 
            +
                    >
         | 
| 97 | 
            +
                      <v-icon
         | 
| 98 | 
            +
                        v-if="column.icon"
         | 
| 99 | 
            +
                        :key="'icon' + index"
         | 
| 100 | 
            +
                        :color="column.icon.color"
         | 
| 101 | 
            +
                        class="selectableIcon"
         | 
| 102 | 
            +
                        size="1.5rem"
         | 
| 103 | 
            +
                        v-on="on"
         | 
| 104 | 
            +
                        v-text="column.icon.icon"
         | 
| 105 | 
            +
                      />
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                      <div
         | 
| 108 | 
            +
                        v-else
         | 
| 109 | 
            +
                        :key="'text' + index"
         | 
| 110 | 
            +
                        class="selectableContent"
         | 
| 111 | 
            +
                        v-on="on"
         | 
| 112 | 
            +
                      >
         | 
| 113 | 
            +
                        {{ column.text(model) }}
         | 
| 114 | 
            +
                      </div>
         | 
| 115 | 
            +
                    </template>
         | 
| 116 | 
            +
                  </template>
         | 
| 117 | 
            +
                </a-search-select>
         | 
| 118 | 
            +
              </div>
         | 
| 119 | 
            +
            </template>
         | 
| 120 | 
            +
             | 
| 121 | 
            +
             | 
| 122 | 
            +
            <script>
         | 
| 123 | 
            +
            import { Component, Vue } from '@a-vue'
         | 
| 124 | 
            +
             | 
| 125 | 
            +
            @Component({
         | 
| 126 | 
            +
              props: [
         | 
| 127 | 
            +
                'value',
         | 
| 128 | 
            +
                'selectedConfig',
         | 
| 129 | 
            +
                'selectedWidth',
         | 
| 130 | 
            +
                'selectableConfig',
         | 
| 131 | 
            +
                'selectableWidth'
         | 
| 132 | 
            +
              ]
         | 
| 133 | 
            +
            })
         | 
| 134 | 
            +
            export default class SearchSelectFormField extends Vue {
         | 
| 135 | 
            +
              clickedItem = null
         | 
| 136 | 
            +
             | 
| 137 | 
            +
              get hasHeader () {
         | 
| 138 | 
            +
                return this.selectedColumns.some(c => !!c.header)
         | 
| 139 | 
            +
              }
         | 
| 140 | 
            +
             | 
| 141 | 
            +
              get selectedItems () {
         | 
| 142 | 
            +
                return this.value
         | 
| 143 | 
            +
              }
         | 
| 144 | 
            +
             | 
| 145 | 
            +
              get selectableSelectedItems () {
         | 
| 146 | 
            +
                return this.value.map(this.selectedConfig.selectedToSelectableItem)
         | 
| 147 | 
            +
              }
         | 
| 148 | 
            +
             | 
| 149 | 
            +
              get isEmptyText () {
         | 
| 150 | 
            +
                return this.selectedConfig.isEmptyText
         | 
| 151 | 
            +
              }
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              isClicked (selectedItem) {
         | 
| 154 | 
            +
                return selectedItem === this.clickedItem
         | 
| 155 | 
            +
              }
         | 
| 156 | 
            +
             | 
| 157 | 
            +
              get selectedColumns () {
         | 
| 158 | 
            +
                return this.selectedConfig.columns
         | 
| 159 | 
            +
              }
         | 
| 160 | 
            +
             | 
| 161 | 
            +
              get selectableColumns () {
         | 
| 162 | 
            +
                return this.selectableConfig.columns
         | 
| 163 | 
            +
              }
         | 
| 164 | 
            +
             | 
| 165 | 
            +
              addItem (selectableItem) {
         | 
| 166 | 
            +
                const selectedItem = this.selectedConfig.newSelectedItem(selectableItem)
         | 
| 167 | 
            +
                const selectedItems = [...this.value, selectedItem]
         | 
| 168 | 
            +
                if (this.$listeners.add) {
         | 
| 169 | 
            +
                  this.$emit('add', selectedItems)
         | 
| 170 | 
            +
                } else {
         | 
| 171 | 
            +
                  this.$emit('input', selectedItems)
         | 
| 172 | 
            +
                }
         | 
| 173 | 
            +
              }
         | 
| 174 | 
            +
             | 
| 175 | 
            +
              async removeItem (selectedItem, rowIndex) {
         | 
| 176 | 
            +
                const removeDialog = this.selectedConfig.removeDialog(selectedItem)
         | 
| 177 | 
            +
                const dialog = {
         | 
| 178 | 
            +
                  anchor: this.$refs['editItem' + rowIndex], // is array [component]
         | 
| 179 | 
            +
                  ...removeDialog,
         | 
| 180 | 
            +
                  yesButton: 'Löschen',
         | 
| 181 | 
            +
                  yesColor: 'red white--text'
         | 
| 182 | 
            +
                }
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                const selectedItems = this.value.filter(i => i !== selectedItem)
         | 
| 185 | 
            +
                if (this.$listeners.remove) {
         | 
| 186 | 
            +
                  this.$emit('remove', selectedItems, dialog)
         | 
| 187 | 
            +
                } else {
         | 
| 188 | 
            +
                  this.$emit('input', selectedItems)
         | 
| 189 | 
            +
                }
         | 
| 190 | 
            +
              }
         | 
| 191 | 
            +
             | 
| 192 | 
            +
              get selectedWidthStyle () {
         | 
| 193 | 
            +
                if (!this.selectedWidth) {
         | 
| 194 | 
            +
                  return ''
         | 
| 195 | 
            +
                }
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                let width = this.selectedWidth
         | 
| 198 | 
            +
                if (!isNaN(width)) {
         | 
| 199 | 
            +
                  width = width + 'px'
         | 
| 200 | 
            +
                }
         | 
| 201 | 
            +
                return `width: ${width};`
         | 
| 202 | 
            +
              }
         | 
| 203 | 
            +
            }
         | 
| 204 | 
            +
            </script>
         | 
| 205 | 
            +
             | 
| 206 | 
            +
             | 
| 207 | 
            +
            <style scoped lang="scss">
         | 
| 208 | 
            +
            .selectedIcon::v-deep {
         | 
| 209 | 
            +
              padding-right: .3rem !important;
         | 
| 210 | 
            +
            }
         | 
| 211 | 
            +
             | 
| 212 | 
            +
            .selectableIcon::v-deep {
         | 
| 213 | 
            +
              padding-right: 2rem !important;
         | 
| 214 | 
            +
            }
         | 
| 215 | 
            +
             | 
| 216 | 
            +
            .selectedContent::v-deep {
         | 
| 217 | 
            +
              width: 100%;
         | 
| 218 | 
            +
            }
         | 
| 219 | 
            +
             | 
| 220 | 
            +
            .selectableContent::v-deep {
         | 
| 221 | 
            +
              width: 100%;
         | 
| 222 | 
            +
            }
         | 
| 223 | 
            +
            </style>
         | 
| @@ -25,7 +25,6 @@ | |
| 25 25 |  | 
| 26 26 | 
             
            <script>
         | 
| 27 27 | 
             
            import { Component, Vue } from '@a-vue'
         | 
| 28 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 29 28 |  | 
| 30 29 | 
             
            @Component({
         | 
| 31 30 | 
             
              props: ['icon', 'iconModelType', 'label']
         | 
| @@ -37,7 +36,7 @@ export default class DetailProperty extends Vue { | |
| 37 36 | 
             
                }
         | 
| 38 37 |  | 
| 39 38 | 
             
                if (this.iconModelType) {
         | 
| 40 | 
            -
                  const ModelClass = apiResources.getModelClass(this.iconModelType)
         | 
| 39 | 
            +
                  const ModelClass = this.$apiResources.getModelClass(this.iconModelType)
         | 
| 41 40 | 
             
                  return ModelClass.icon
         | 
| 42 41 | 
             
                }
         | 
| 43 42 | 
             
              }
         | 
| @@ -77,6 +76,5 @@ export default class DetailProperty extends Vue { | |
| 77 76 | 
             
                  padding-left: 55px;
         | 
| 78 77 | 
             
                }
         | 
| 79 78 | 
             
              }
         | 
| 80 | 
            -
             | 
| 81 79 | 
             
            }
         | 
| 82 80 | 
             
            </style>
         | 
| @@ -9,7 +9,15 @@ | |
| 9 9 | 
             
                </div>
         | 
| 10 10 |  | 
| 11 11 | 
             
                <template v-if="models_.length">
         | 
| 12 | 
            -
                  <template v-if=" | 
| 12 | 
            +
                  <template v-if="$scopedSlots.models">
         | 
| 13 | 
            +
                    <slot
         | 
| 14 | 
            +
                      name="models"
         | 
| 15 | 
            +
                      :models="models_"
         | 
| 16 | 
            +
                      :setFilter="setFilter"
         | 
| 17 | 
            +
                    />
         | 
| 18 | 
            +
                  </template>
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  <template v-else-if="$scopedSlots['model-table']">
         | 
| 13 21 | 
             
                    <a-table>
         | 
| 14 22 | 
             
                      <a-table-header>
         | 
| 15 23 | 
             
                        <div v-if="$has.icon" />
         | 
| @@ -20,6 +28,7 @@ | |
| 20 28 | 
             
                      <a-table-row
         | 
| 21 29 | 
             
                        v-for="model in models_"
         | 
| 22 30 | 
             
                        :key="model.id"
         | 
| 31 | 
            +
                        :class="getModelClass(model)"
         | 
| 23 32 | 
             
                      >
         | 
| 24 33 | 
             
                        <v-icon
         | 
| 25 34 | 
             
                          v-if="$has.icon"
         | 
| @@ -37,10 +46,11 @@ | |
| 37 46 | 
             
                    </a-table>
         | 
| 38 47 | 
             
                  </template>
         | 
| 39 48 |  | 
| 40 | 
            -
                  <template v-else>
         | 
| 49 | 
            +
                  <template v-else-if="$scopedSlots.model">
         | 
| 41 50 | 
             
                    <div
         | 
| 42 51 | 
             
                      v-for="model in models_"
         | 
| 43 52 | 
             
                      :key="model.id"
         | 
| 53 | 
            +
                      :class="getModelClass(model)"
         | 
| 44 54 | 
             
                    >
         | 
| 45 55 | 
             
                      <slot
         | 
| 46 56 | 
             
                        name="model"
         | 
| @@ -66,7 +76,7 @@ import { ListViewMixin } from '@a-vue/components/list/ListViewMixin' | |
| 66 76 | 
             
            import { LoadingEvent } from '@a-vue/events'
         | 
| 67 77 |  | 
| 68 78 | 
             
            @Component({
         | 
| 69 | 
            -
              props: [' | 
| 79 | 
            +
              props: ['modelClass']
         | 
| 70 80 | 
             
            })
         | 
| 71 81 | 
             
            export default class ListView extends Mixins(ListViewMixin) {
         | 
| 72 82 | 
             
              $hasOptions = ['icon']
         | 
| @@ -83,8 +93,8 @@ export default class ListView extends Mixins(ListViewMixin) { | |
| 83 93 | 
             
                this.$emit('update:isLoading', this.isLoading)
         | 
| 84 94 | 
             
              }
         | 
| 85 95 |  | 
| 86 | 
            -
               | 
| 87 | 
            -
                return this. | 
| 96 | 
            +
              getModelClass (model) {
         | 
| 97 | 
            +
                return this.modelClass && this.modelClass(model)
         | 
| 88 98 | 
             
              }
         | 
| 89 99 |  | 
| 90 100 | 
             
              setFilter (name, value) {
         | 
| @@ -49,7 +49,6 @@ | |
| 49 49 | 
             
            <script>
         | 
| 50 50 | 
             
            import { Component, Mixins } from '@a-vue'
         | 
| 51 51 | 
             
            import { EditPageMixin } from './EditPageMixin'
         | 
| 52 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 53 52 |  | 
| 54 53 | 
             
            @Component({
         | 
| 55 54 | 
             
              props: ['icon', 'title', 'createModel', 'listLink']
         | 
| @@ -86,7 +85,7 @@ export default class CreatePage extends Mixins(EditPageMixin) { | |
| 86 85 | 
             
                  return this.title
         | 
| 87 86 | 
             
                }
         | 
| 88 87 |  | 
| 89 | 
            -
                const type = apiResources.getType(this.ModelClass.type)
         | 
| 88 | 
            +
                const type = this.$apiResources.getType(this.ModelClass.type)
         | 
| 90 89 | 
             
                return type.t('TITLE_NEW')
         | 
| 91 90 | 
             
              }
         | 
| 92 91 |  | 
| @@ -64,8 +64,8 @@ export default class DetailPage extends Vue { | |
| 64 64 | 
             
              removeConfirmed = null
         | 
| 65 65 |  | 
| 66 66 | 
             
              created () {
         | 
| 67 | 
            -
                if (!this.$parent.constructor. | 
| 68 | 
            -
                  console.warn('<detail-page> owner must provide a static  | 
| 67 | 
            +
                if (!this.$parent.constructor.getDetailRouteConfig) {
         | 
| 68 | 
            +
                  console.warn('<detail-page> owner must provide a static getDetailRouteConfig method.')
         | 
| 69 69 | 
             
                }
         | 
| 70 70 | 
             
                this.$emit('model', this.model)
         | 
| 71 71 | 
             
              }
         | 
| @@ -80,7 +80,7 @@ export default class DetailPage extends Vue { | |
| 80 80 | 
             
              }
         | 
| 81 81 |  | 
| 82 82 | 
             
              get detailConfig () {
         | 
| 83 | 
            -
                return this.$parent.constructor. | 
| 83 | 
            +
                return this.$parent.constructor.getDetailRouteConfig(this.$route)
         | 
| 84 84 | 
             
              }
         | 
| 85 85 |  | 
| 86 86 | 
             
              get document () {
         | 
| @@ -66,7 +66,6 @@ | |
| 66 66 |  | 
| 67 67 | 
             
            <script>
         | 
| 68 68 | 
             
            import { Component, Mixins, Watch } from '@a-vue'
         | 
| 69 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 70 69 | 
             
            import { EditPageMixin } from './EditPageMixin'
         | 
| 71 70 |  | 
| 72 71 | 
             
            @Component({
         | 
| @@ -78,8 +77,8 @@ export default class EditPage extends Mixins(EditPageMixin) { | |
| 78 77 | 
             
              model_ = null
         | 
| 79 78 |  | 
| 80 79 | 
             
              created () {
         | 
| 81 | 
            -
                if (!this.$parent.constructor. | 
| 82 | 
            -
                  console.warn('<edit-page> owner must provide a static  | 
| 80 | 
            +
                if (!this.$parent.constructor.getEditRouteConfig) {
         | 
| 81 | 
            +
                  console.warn('<edit-page> owner must provide a static getEditRouteConfig method.')
         | 
| 83 82 | 
             
                }
         | 
| 84 83 |  | 
| 85 84 | 
             
                this.model_ = this.model
         | 
| @@ -95,7 +94,7 @@ export default class EditPage extends Mixins(EditPageMixin) { | |
| 95 94 | 
             
              }
         | 
| 96 95 |  | 
| 97 96 | 
             
              get editConfig () {
         | 
| 98 | 
            -
                return this.$parent.constructor. | 
| 97 | 
            +
                return this.$parent.constructor.getEditRouteConfig(this.$route)
         | 
| 99 98 | 
             
              }
         | 
| 100 99 |  | 
| 101 100 | 
             
              get modelUpateAction () {
         | 
| @@ -123,7 +122,7 @@ export default class EditPage extends Mixins(EditPageMixin) { | |
| 123 122 | 
             
                  return this.title
         | 
| 124 123 | 
             
                }
         | 
| 125 124 |  | 
| 126 | 
            -
                const type = apiResources.getType(this.ModelClass.type)
         | 
| 125 | 
            +
                const type = this.$apiResources.getType(this.ModelClass.type)
         | 
| 127 126 | 
             
                return type.t('TITLE_EMPTY')
         | 
| 128 127 | 
             
              }
         | 
| 129 128 |  | 
| @@ -21,7 +21,6 @@ | |
| 21 21 |  | 
| 22 22 | 
             
            <script>
         | 
| 23 23 | 
             
            import { Component, Vue } from '@a-vue'
         | 
| 24 | 
            -
            import { apiResources } from '@afeefa/api-resources-client'
         | 
| 25 24 |  | 
| 26 25 | 
             
            @Component({
         | 
| 27 26 | 
             
              props: ['icon', 'title', 'newTitle', 'newLink', 'Model']
         | 
| @@ -38,7 +37,7 @@ export default class ListPage extends Vue { | |
| 38 37 | 
             
                  return this.title
         | 
| 39 38 | 
             
                }
         | 
| 40 39 |  | 
| 41 | 
            -
                const type = apiResources.getType(this.Model.type)
         | 
| 40 | 
            +
                const type = this.$apiResources.getType(this.Model.type)
         | 
| 42 41 | 
             
                return type.t('TITLE_PLURAL')
         | 
| 43 42 | 
             
              }
         | 
| 44 43 |  | 
| @@ -47,7 +46,7 @@ export default class ListPage extends Vue { | |
| 47 46 | 
             
                  return this.newTitle
         | 
| 48 47 | 
             
                }
         | 
| 49 48 |  | 
| 50 | 
            -
                const type = apiResources.getType(this.Model.type)
         | 
| 49 | 
            +
                const type = this.$apiResources.getType(this.Model.type)
         | 
| 51 50 | 
             
                return type.t('TITLE_SINGULAR')
         | 
| 52 51 | 
             
              }
         | 
| 53 52 |  | 
| @@ -19,11 +19,11 @@ function load (route) { | |
| 19 19 | 
             
              const routeDefinition = route.meta.routeDefinition
         | 
| 20 20 | 
             
              const Component = routeDefinition.config.detail
         | 
| 21 21 |  | 
| 22 | 
            -
              if (!Component. | 
| 23 | 
            -
                console.warn('A detail route component must implement a static  | 
| 22 | 
            +
              if (!Component.getDetailRouteConfig) {
         | 
| 23 | 
            +
                console.warn('A detail route component must implement a static getDetailRouteConfig property.')
         | 
| 24 24 | 
             
              }
         | 
| 25 25 |  | 
| 26 | 
            -
              const detailConfig = Component. | 
| 26 | 
            +
              const detailConfig = Component.getDetailRouteConfig(route)
         | 
| 27 27 | 
             
              const action = detailConfig.action || detailConfig.ModelClass.getAction('get')
         | 
| 28 28 |  | 
| 29 29 | 
             
              return new GetAction()
         | 
| @@ -20,11 +20,11 @@ function load (route) { | |
| 20 20 | 
             
              const routeDefinition = route.meta.routeDefinition
         | 
| 21 21 | 
             
              const Component = routeDefinition.config.edit
         | 
| 22 22 |  | 
| 23 | 
            -
              if (!Component. | 
| 24 | 
            -
                console.warn('An edit route component must implement a static  | 
| 23 | 
            +
              if (!Component.getEditRouteConfig) {
         | 
| 24 | 
            +
                console.warn('An edit route component must implement a static getEditRouteConfig property.')
         | 
| 25 25 | 
             
              }
         | 
| 26 26 |  | 
| 27 | 
            -
              const editConfig = Component. | 
| 27 | 
            +
              const editConfig = Component.getEditRouteConfig(route)
         | 
| 28 28 | 
             
              const action = editConfig.getAction || editConfig.ModelClass.getAction('get')
         | 
| 29 29 |  | 
| 30 30 | 
             
              return new GetAction()
         | 
| @@ -31,11 +31,11 @@ function load (route) { | |
| 31 31 | 
             
              const routeDefinition = route.meta.routeDefinition
         | 
| 32 32 | 
             
              const Component = routeDefinition.config.list
         | 
| 33 33 |  | 
| 34 | 
            -
              if (!Component. | 
| 35 | 
            -
                console.warn('A list route component must implement a static  | 
| 34 | 
            +
              if (!Component.getListRouteConfig) {
         | 
| 35 | 
            +
                console.warn('A list route component must implement a static getListRouteConfig property.')
         | 
| 36 36 | 
             
              }
         | 
| 37 37 |  | 
| 38 | 
            -
              const request = new ListViewModel(Component. | 
| 38 | 
            +
              const request = new ListViewModel(Component.getListRouteConfig(route))
         | 
| 39 39 | 
             
                // read from next route query string, but do not push
         | 
| 40 40 | 
             
                // list component will be init with used_filters
         | 
| 41 41 | 
             
                .filterSource(new NextRouteFilterSource(route), false)
         | 
| @@ -6,6 +6,7 @@ import { | |
| 6 6 | 
             
              mdiDelete,
         | 
| 7 7 | 
             
              mdiDotsHorizontal,
         | 
| 8 8 | 
             
              mdiDotsVertical,
         | 
| 9 | 
            +
              mdiLock,
         | 
| 9 10 | 
             
              mdiLogoutVariant,
         | 
| 10 11 | 
             
              mdiMagnify,
         | 
| 11 12 | 
             
              mdiPencil,
         | 
| @@ -32,7 +33,8 @@ export default new Vuetify({ | |
| 32 33 | 
             
                  pencilIcon: mdiPencil,
         | 
| 33 34 | 
             
                  trashCanIcon: mdiDelete,
         | 
| 34 35 | 
             
                  calendarIcon: mdiCalendar,
         | 
| 35 | 
            -
                  searchIcon: mdiMagnify
         | 
| 36 | 
            +
                  searchIcon: mdiMagnify,
         | 
| 37 | 
            +
                  lockIcon: mdiLock
         | 
| 36 38 | 
             
                }
         | 
| 37 39 | 
             
              },
         | 
| 38 40 | 
             
              breakpoint: {
         | 
| @@ -1,11 +1,17 @@ | |
| 1 1 | 
             
            import { Model as ApiResourcesModel, apiResources } from '@afeefa/api-resources-client'
         | 
| 2 2 | 
             
            import { mdiAlphaMCircle } from '@mdi/js'
         | 
| 3 3 |  | 
| 4 | 
            +
            import { ModelAdminConfig } from './ModelAdminConfig'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            export { ModelAdminConfig }
         | 
| 7 | 
            +
             | 
| 4 8 | 
             
            export class Model extends ApiResourcesModel {
         | 
| 5 9 | 
             
              static resourceType = null
         | 
| 6 10 | 
             
              static routeName = null
         | 
| 7 11 | 
             
              static routeIdKey = 'id'
         | 
| 8 12 |  | 
| 13 | 
            +
              static config = null
         | 
| 14 | 
            +
             | 
| 9 15 | 
             
              static getLink (action) {
         | 
| 10 16 | 
             
                return (new this()).getLink(action)
         | 
| 11 17 | 
             
              }
         | 
| @@ -21,6 +27,13 @@ export class Model extends ApiResourcesModel { | |
| 21 27 | 
             
                return null
         | 
| 22 28 | 
             
              }
         | 
| 23 29 |  | 
| 30 | 
            +
              static get adminConfig () {
         | 
| 31 | 
            +
                return new ModelAdminConfig()
         | 
| 32 | 
            +
                  .setIcon({
         | 
| 33 | 
            +
                    icon: mdiAlphaMCircle
         | 
| 34 | 
            +
                  })
         | 
| 35 | 
            +
              }
         | 
| 36 | 
            +
             | 
| 24 37 | 
             
              static icon = {
         | 
| 25 38 | 
             
                icon: mdiAlphaMCircle,
         | 
| 26 39 | 
             
                color: 'blue lighten-2'
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            export class ModelAdminConfig {
         | 
| 2 | 
            +
              icon = null
         | 
| 3 | 
            +
              selectedListConfig = null
         | 
| 4 | 
            +
              selectableListConfig = null
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              setIcon (icon) {
         | 
| 7 | 
            +
                this.icon = icon
         | 
| 8 | 
            +
                return this
         | 
| 9 | 
            +
              }
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              setSelectedListConfig (selectedListConfig) {
         | 
| 12 | 
            +
                this.selectedListConfig = selectedListConfig
         | 
| 13 | 
            +
                return this
         | 
| 14 | 
            +
              }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              setSelectableListConfig (selectableListConfig) {
         | 
| 17 | 
            +
                this.selectableListConfig = selectableListConfig
         | 
| 18 | 
            +
                return this
         | 
| 19 | 
            +
              }
         | 
| 20 | 
            +
            }
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            <template>
         | 
| 2 | 
            +
              <div>TEST</div>
         | 
| 3 | 
            +
            </template>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
            <script>
         | 
| 7 | 
            +
            import { Component, Vue } from '@a-vue'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            @Component({
         | 
| 10 | 
            +
              props: ['name']
         | 
| 11 | 
            +
            })
         | 
| 12 | 
            +
            export default class Splash extends Vue {
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
            </script>
         | 
| 15 | 
            +
             | 
| 16 | 
            +
             | 
| 17 | 
            +
            <style lang="scss" scoped>
         | 
| 18 | 
            +
            </style>
         |