Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wiki-js
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
wiki-js
Commits
7b08c8bb
You need to sign in or sign up before continuing.
Commit
7b08c8bb
authored
May 19, 2019
by
Nick
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: lists UX + assets create folder UI (wip)
parent
965f0ad2
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
192 additions
and
18 deletions
+192
-18
editor-markdown.vue
client/components/editor/editor-markdown.vue
+26
-0
editor-modal-blocks.vue
client/components/editor/editor-modal-blocks.vue
+6
-1
editor-modal-media.vue
client/components/editor/editor-modal-media.vue
+62
-17
v-dialog.scss
client/scss/components/v-dialog.scss
+6
-0
app.scss
client/themes/default/scss/app.scss
+91
-0
assets.js
server/models/assets.js
+1
-0
No files found.
client/components/editor/editor-markdown.vue
View file @
7b08c8bb
...
@@ -397,6 +397,13 @@ export default {
...
@@ -397,6 +397,13 @@ export default {
return
lvl
return
lvl
},
},
/**
/**
* Insert content at cursor
*/
insertAtCursor
({
content
})
{
const
cursor
=
this
.
cm
.
doc
.
getCursor
(
'head'
)
this
.
cm
.
doc
.
replaceRange
(
content
,
cursor
)
},
/**
* Insert content after current line
* Insert content after current line
*/
*/
insertAfter
({
content
,
newLine
})
{
insertAfter
({
content
,
newLine
})
{
...
@@ -457,6 +464,25 @@ export default {
...
@@ -457,6 +464,25 @@ export default {
toggleFullscreen
()
{
toggleFullscreen
()
{
this
.
cm
.
setOption
(
'fullScreen'
,
true
)
this
.
cm
.
setOption
(
'fullScreen'
,
true
)
}
}
},
mounted
()
{
this
.
$root
.
$on
(
'editorInsert'
,
opts
=>
{
switch
(
opts
.
kind
)
{
case
'IMAGE'
:
this
.
insertAtCursor
({
content
:
``
})
break
case
'BINARY'
:
this
.
insertAtCursor
({
content
:
`[
${
opts
.
text
}
](
${
opts
.
path
}
)`
})
break
}
})
},
beforeDestroy
()
{
this
.
$root
.
$off
(
'editorInsert'
)
}
}
}
}
</
script
>
</
script
>
...
...
client/components/editor/editor-modal-blocks.vue
View file @
7b08c8bb
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
v-card.editor-modal-blocks.animated.fadeInLeft(flat, tile)
v-card.editor-modal-blocks.animated.fadeInLeft(flat, tile)
v-container.pa-3(grid-list-lg, fluid)
v-container.pa-3(grid-list-lg, fluid)
v-layout(row, wrap)
v-layout(row, wrap)
v-flex(xs3)
v-flex(xs
12, lg4, xl
3)
v-card.radius-7(light)
v-card.radius-7(light)
v-card-text
v-card-text
.d-flex
.d-flex
...
@@ -82,5 +82,10 @@ export default {
...
@@ -82,5 +82,10 @@ export default {
width
:
calc
(
100vw
-
64px
-
17px
);
width
:
calc
(
100vw
-
64px
-
17px
);
height
:
calc
(
100vh
-
112px
-
24px
);
height
:
calc
(
100vh
-
112px
-
24px
);
background-color
:
rgba
(
darken
(
mc
(
'grey'
,
'900'
)
,
3%
)
,
.9
)
!
important
;
background-color
:
rgba
(
darken
(
mc
(
'grey'
,
'900'
)
,
3%
)
,
.9
)
!
important
;
@include
until
(
$tablet
)
{
left
:
40px
;
width
:
calc
(
100vw
-
40px
);
}
}
}
</
style
>
</
style
>
client/components/editor/editor-modal-media.vue
View file @
7b08c8bb
...
@@ -11,9 +11,30 @@
...
@@ -11,9 +11,30 @@
v-btn.ml-3.my-0.radius-7(outline, large, color='teal', disabled, :icon='$vuetify.breakpoint.xsOnly')
v-btn.ml-3.my-0.radius-7(outline, large, color='teal', disabled, :icon='$vuetify.breakpoint.xsOnly')
v-icon(:left='$vuetify.breakpoint.mdAndUp') keyboard_arrow_up
v-icon(:left='$vuetify.breakpoint.mdAndUp') keyboard_arrow_up
span.hidden-sm-and-down Parent Folder
span.hidden-sm-and-down Parent Folder
v-btn.my-0.mr-0.radius-7(outline, large, color='teal', :icon='$vuetify.breakpoint.xsOnly')
v-dialog(v-model='newFolderDialog', max-width='550')
v-btn.my-0.mr-0.radius-7(outline, large, color='teal', :icon='$vuetify.breakpoint.xsOnly', slot='activator')
v-icon(:left='$vuetify.breakpoint.mdAndUp') add
v-icon(:left='$vuetify.breakpoint.mdAndUp') add
span.hidden-sm-and-down New Folder
span.hidden-sm-and-down New Folder
v-card.wiki-form
.dialog-header.is-short New Folder
v-card-text
v-text-field.md2(
outline
background-color='grey lighten-3'
prepend-icon='folder'
v-model='newFolderName'
label='Folder Name'
counter='255'
@keyup.enter='createFolder'
@keyup.esc='newFolderDialog = false'
ref='folderNameIpt'
hint='Lowercase. No spaces allowed.'
persistent-hint
)
v-card-chin
v-spacer
v-btn(flat, @click='newFolderDialog = false') Cancel
v-btn(color='primary', @click='createFolder', :disabled='!isFolderNameValid') Create
v-data-table(
v-data-table(
:items='assets'
:items='assets'
:headers='headers'
:headers='headers'
...
@@ -26,40 +47,40 @@
...
@@ -26,40 +47,40 @@
template(slot='items', slot-scope='props')
template(slot='items', slot-scope='props')
tr.is-clickable(
tr.is-clickable(
@click.left='currentFileId = props.item.id'
@click.left='currentFileId = props.item.id'
@click.right=''
@click.right
.prevent
=''
:class='currentFileId === props.item.id ? `teal lighten-5` : ``'
:class='currentFileId === props.item.id ? `teal lighten-5` : ``'
)
)
td.text-xs-right(v-if='$vuetify.breakpoint.smAndUp')
{{
props
.
item
.
id
}}
td.text-xs-right(v-if='$vuetify.breakpoint.smAndUp')
{{
props
.
item
.
id
}}
td
td
.body-2(:class='currentFileId === props.item.id ? `teal--text` : ``')
{{
props
.
item
.
filename
}}
.body-2(:class='currentFileId === props.item.id ? `teal--text` : ``')
{{
props
.
item
.
filename
}}
.caption
{{
props
.
item
.
description
}}
.caption
.grey--text
{{
props
.
item
.
description
}}
td.text-xs-center(v-if='$vuetify.breakpoint.lgAndUp')
td.text-xs-center(v-if='$vuetify.breakpoint.lgAndUp')
v-chip(small, :color='$vuetify.dark ? `grey darken-4` : `grey lighten-4`')
v-chip
.ma-0
(small, :color='$vuetify.dark ? `grey darken-4` : `grey lighten-4`')
.caption
{{
props
.
item
.
ext
.
toUpperCase
().
substring
(
1
)
}}
.caption
{{
props
.
item
.
ext
.
toUpperCase
().
substring
(
1
)
}}
td(v-if='$vuetify.breakpoint.mdAndUp')
{{
props
.
item
.
fileSize
|
prettyBytes
}}
td(v-if='$vuetify.breakpoint.mdAndUp')
{{
props
.
item
.
fileSize
|
prettyBytes
}}
td(v-if='$vuetify.breakpoint.mdAndUp')
{{
props
.
item
.
upd
atedAt
|
moment
(
'from'
)
}}
td(v-if='$vuetify.breakpoint.mdAndUp')
{{
props
.
item
.
cre
atedAt
|
moment
(
'from'
)
}}
td(v-if='$vuetify.breakpoint.smAndUp')
td(v-if='$vuetify.breakpoint.smAndUp')
v-menu(offset-x)
v-menu(offset-x)
v-btn(icon, slot='activator')
v-btn
.ma-0
(icon, slot='activator')
v-icon(color='grey darken-2') more_horiz
v-icon(color='grey darken-2') more_horiz
v-list.py-0
v-list.py-0
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-list-tile-avatar
v-icon(color='teal') short_text
v-icon(color='teal') short_text
v-list-tile-content Properties
v-list-tile-content Properties
v-divider
v-divider
template(v-if='props.item.kind === `IMAGE`')
template(v-if='props.item.kind === `IMAGE`')
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-list-tile-avatar
v-icon(color='indigo') crop_rotate
v-icon(color='indigo') crop_rotate
v-list-tile-content Edit
v-list-tile-content Edit
v-divider
v-divider
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-list-tile-avatar
v-icon(color='blue') keyboard
v-icon(color='blue') keyboard
v-list-tile-content Rename / Move
v-list-tile-content Rename / Move
v-divider
v-divider
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-list-tile-avatar
v-icon(color='red') delete
v-icon(color='red') delete
v-list-tile-content Delete
v-list-tile-content Delete
...
@@ -147,6 +168,8 @@ import 'filepond/dist/filepond.min.css'
...
@@ -147,6 +168,8 @@ import 'filepond/dist/filepond.min.css'
import
listAssetQuery
from
'gql/editor/editor-media-query-list.gql'
import
listAssetQuery
from
'gql/editor/editor-media-query-list.gql'
const
FilePond
=
vueFilePond
()
const
FilePond
=
vueFilePond
()
const
localeSegmentRegex
=
/^
[
A-Z
]{2}(
-
[
A-Z
]{2})?
$/i
const
disallowedFolderChars
=
/
[
A-Z()=.!@#$%?&*+`~<>,;:
\\/
[
\]
¬{|
]
/
export
default
{
export
default
{
components
:
{
components
:
{
...
@@ -172,7 +195,9 @@ export default {
...
@@ -172,7 +195,9 @@ export default {
],
],
imageAlignment
:
''
,
imageAlignment
:
''
,
currentFileId
:
null
,
currentFileId
:
null
,
loading
:
false
loading
:
false
,
newFolderDialog
:
false
,
newFolderName
:
''
}
}
},
},
computed
:
{
computed
:
{
...
@@ -191,12 +216,24 @@ export default {
...
@@ -191,12 +216,24 @@ export default {
headers
()
{
headers
()
{
return
_
.
compact
([
return
_
.
compact
([
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'ID'
,
value
:
'id'
,
width
:
50
,
align
:
'right'
},
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'ID'
,
value
:
'id'
,
width
:
50
,
align
:
'right'
},
{
text
:
'
Title'
,
value
:
'titl
e'
},
{
text
:
'
Filename'
,
value
:
'filenam
e'
},
this
.
$vuetify
.
breakpoint
.
lgAndUp
&&
{
text
:
'Type'
,
value
:
'
path
'
,
width
:
50
},
this
.
$vuetify
.
breakpoint
.
lgAndUp
&&
{
text
:
'Type'
,
value
:
'
ext
'
,
width
:
50
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'File Size'
,
value
:
'
createdAt'
,
width
:
15
0
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'File Size'
,
value
:
'
fileSize'
,
width
:
11
0
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'
Last Updated'
,
value
:
'upd
atedAt'
,
width
:
150
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'
Added'
,
value
:
'cre
atedAt'
,
width
:
150
},
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'
'
,
value
:
''
,
width
:
50
,
sortable
:
false
}
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'
Actions'
,
value
:
''
,
width
:
40
,
sortable
:
false
,
align
:
'right'
}
])
])
},
isFolderNameValid
()
{
return
this
.
newFolderName
.
length
>
1
&&
!
localeSegmentRegex
.
test
(
this
.
newFolderName
)
&&
!
disallowedFolderChars
.
test
(
this
.
newFolderName
)
}
},
watch
:
{
newFolderDialog
(
newValue
,
oldValue
)
{
if
(
newValue
)
{
this
.
$nextTick
(()
=>
{
this
.
$refs
.
folderNameIpt
.
focus
()
})
}
}
}
},
},
filters
:
{
filters
:
{
...
@@ -227,8 +264,13 @@ export default {
...
@@ -227,8 +264,13 @@ export default {
},
},
methods
:
{
methods
:
{
insert
()
{
insert
()
{
const
asset
=
_
.
find
(
this
.
assets
,
[
'id'
,
this
.
currentFileId
])
this
.
$root
.
$emit
(
'editorInsert'
,
{
kind
:
asset
.
kind
,
path
:
`/
${
asset
.
filename
}
`
,
text
:
asset
.
filename
})
this
.
activeModal
=
''
this
.
activeModal
=
''
},
},
browse
()
{
browse
()
{
this
.
$refs
.
pond
.
browse
()
this
.
$refs
.
pond
.
browse
()
...
@@ -262,6 +304,9 @@ export default {
...
@@ -262,6 +304,9 @@ export default {
},
5000
)
},
5000
)
await
this
.
$apollo
.
queries
.
assets
.
refetch
()
await
this
.
$apollo
.
queries
.
assets
.
refetch
()
},
async
createFolder
()
{
}
}
},
},
apollo
:
{
apollo
:
{
...
...
client/scss/components/v-dialog.scss
View file @
7b08c8bb
...
@@ -26,6 +26,12 @@
...
@@ -26,6 +26,12 @@
background-image
:
radial-gradient
(
ellipse
at
top
,
mc
(
'grey'
,
'800'
)
,
mc
(
'grey'
,
'900'
))
,
background-image
:
radial-gradient
(
ellipse
at
top
,
mc
(
'grey'
,
'800'
)
,
mc
(
'grey'
,
'900'
))
,
radial-gradient
(
ellipse
at
bottom
,
mc
(
'grey'
,
'800'
)
,
mc
(
'grey'
,
'900'
));
radial-gradient
(
ellipse
at
bottom
,
mc
(
'grey'
,
'800'
)
,
mc
(
'grey'
,
'900'
));
}
}
&
.is-teal
{
background-color
:
mc
(
'teal'
,
'700'
);
background-image
:
radial-gradient
(
ellipse
at
top
,
mc
(
'teal'
,
'500'
)
,
mc
(
'teal'
,
'700'
))
,
radial-gradient
(
ellipse
at
bottom
,
mc
(
'teal'
,
'800'
)
,
mc
(
'teal'
,
'700'
));
}
}
}
.v-dialog--fullscreen
{
.v-dialog--fullscreen
{
...
...
client/themes/default/scss/app.scss
View file @
7b08c8bb
...
@@ -235,6 +235,92 @@
...
@@ -235,6 +235,92 @@
li
+
li
{
li
+
li
{
margin-top
:
.5rem
;
margin-top
:
.5rem
;
}
}
&
.links-list
{
li
{
background-color
:
mc
(
'grey'
,
'50'
);
background-image
:
linear-gradient
(
to
bottom
,
#FFF
,
mc
(
'grey'
,
'50'
));
border-right
:
1px
solid
mc
(
'grey'
,
'200'
);
border-bottom
:
1px
solid
mc
(
'grey'
,
'200'
);
border-left
:
5px
solid
mc
(
'grey'
,
'300'
);
box-shadow
:
0
3px
8px
0
rgba
(
116
,
129
,
141
,
0
.1
);
padding
:
1rem
;
border-radius
:
5px
;
font-weight
:
500
;
&
:hover
{
background-image
:
linear-gradient
(
to
bottom
,
#FFF
,
lighten
(
mc
(
'blue'
,
'50'
)
,
4%
));
border-left-color
:
mc
(
'blue'
,
'500'
);
cursor
:
pointer
;
}
&
:
:
before
{
content
:
''
;
display
:
none
;
}
>
a
{
display
:
block
;
text-decoration
:
none
;
margin
:
-1rem
;
padding
:
1rem
;
}
@at-root
.theme--dark
&
{
background-color
:
mc
(
'grey'
,
'50'
);
background-image
:
linear-gradient
(
to
bottom
,
lighten
(
mc
(
'grey'
,
'900'
)
,
5%
)
,
mc
(
'grey'
,
'900'
));
border-right
:
1px
solid
mc
(
'grey'
,
'900'
);
border-bottom
:
1px
solid
mc
(
'grey'
,
'900'
);
border-left
:
5px
solid
mc
(
'grey'
,
'700'
);
box-shadow
:
0
3px
8px
0
rgba
(
0
,
0
,
0
,
0
.1
);
&
:hover
{
background-image
:
linear-gradient
(
to
bottom
,
lighten
(
mc
(
'grey'
,
'900'
)
,
2%
)
,
darken
(
mc
(
'grey'
,
'900'
)
,
3%
));
border-left-color
:
mc
(
'blue'
,
'500'
);
cursor
:
pointer
;
}
}
}
}
&
.grid-list
{
margin
:
1rem
24px
0
24px
;
background-color
:
#FFF
;
border
:
1px
solid
mc
(
'grey'
,
'200'
);
padding
:
1px
;
display
:
inline-block
;
@at-root
.theme--dark
&
{
background-color
:
#000
;
border
:
1px
solid
mc
(
'grey'
,
'800'
);
}
li
{
background-color
:
mc
(
'grey'
,
'50'
);
padding
:
.6rem
1rem
;
display
:
block
;
&
:nth-child
(
odd
)
{
background-color
:
mc
(
'grey'
,
'100'
);
}
&
+
li
{
margin-top
:
0
;
}
&
:
:
before
{
color
:
mc
(
'grey'
,
'400'
);
}
@at-root
.theme--dark
&
{
background-color
:
mc
(
'grey'
,
'900'
);
&
:nth-child
(
odd
)
{
background-color
:
darken
(
mc
(
'grey'
,
'900'
)
,
5%
);
}
}
}
}
}
}
ul
{
ul
{
...
@@ -264,6 +350,11 @@
...
@@ -264,6 +350,11 @@
&
:
:
before
,
&::
after
{
&
:
:
before
,
&::
after
{
display
:
none
;
display
:
none
;
}
}
@at-root
.theme--dark
&
{
background-color
:
darken
(
mc
(
'grey'
,
'900'
)
,
5%
);
color
:
mc
(
'indigo'
,
'100'
);
}
}
}
.prismjs
{
.prismjs
{
...
...
server/models/assets.js
View file @
7b08c8bb
...
@@ -77,6 +77,7 @@ module.exports = class Asset extends Model {
...
@@ -77,6 +77,7 @@ module.exports = class Asset extends Model {
kind
:
_
.
startsWith
(
opts
.
mimetype
,
'image/'
)
?
'image'
:
'binary'
,
kind
:
_
.
startsWith
(
opts
.
mimetype
,
'image/'
)
?
'image'
:
'binary'
,
mime
:
opts
.
mimetype
,
mime
:
opts
.
mimetype
,
fileSize
:
opts
.
size
,
fileSize
:
opts
.
size
,
folderId
:
null
,
authorId
:
opts
.
userId
authorId
:
opts
.
userId
})
})
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment