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
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 {
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
*/
insertAfter
({
content
,
newLine
})
{
...
...
@@ -457,6 +464,25 @@ export default {
toggleFullscreen
()
{
this
.
cm
.
setOption
(
'fullScreen'
,
true
)
}
},
mounted
()
{
this
.
$root
.
$on
(
'editorInsert'
,
opts
=>
{
switch
(
opts
.
kind
)
{
case
'IMAGE'
:
this
.
insertAtCursor
({
content
:
`![
${
opts
.
text
}
](
${
opts
.
path
}
)`
})
break
case
'BINARY'
:
this
.
insertAtCursor
({
content
:
`[
${
opts
.
text
}
](
${
opts
.
path
}
)`
})
break
}
})
},
beforeDestroy
()
{
this
.
$root
.
$off
(
'editorInsert'
)
}
}
</
script
>
...
...
client/components/editor/editor-modal-blocks.vue
View file @
7b08c8bb
...
...
@@ -2,7 +2,7 @@
v-card.editor-modal-blocks.animated.fadeInLeft(flat, tile)
v-container.pa-3(grid-list-lg, fluid)
v-layout(row, wrap)
v-flex(xs3)
v-flex(xs
12, lg4, xl
3)
v-card.radius-7(light)
v-card-text
.d-flex
...
...
@@ -82,5 +82,10 @@ export default {
width
:
calc
(
100vw
-
64px
-
17px
);
height
:
calc
(
100vh
-
112px
-
24px
);
background-color
:
rgba
(
darken
(
mc
(
'grey'
,
'900'
)
,
3%
)
,
.9
)
!
important
;
@include
until
(
$tablet
)
{
left
:
40px
;
width
:
calc
(
100vw
-
40px
);
}
}
</
style
>
client/components/editor/editor-modal-media.vue
View file @
7b08c8bb
...
...
@@ -11,9 +11,30 @@
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
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
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(
:items='assets'
:headers='headers'
...
...
@@ -26,40 +47,40 @@
template(slot='items', slot-scope='props')
tr.is-clickable(
@click.left='currentFileId = props.item.id'
@click.right=''
@click.right
.prevent
=''
:class='currentFileId === props.item.id ? `teal lighten-5` : ``'
)
td.text-xs-right(v-if='$vuetify.breakpoint.smAndUp')
{{
props
.
item
.
id
}}
td
.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')
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
)
}}
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')
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-list.py-0
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-icon(color='teal') short_text
v-list-tile-content Properties
v-divider
template(v-if='props.item.kind === `IMAGE`')
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-icon(color='indigo') crop_rotate
v-list-tile-content Edit
v-divider
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-icon(color='blue') keyboard
v-list-tile-content Rename / Move
v-divider
v-list-tile
v-list-tile
(@click='')
v-list-tile-avatar
v-icon(color='red') delete
v-list-tile-content Delete
...
...
@@ -147,6 +168,8 @@ import 'filepond/dist/filepond.min.css'
import
listAssetQuery
from
'gql/editor/editor-media-query-list.gql'
const
FilePond
=
vueFilePond
()
const
localeSegmentRegex
=
/^
[
A-Z
]{2}(
-
[
A-Z
]{2})?
$/i
const
disallowedFolderChars
=
/
[
A-Z()=.!@#$%?&*+`~<>,;:
\\/
[
\]
¬{|
]
/
export
default
{
components
:
{
...
...
@@ -172,7 +195,9 @@ export default {
],
imageAlignment
:
''
,
currentFileId
:
null
,
loading
:
false
loading
:
false
,
newFolderDialog
:
false
,
newFolderName
:
''
}
},
computed
:
{
...
...
@@ -191,12 +216,24 @@ export default {
headers
()
{
return
_
.
compact
([
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'ID'
,
value
:
'id'
,
width
:
50
,
align
:
'right'
},
{
text
:
'
Title'
,
value
:
'titl
e'
},
this
.
$vuetify
.
breakpoint
.
lgAndUp
&&
{
text
:
'Type'
,
value
:
'
path
'
,
width
:
50
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'File Size'
,
value
:
'
createdAt'
,
width
:
15
0
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'
Last Updated'
,
value
:
'upd
atedAt'
,
width
:
150
},
this
.
$vuetify
.
breakpoint
.
smAndUp
&&
{
text
:
'
'
,
value
:
''
,
width
:
50
,
sortable
:
false
}
{
text
:
'
Filename'
,
value
:
'filenam
e'
},
this
.
$vuetify
.
breakpoint
.
lgAndUp
&&
{
text
:
'Type'
,
value
:
'
ext
'
,
width
:
50
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'File Size'
,
value
:
'
fileSize'
,
width
:
11
0
},
this
.
$vuetify
.
breakpoint
.
mdAndUp
&&
{
text
:
'
Added'
,
value
:
'cre
atedAt'
,
width
:
150
},
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
:
{
...
...
@@ -227,8 +264,13 @@ export default {
},
methods
:
{
insert
()
{
const
asset
=
_
.
find
(
this
.
assets
,
[
'id'
,
this
.
currentFileId
])
this
.
$root
.
$emit
(
'editorInsert'
,
{
kind
:
asset
.
kind
,
path
:
`/
${
asset
.
filename
}
`
,
text
:
asset
.
filename
})
this
.
activeModal
=
''
},
browse
()
{
this
.
$refs
.
pond
.
browse
()
...
...
@@ -262,6 +304,9 @@ export default {
},
5000
)
await
this
.
$apollo
.
queries
.
assets
.
refetch
()
},
async
createFolder
()
{
}
},
apollo
:
{
...
...
client/scss/components/v-dialog.scss
View file @
7b08c8bb
...
...
@@ -26,6 +26,12 @@
background-image
:
radial-gradient
(
ellipse
at
top
,
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
{
...
...
client/themes/default/scss/app.scss
View file @
7b08c8bb
...
...
@@ -235,6 +235,92 @@
li
+
li
{
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
{
...
...
@@ -264,6 +350,11 @@
&
:
:
before
,
&::
after
{
display
:
none
;
}
@at-root
.theme--dark
&
{
background-color
:
darken
(
mc
(
'grey'
,
'900'
)
,
5%
);
color
:
mc
(
'indigo'
,
'100'
);
}
}
.prismjs
{
...
...
server/models/assets.js
View file @
7b08c8bb
...
...
@@ -77,6 +77,7 @@ module.exports = class Asset extends Model {
kind
:
_
.
startsWith
(
opts
.
mimetype
,
'image/'
)
?
'image'
:
'binary'
,
mime
:
opts
.
mimetype
,
fileSize
:
opts
.
size
,
folderId
:
null
,
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