diff --git a/.scss-lint.yml b/.scss-lint.yml index dcd4cac780aa8bb49bc9edd23b0372e38e09579d..180d377d6f86a18d7f8d8e9c3ed559dd0d3e4abc 100644 --- a/.scss-lint.yml +++ b/.scss-lint.yml @@ -59,6 +59,8 @@ linters: # Reports when you define the same property twice in a single rule set. DuplicateProperty: enabled: true + ignore_consecutive: + - cursor # Separate rule, function, and mixin declarations with empty lines. EmptyLineBetweenBlocks: diff --git a/app/assets/javascripts/ide/components/ide_file_buttons.vue b/app/assets/javascripts/ide/components/ide_file_buttons.vue index 6d07329df714ffaa791aa444acde6082e5f34ede..a6c6f46a14418a5ad9c169c69aab7916a8048c86 100644 --- a/app/assets/javascripts/ide/components/ide_file_buttons.vue +++ b/app/assets/javascripts/ide/components/ide_file_buttons.vue @@ -36,6 +36,7 @@ export default { > - import icon from '~/vue_shared/components/icon.vue'; - import tooltip from '~/vue_shared/directives/tooltip'; - import timeAgoMixin from '~/vue_shared/mixins/timeago'; +import icon from '~/vue_shared/components/icon.vue'; +import tooltip from '~/vue_shared/directives/tooltip'; +import timeAgoMixin from '~/vue_shared/mixins/timeago'; - export default { - components: { - icon, +export default { + components: { + icon, + }, + directives: { + tooltip, + }, + mixins: [timeAgoMixin], + props: { + file: { + type: Object, + required: true, }, - directives: { - tooltip, - }, - mixins: [ - timeAgoMixin, - ], - props: { - file: { - type: Object, - required: true, - }, - }, - }; + }, +}; diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js index 6a143e518f97c581cd4be9f0cb4e91b29a8a4578..eeb14b5490c33abd50daf4fd420e07d2daa254b3 100644 --- a/app/assets/javascripts/ide/stores/mutations/file.js +++ b/app/assets/javascripts/ide/stores/mutations/file.js @@ -43,6 +43,7 @@ export default { raw: null, baseRaw: null, html: data.html, + size: data.size, }); }, [types.SET_FILE_RAW_DATA](state, { file, raw }) { diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 4befcc501efd7ea0ac29742b7309ac084b61548d..05a019de54fc2efbacea0d3be0e374878779a4be 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -40,6 +40,7 @@ export const dataStructure = () => ({ eol: '', viewMode: 'edit', previewMode: null, + size: 0, }); export const decorateData = entity => { diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue index fb8ccea91c79a6a5adbac3628bb992b798018ba5..4155e1bab9ca449b70a5e316347aea97ab7c1722 100644 --- a/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/content_viewer/content_viewer.vue @@ -1,17 +1,24 @@ + + diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue new file mode 100644 index 0000000000000000000000000000000000000000..a5999f909ca176ada897b2ab04120737891f0af3 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue @@ -0,0 +1,68 @@ + + + diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss index 8cc5c8fc877ebb47828844b96bdf824e3f8b49a8..a414deb892121cff4cca8c38d9c15bf9f0854aef 100644 --- a/app/assets/stylesheets/pages/repo.scss +++ b/app/assets/stylesheets/pages/repo.scss @@ -312,6 +312,45 @@ height: 100%; overflow: auto; + .file-container { + background-color: $gray-darker; + display: flex; + height: 100%; + align-items: center; + justify-content: center; + + text-align: center; + + .file-content { + padding: $gl-padding; + max-width: 100%; + max-height: 100%; + + img { + max-width: 90%; + max-height: 90%; + } + + .isZoomable { + cursor: pointer; + cursor: zoom-in; + + &.isZoomed { + cursor: pointer; + cursor: zoom-out; + max-width: none; + max-height: none; + margin-right: $gl-padding; + } + } + } + + .file-info { + font-size: $label-font-size; + color: $diff-image-info-color; + } + } + .md-previewer { padding: $gl-padding; } diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js index c7c454a0b4511211b37b549f3b722887ec0fbfba..383f0cd29ea4fc981a4a85350923e5bc306b9560 100644 --- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js +++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js @@ -38,4 +38,33 @@ describe('ContentViewer', () => { done(); }); }); + + it('renders image preview', done => { + createComponent({ + path: 'test.jpg', + fileSize: 1024, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe('test.jpg'); + + done(); + }); + }); + + it('renders fallback download control', done => { + createComponent({ + path: 'test.abc', + fileSize: 1024, + }); + + setTimeout(() => { + expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain( + 'test.abc (1.00 KiB)', + ); + expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toContain('Download'); + + done(); + }); + }); });