loft 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ccac65018592298f1e32ccdc370f0c0b7bb845ad
4
- data.tar.gz: ec7a87c3cbc6b444957a82ba71a9086a613bf1a4
3
+ metadata.gz: cb9743cdf74ccbaeba6c950df7dd300cbf01366e
4
+ data.tar.gz: 4c0eab2a4a70f0b97040990532c2d109abd41fb3
5
5
  SHA512:
6
- metadata.gz: ea38f740b9bc4a63612cc738e4603e539f72a59f1f93047fd684d3fa88d7385c2b6fd312e88ba533547c7a99f193799a59c5e02f526cbb10c383bb350f2cf8b9
7
- data.tar.gz: 038275e74677eacc7818653af9f2567afe4d2eed4cf830c2827107d03059d27bc4ee96ebf2e6c29bfa09bbefa90fd41b0a366fc45dfa025b149bd882fb3b4286
6
+ metadata.gz: e89aaeae7d1f9a4a8de33b35558b5302ac667254bb2c529764c5f8a9cb51e952e010ed678618e0199a78c8d27820f693d689381379fad26828e9e19a1ec060f7
7
+ data.tar.gz: 8d9d873ea22d68690893052c4bb81c69beaf01179819d3b308625ae1ca3395e1977af1f15537f6e50307d4b99d3331a8ea13ec199f68fbac4e0af4b56abc076f
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ .DS_Store
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Loft
2
2
 
3
- ## Character CMS media assets plugin
3
+ *Media assets manager for [Character CMS](https://github.com/slate-studio/chr).*
4
+
4
5
 
5
6
  ### Installation
6
7
 
@@ -10,46 +11,58 @@ Add to ```Gemfile```:
10
11
 
11
12
  Setup a new model for assets ```asset.rb```:
12
13
 
13
- class Asset
14
- include Mongoid::Document
15
- include Mongoid::Timestamps
16
- include Mongoid::SerializableId
17
- include Mongoid::LoftAsset
18
- end
14
+ ```ruby
15
+ class Asset
16
+ include Mongoid::Document
17
+ include Mongoid::Timestamps
18
+ include Mongoid::SerializableId
19
+ include Mongoid::LoftAsset
20
+ end
21
+ ```
19
22
 
20
23
  Add controller for asset model to make it accesible via CMS, e.g. ```app/controllers/admin/assets_controller.rb```:
21
24
 
22
- class Admin::AssetsController < Admin::BaseController
23
- mongosteen
24
- has_scope :by_type
25
- json_config({ methods: [ :list_item_thumbnail, :created_ago ] })
26
- end
25
+ ```ruby
26
+ class Admin::AssetsController < Admin::BaseController
27
+ mongosteen
28
+ has_scope :by_type
29
+ json_config({ methods: [ :item_thumbnail, :created_ago ] })
30
+ end
31
+ ```
27
32
 
28
33
  Add admin assets controller to ```routes.rb```:
29
34
 
30
- resources :assets
35
+ ```ruby
36
+ resources :assets
37
+ ```
31
38
 
32
39
  Add to ```admin.scss```:
33
40
 
34
- @import "loft";
41
+ ```scss
42
+ @import "loft";
43
+ ```
35
44
 
36
45
  Add to ```admin.coffee``` character configuration object:
37
46
 
38
- assets: new Loft('Library', 'asset', '/admin/assets')
47
+ ```coffee
48
+ assets: new Loft('Library', 'asset', '/admin/assets')
49
+ ```
39
50
 
40
51
 
41
52
  ## Loft family
42
53
 
54
+ - [Character](https://github.com/slate-studio/chr): Powerful javascript CMS for apps
43
55
  - [Mongosteen](https://github.com/slate-studio/mongosteen): An easy way to add restful actions for mongoid models
44
- - [Character](https://github.com/slate-studio/chr): A simple and lightweight javascript library for building data management web apps
45
56
  - [Inverter](https://github.com/slate-studio/inverter): An easy way to connect Rails templates content to CMS
46
57
 
47
- ## Credits
48
58
 
49
- [![Slate Studio](https://slate-git-images.s3-us-west-1.amazonaws.com/slate.png)](http://slatestudio.com)
59
+ ## License
50
60
 
51
- Inverter is maintained and funded by [Slate Studio, LLC](http://slatestudio.com). Tweet your questions or suggestions to [@slatestudio](https://twitter.com/slatestudio) and while you’re at it follow us too.
61
+ Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com). Loft is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
52
62
 
53
- ## License
54
63
 
55
- Copyright © 2015 [Slate Studio, LLC](http://slatestudio.com). Character is free software, and may be redistributed under the terms specified in the [license](LICENSE.md).
64
+ ## About Slate Studio
65
+
66
+ [![Slate Studio](https://slate-git-images.s3-us-west-1.amazonaws.com/slate.png)](http://slatestudio.com)
67
+
68
+ Loft is maintained and funded by [Slate Studio, LLC](http://slatestudio.com). Tweet your questions or suggestions to [@slatestudio](https://twitter.com/slatestudio) and while you’re at it follow us too.
@@ -0,0 +1,87 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # INPUT LOFT IMAGE
11
+ # -----------------------------------------------------------------------------
12
+ class @InputLoftImage extends InputString
13
+ _addInput: ->
14
+ @config.placeholder ?= 'Image url'
15
+
16
+ @$input =$ "<input type='string' name='#{ @name }' value='#{ @_valueSafe() }' id='#{ @name }' />"
17
+ @$el.append @$input
18
+ @$input.on 'change', (e) =>
19
+ @updateValue($(e.target).val())
20
+
21
+ @_add_image()
22
+ @_add_choose_button()
23
+ @_add_remove_button()
24
+ @_update_input_class()
25
+
26
+
27
+ _add_image: ->
28
+ @$image =$ "<a href='' target='_blank' class='image'><img src='' /></a>"
29
+ @$el.append @$image
30
+ @_update_image()
31
+
32
+
33
+ _add_choose_button: ->
34
+ @$chooseBtn =$ "<a href='#' class='choose'></a><br/>"
35
+ @$el.append @$chooseBtn
36
+
37
+ @_update_choose_button_title()
38
+
39
+ @$chooseBtn.on 'click', (e) =>
40
+ e.preventDefault()
41
+ chr.modules.assets.showModal 'images', false, (objects) =>
42
+ asset = objects[0]
43
+ @updateValue(asset.file.url)
44
+
45
+
46
+ _add_remove_button: ->
47
+ @$removeBtn =$ "<a href='#' class='remove'>Remove</a>"
48
+ @$el.append @$removeBtn
49
+
50
+ @$removeBtn.on 'click', (e) =>
51
+ e.preventDefault()
52
+ if confirm('Are you sure?')
53
+ @updateValue('')
54
+
55
+
56
+ _update_image: ->
57
+ url = @value
58
+ @$image.attr('href', @value).children().attr('src', @value)
59
+ if @value == '' then @$image.hide() else @$image.show()
60
+
61
+
62
+ _update_choose_button_title: ->
63
+ title = if @value == '' then 'Choose or upload' else 'Choose other or upload'
64
+ @$chooseBtn.html(title)
65
+
66
+
67
+ _update_input_class: ->
68
+ if @value == '' then @$el.removeClass('has-value') else @$el.addClass('has-value')
69
+
70
+
71
+ #
72
+ # PUBLIC
73
+ #
74
+
75
+ updateValue: (@value) ->
76
+ @$input.val(@value)
77
+
78
+ @_update_image()
79
+ @_update_choose_button_title()
80
+ @_update_input_class()
81
+
82
+
83
+ _chrFormInputs['loft-image'] = InputLoftImage
84
+
85
+
86
+
87
+
@@ -1,10 +1,17 @@
1
- # add file icon
2
- # add selectbox for delete
3
- # add modal dialog mode
4
-
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # Loft Asset Item
11
+ # -----------------------------------------------------------------------------
5
12
  class @LoftAssetItem extends Item
6
13
  constructor: (@module, @path, @object, @config) ->
7
- @$el =$ "<div class='item asset' data-id='#{ @object._id }' data-title=''></div>"
14
+ @$el =$ "<div class='item asset asset-#{ @object.type }' data-id='#{ @object._id }' data-title=''></div>"
8
15
  @render()
9
16
 
10
17
 
@@ -18,14 +25,26 @@ class @LoftAssetItem extends Item
18
25
  @$link =$ "<a class='asset-icon' href='#{ @object.file.url }' target='_blank'></a>"
19
26
  @$el.prepend(@$link)
20
27
 
28
+ # thumbnail for images
29
+ if @object.type == 'image' && @object.grid_item_thumbnail != ''
30
+ @$thumbnailSmall =$ "<img class='asset-thumbnail-small' src='#{ @object.item_thumbnail.small }' />"
31
+ @$thumbnailMedium =$ "<img class='asset-thumbnail-medium' src='#{ @object.item_thumbnail.medium }' />"
32
+ @$link.append @$thumbnailSmall
33
+ @$link.append @$thumbnailMedium
34
+
21
35
  # checkbox for item selection
22
- @$checkbox =$ "<input class='asset-checkbox' type='checkbox' />"
36
+ @$checkbox =$ "<div class='asset-checkbox'></div>"
37
+ @$checkboxInput =$ "<input type='checkbox' />"
38
+ @$checkbox.append(@$checkboxInput)
23
39
  @$el.prepend(@$checkbox)
24
40
 
41
+
25
42
  # input for assets name
26
43
  name = @$el.attr('data-title')
27
- @$nameInput =$ "<input class='asset-name' type='text' value='#{ name }' />"
28
- @$title.before @$nameInput
44
+ @$name =$ "<div class='asset-name'></div>"
45
+ @$nameInput =$ "<input type='text' value='#{ name }' />"
46
+ @$name.append @$nameInput
47
+ @$title.before @$name
29
48
  @_bind_name_input()
30
49
 
31
50
  # handler for asset name change on title click
@@ -1,5 +1,16 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # Loft Group Actions
11
+ # -----------------------------------------------------------------------------
1
12
  class @LoftGroupActions
2
- constructor: (@list, @module) ->
13
+ constructor: (@list, @loft) ->
3
14
  @_render()
4
15
  @_bind_checkboxes()
5
16
 
@@ -8,53 +19,83 @@ class @LoftGroupActions
8
19
  @$el =$ "<div class='assets-group-actions' style='display:none;'></div>"
9
20
  @list.$header.append @$el
10
21
 
11
- @$unselectBtn =$ "<a href='#' class='unselect'>Unselect All</a>"
12
- @$unselectBtn.on 'click', (e) => e.preventDefault(); @_unselect_list_items()
13
- @$el.append @$unselectBtn
22
+ # accept selected
23
+ @$acceptBtn =$ "<a href='#' class='accept'>Accept</a>"
24
+ @$acceptBtn.on 'click', (e) => e.preventDefault(); @_accept_selected_items()
25
+ @$el.append @$acceptBtn
14
26
 
27
+ # delete button
15
28
  @$deleteBtn =$ "<a href='#' class='delete'>Delete Selected</a>"
16
29
  @$deleteBtn.on 'click', (e) => e.preventDefault(); @_delete_selected_list_items()
17
30
  @$el.append @$deleteBtn
18
31
 
32
+ # unselect button
33
+ @$unselectBtn =$ "<a href='#' class='unselect'>Unselect</a>"
34
+ @$unselectBtn.on 'click', (e) => e.preventDefault(); @_unselect_list_items()
35
+ @$el.append @$unselectBtn
36
+
19
37
 
20
38
  _bind_checkboxes: ->
21
- @list.$el.on 'click', '.asset .asset-checkbox', (e) =>
39
+ @list.$el.on 'click', '.asset .asset-checkbox input', (e) =>
40
+ # when multiple selection disabled select only one asset a time
41
+ if ! @loft.selectMultipleAssets
42
+ @_select_single_item($(e.target))
43
+
22
44
  selectedItems = @_selected_list_items()
23
45
  if selectedItems.length > 0
24
46
  @_show()
25
47
  else
26
- @_hide()
48
+ @hide()
49
+
50
+
51
+ _select_single_item: ($checkbox) ->
52
+ if $checkbox.prop('checked')
53
+ @list.$el.find('.asset .asset-checkbox input:checked').prop('checked' , false)
54
+ $checkbox.prop('checked', true)
27
55
 
28
56
 
29
57
  _selected_list_items: ->
30
- @list.$el.find '.asset .asset-checkbox:checked'
58
+ $.map @list.$el.find('.asset .asset-checkbox input:checked'), (checkbox) -> $(checkbox).parent().parent()
31
59
 
32
60
 
33
61
  _unselect_list_items: ->
34
- @list.$el.find('.asset .asset-checkbox').prop('checked', false)
35
- @_hide()
62
+ @list.$el.find('.asset .asset-checkbox input').prop('checked', false)
63
+ @hide()
36
64
 
37
65
 
38
66
  _delete_selected_list_items: ->
39
67
  if confirm("Are you sure?")
40
- selectedItems = @_selected_list_items()
41
- filesToRemoveCounter = selectedItems.length
68
+ $selectedItems = @_selected_list_items()
69
+ filesToRemoveCounter = $selectedItems.length
42
70
 
43
71
  # we have on scroll pagination so after some items are removed,
44
72
  # next page request might skip items that replaced removed ones
45
- for item in selectedItems
46
- objectId = $(item).parent().attr('data-id')
73
+ for $item in $selectedItems
74
+ objectId = $item.attr('data-id')
47
75
  @list.config.arrayStore.remove objectId,
48
76
  onSuccess: => # success notification
49
77
  onError: => # error notification
50
- @_hide()
78
+ @hide()
79
+
80
+
81
+ _accept_selected_items: ->
82
+ $selectedItems = @_selected_list_items()
83
+
84
+ objects = []
85
+ for $item in $selectedItems
86
+ objectId = $item.attr('data-id')
87
+ object = @list.config.arrayStore.get(objectId)
88
+ objects.push object
89
+
90
+ @loft.onAcceptCallback(objects)
91
+ @loft.closeModal()
51
92
 
52
93
 
53
94
  _show: ->
54
95
  @$el.show()
55
96
 
56
97
 
57
- _hide: ->
98
+ hide: ->
58
99
  @$el.hide()
59
100
 
60
101
 
@@ -1,3 +1,20 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # Loft
11
+ #
12
+ # public methods:
13
+ # new Loft(title, @resource, @resourcePath)
14
+ # showModal(assetType, @selectMultipleAssets, @onAcceptCallback)
15
+ # closeModal()
16
+ #
17
+ # -----------------------------------------------------------------------------
1
18
  class @Loft
2
19
  constructor: (title, @resource, @resourcePath) ->
3
20
  @module = {}
@@ -6,8 +23,9 @@ class @Loft
6
23
  @_uploadsCounter = 0
7
24
 
8
25
  moduleConfig =
9
- title: title
26
+ title: title
10
27
  showNestedListsAside: true
28
+ itemClass: LoftTypeItem
11
29
  items:
12
30
  assets_all: @_nested_list_config 'All'
13
31
  assets_images: @_nested_list_config 'Images', 'image'
@@ -27,12 +45,28 @@ class @Loft
27
45
  @module = module
28
46
  @store = @module.nestedLists.assets_all.config.arrayStore
29
47
 
48
+ # API method
49
+ @module.showModal = (assetType, selectMultipleAssets, callback) =>
50
+ @showModal(assetType, selectMultipleAssets, callback)
51
+ @selectMultipleAssets = true
52
+
53
+ # modal close button
54
+ @module.rootList.$modalCloseBtn =$ "<a href='#' class='modal-close'>Cancel</a>"
55
+ @module.rootList.$header.prepend @module.rootList.$modalCloseBtn
56
+ @module.rootList.$modalCloseBtn.on 'click', (e) => e.preventDefault() ; @closeModal()
57
+
58
+ # enable grid mode as default on desktop/tablet
59
+ if ! _isMobile()
60
+ @module.$el.addClass('grid-mode')
61
+
30
62
 
31
63
  _nested_list_config: (moduleName, assetType) ->
32
64
  arrayStoreConfig =
33
- resource: @resource
34
- path: @resourcePath
35
- searchable: true
65
+ resource: @resource
66
+ path: @resourcePath
67
+ searchable: true
68
+ sortBy: 'created_at'
69
+ sortReverse: true
36
70
 
37
71
  if assetType
38
72
  $.extend(arrayStoreConfig, { urlParams: { by_type: assetType } })
@@ -44,15 +78,12 @@ class @Loft
44
78
  itemClass: LoftAssetItem
45
79
  arrayStore: new MongosteenArrayStore(arrayStoreConfig)
46
80
  onListInit: (list) => @_inititialize_list(list)
81
+ onListShow: (list) => @_clear_assets_selection()
47
82
 
48
83
  return config
49
84
 
50
85
 
51
86
  _inititialize_list: (list) ->
52
- # uploading spinner
53
- list.$loading =$ "<div class='loader'></div>"
54
- list.$el.append list.$loading
55
-
56
87
  # file input button for uploading new files
57
88
  list.$uploadInput =$ "<input class='asset-upload' type='file' multiple='multiple' />"
58
89
  list.$search.before list.$uploadInput
@@ -64,7 +95,12 @@ class @Loft
64
95
  @_upload(file, list) for file in files
65
96
 
66
97
  # group actions toolbar
67
- list.$groupActions = new LoftGroupActions(list, this)
98
+ list.groupActions = new LoftGroupActions(list, this)
99
+
100
+ # grid/list checkbox
101
+ list.$switchMode =$ "<a class='assets-switch-mode' href='#'></a>"
102
+ list.$backBtn.after list.$switchMode
103
+ list.$switchMode.on 'click', (e) => e.preventDefault() ; @module.$el.toggleClass('grid-mode')
68
104
 
69
105
 
70
106
  _upload: (file, list) ->
@@ -74,8 +110,9 @@ class @Loft
74
110
  @_start_file_upload()
75
111
  @store.push obj,
76
112
  onSuccess: (object) => @_finish_file_upload(list)
77
- onError: (errors) => @_finish_file_upload(list)
78
- # + process validation errors, if any
113
+ onError: (errors) =>
114
+ @_finish_file_upload(list)
115
+ chr.showError('Can\'t upload file.')
79
116
 
80
117
 
81
118
  _start_file_upload: ->
@@ -89,11 +126,38 @@ class @Loft
89
126
  @module.$el.removeClass('assets-uploading')
90
127
 
91
128
  # update data in list if it's not assets_all,
92
- # in there new objects are added automatically
129
+ # in assets_all new objects are added automatically
93
130
  visibleList = @module.visibleNestedListShownWithParent()
94
131
  if visibleList.name != 'assets_all'
95
132
  visibleList.updateItems()
96
133
 
97
134
 
135
+ _clear_assets_selection: ->
136
+ for name, list of @module.nestedLists
137
+ list.groupActions.hide()
138
+ list.$items.find('.asset-checkbox').prop('checked', false)
139
+
140
+
141
+ closeModal: ->
142
+ @selectMultipleAssets = true
143
+ @_clear_assets_selection()
144
+ @module.$el.removeClass('module-modal')
145
+ @module.hide()
146
+
147
+
148
+ # chr.modules.assets.showModal()
149
+ showModal: (assetType='all', @selectMultipleAssets=false, @onAcceptCallback=$.noop) ->
150
+ # modal mode
151
+ @module.$el.addClass('module-modal')
152
+ # show nested list
153
+ @module.showNestedList("assets_#{ assetType }")
154
+ # select active item
155
+ @module.rootList.$items.children().removeClass('active')
156
+ @module.rootList.$items.children("[href='#/assets/assets_#{ assetType }']").addClass('active')
157
+ # show module
158
+ @module.show()
159
+
160
+
161
+
98
162
 
99
163
 
@@ -0,0 +1,37 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # Loft Asset Item
11
+ # -----------------------------------------------------------------------------
12
+ class @LoftTypeItem extends Item
13
+ onClick: (e) ->
14
+ if @.$el.hasClass('active') then e.preventDefault() ; return
15
+
16
+ if ! @module.$el.hasClass 'module-modal'
17
+ window._skipHashchange = true
18
+
19
+ location.hash = $(e.currentTarget).attr('href')
20
+ crumbs = location.href.split('/')
21
+
22
+ @module.showNestedList(_last(crumbs), true)
23
+
24
+ else
25
+ e.preventDefault()
26
+
27
+ $item = $(e.currentTarget)
28
+
29
+ $item.parent().children('.active').removeClass('active')
30
+ $item.addClass('active')
31
+
32
+ listName = $item.attr('href').split('/')[2]
33
+ @module.showNestedList(listName, true)
34
+
35
+
36
+
37
+
@@ -0,0 +1,67 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Author: Alexander Kravets <alex@slatestudio.com>,
3
+ # Slate Studio (http://www.slatestudio.com)
4
+ #
5
+ # Coding Guide:
6
+ # https://github.com/thoughtbot/guides/tree/master/style/coffeescript
7
+ # -----------------------------------------------------------------------------
8
+
9
+ # -----------------------------------------------------------------------------
10
+ # Redactor.js Loft Plugin
11
+ # -----------------------------------------------------------------------------
12
+
13
+ if ! RedactorPlugins then @RedactorPlugins = {}
14
+
15
+ RedactorPlugins.loft = ->
16
+ methods =
17
+ init: ->
18
+ imageButton = @button.add('image', 'Insert Image')
19
+ @button.addCallback(imageButton, @loft.showImagesModal)
20
+
21
+ fileButton = @button.add('file', 'Insert File')
22
+ @button.addCallback(fileButton, @loft.showAllModal)
23
+
24
+
25
+ showImagesModal: ->
26
+ chr.modules.assets.showModal 'images', true, (objects) => @loft.insertImages(objects)
27
+
28
+
29
+ # allow multiple assets when no text is selected
30
+ showAllModal: ->
31
+ multipleAssets = this.selection.getText() == ''
32
+ chr.modules.assets.showModal 'all', multipleAssets, (objects) => @loft.insertFiles(objects)
33
+
34
+
35
+ # if text is selected replace text with <a>{{ text }}</a>
36
+ # otherwise add link(s) split by <br/> tag
37
+ insertFiles: (objects) ->
38
+ if objects.length > 0
39
+ selectedText = this.selection.getText()
40
+ if selectedText != ''
41
+ asset = objects[0]
42
+ html = "<a href='#{ asset.file.url }' target='_blank'>#{ selectedText }</a>"
43
+ else
44
+ links = []
45
+ for asset in objects
46
+ links.push "<a href='#{ asset.file.url }' target='_blank'>#{ asset.name }</a>"
47
+ html = links.join('<br>')
48
+
49
+ this.insert.html(html, false)
50
+
51
+
52
+ insertImages: (objects) ->
53
+ if objects.length > 0
54
+ images = []
55
+ for asset in objects
56
+ images.push "<img src='#{ asset.file.url }' alt='#{ asset.name }' />"
57
+
58
+ html = images.join('<br>')
59
+
60
+ this.insert.html(html, false)
61
+
62
+
63
+ return methods
64
+
65
+
66
+
67
+
@@ -1,3 +1,9 @@
1
1
  #= require chr/loft/group-actions
2
2
  #= require chr/loft/asset-item
3
+ #= require chr/loft/type-item
3
4
  #= require chr/loft/module
5
+ #= require chr/redactor/loft
6
+ #= require chr/form/input-loft-image
7
+
8
+ # TODOs:
9
+ # - refactor group remove action
@@ -2,106 +2,243 @@
2
2
  // Styles for Loft Character CMS plugin, this should be included in
3
3
  // admin.scss (default)
4
4
  //
5
- // - mobile version
6
- //
7
5
 
8
- /* Tablet Layout */
9
- @media #{$tablet} {
10
- .module.assets {
11
- .list:not(:first-child) {
12
- .items {
13
- .item { min-height: 49px; }
14
- .item-title { display: inline-block; }
15
- .item-subtitle { display: inline; float: right; }
16
- .item.has-subtitle { padding: 1em; }
17
- }
18
- }
6
+ @import "chr/form/input-loft-image";
7
+
8
+ // MIXINS
9
+
10
+ @mixin iconBase($bgSize, $width, $height) {
11
+ background-image: image-url('loft/library@3x.png'); background-size: $bgSize;
12
+ display: block; width: $width; height: $height;
13
+ }
14
+
15
+ @mixin iconLabel($title) {
16
+ &:before {
17
+ display: block; margin-top: 2.6em;
18
+ content: $title; color: $contrastColor;
19
+ font-size: 1.5em; font-weight: 300; letter-spacing: 8px;
20
+ text-align: center; text-transform: uppercase;
19
21
  }
20
22
  }
21
23
 
22
24
 
23
- // Group Actions
25
+ // ASSET TYPE ICONS
26
+
27
+ .module.assets .list:first-child .items {
28
+ .item {
29
+ .item-title { margin-left: 2.05em; }
30
+ &:before { @include absolutePosition(inherit inherit inherit 1em); content: ''; display: block; }
31
+ }
32
+
33
+ .item {
34
+ &:before { @include iconBase(32px 112px, 16px, 16px); }
35
+ &.active:before { background-position: 16px 0px; }
36
+ }
37
+ .item:nth-child(2) {
38
+ &:before { background-position: 0px -16px; }
39
+ &.active { &:before { background-position: 16px -16px; } }
40
+ }
41
+ .item:nth-child(3) {
42
+ &:before { background-position: 0px -32px; }
43
+ &.active { &:before { background-position: 16px -32px; } }
44
+ }
45
+ .item:nth-child(4) {
46
+ &:before { background-position: 0px -48px; }
47
+ &.active { &:before { background-position: 16px -48px; } }
48
+ }
49
+ .item:nth-child(5) {
50
+ &:before { background-position: 0px -64px; }
51
+ &.active { &:before { background-position: 16px -64px; } }
52
+ }
53
+ .item:nth-child(6) {
54
+ &:before { background-position: 0px -80px; }
55
+ &.active { &:before { background-position: 16px -80px; } }
56
+ }
57
+ .item:nth-child(7) {
58
+ &:before { background-position: 0px -96px; }
59
+ &.active { &:before { background-position: 16px -96px; } }
60
+ }
61
+ }
62
+
63
+
64
+ // LIST MODE
65
+
66
+ .module.assets .list:not(:first-child) .items .item.asset {
67
+ padding-left: 6.25em; // 100px;
68
+
69
+ // checkbox
70
+ .asset-checkbox {
71
+ @include absolutePosition(1.3em inherit inherit 1em); z-index: 1;
72
+ input { @include absolutePosition(0px inherit inherit 0px); }
73
+ }
74
+
75
+ // type icon
76
+ .asset-icon {
77
+ @include iconBase(64px 224px, 32px, 32px);
78
+ @include absolutePosition(12px inherit inherit 3.05em);
79
+ }
80
+
81
+ // image thumbnail
82
+ &.asset-image .asset-icon {
83
+ @include absolutePosition(8px inherit inherit 2.8em);
84
+ overflow: hidden; width: 40px; height: 40px; border-radius: 20px;
85
+ img { height: 100%; }
86
+ }
87
+
88
+ &.asset-image .asset-icon { background-position: 0px -32px; }
89
+ &.asset-text .asset-icon { background-position: 0px -64px; }
90
+ &.asset-archive .asset-icon { background-position: 0px -96px; }
91
+ &.asset-audio .asset-icon { background-position: 0px -128px; }
92
+ &.asset-video .asset-icon { background-position: 0px -160px; }
93
+ &.asset-other .asset-icon { background-position: 0px -192px; }
94
+
95
+ .asset-thumnail-small { display: block; }
96
+ .asset-thumnail-medium { display: none; }
97
+
98
+ // name
99
+ .item-title { cursor: pointer; }
100
+ .asset-name {
101
+ display: none; z-index: 1;
102
+ @include absolutePosition(7px .5em inherit 94px);
103
+ input { width: 100%; padding: .1em .3em .3em .3em; @include noFocus();
104
+ border: 1px solid $stableColor; box-shadow: 0 0 10px $lightColor; border-radius: 3px; }
105
+ }
106
+ &.edit-name .asset-name { display: block; }
107
+ }
108
+
109
+
110
+ // LIST-GRID MODE SWITCHER
111
+
112
+ .assets-switch-mode { display: none; }
113
+
114
+
115
+ // GROUP ACTIONS
116
+
24
117
  .assets-group-actions {
25
118
  @include absolutePosition(0 0 0 0); background: $white;
26
- .unselect { @include headerButton($stableColor); float: left; margin-left: 1em; }
119
+ .accept { @include headerButton($positiveColor); float: right; margin-right: 1em; }
27
120
  .delete { @include headerButton($assertiveColor); float: right; margin-right: 1em; }
121
+ .unselect { @include headerButton($stableColor); float: left; margin-left: 1em; }
28
122
  }
29
123
 
124
+ .assets-group-actions .accept { display: none; }
125
+ .module-modal .assets-group-actions .accept { display: inline; }
126
+ .module-modal .assets-group-actions .delete { display: none; }
30
127
 
31
- // Icons for Parrent List
32
- .list.assets {
33
- .items .item {
34
- .item-title { margin-left: 2.05em; }
35
- &:before {
36
- content: '';
37
- position: absolute;
38
- display: block; width: 16px; height: 16px;
39
- margin-right: 1em;
40
- background-color: $lightColor;
41
- }
42
- }
43
- }
44
128
 
129
+ // UPLOAD BUTTON
45
130
 
46
- // Upload Button
47
131
  .asset-upload {
48
- @include absolutePosition(0 0 inherit inherit);
49
- @extend .icon-plus;
50
- cursor: pointer;
51
- right: -40px;
52
- padding-left: 80px;
53
- &:focus { outline: none; }
132
+ @include absolutePosition(0 -40px inherit inherit);
133
+ @extend .icon-plus; @include noFocus;
134
+ cursor: pointer; padding-left: 80px;
54
135
  }
55
136
 
56
- .list header {
57
- .asset-upload + .search { @include absolutePosition(0 40px inherit inherit); }
58
- }
137
+ .list header .asset-upload + .search { @include absolutePosition(0 40px inherit inherit); }
138
+ .list.list-search header .asset-upload + .search { @include absolutePosition(0 0 inherit 0); }
139
+ .module.assets.assets-uploading .list:not(:first-child) header .spinner { display: inline-block; }
59
140
 
60
- .list.list-search header {
61
- .asset-upload + .search { @include absolutePosition(0 0 inherit 0); }
62
- }
63
141
 
142
+ // MODAL MODE
64
143
 
65
- // Loader Spinner
66
- .module.assets.assets-uploading .list:not(:first-child) header .spinner { display: inline-block; }
144
+ .module.assets {
145
+ .modal-close {
146
+ display: none;
147
+ @include headerButton();
148
+ @include absolutePosition(null null null 1em );
149
+ }
150
+ &.module-modal {
151
+ @include absolutePosition(.5em .5em 0em .5em); z-index: 1100;
67
152
 
153
+ &:after { display: none; }
154
+
155
+ // dark background
156
+ &:before {
157
+ @include position(fixed, 0px 0px 0px 0px); z-index: 0;
158
+ content: ''; display: block; background: rgba(0,0,0,.25);
159
+ }
68
160
 
69
- // List Item
70
- .item.asset {
71
- .item-title {
72
- cursor: pointer;
73
- position: relative;
74
- &:hover { color: $positiveColor; text-decoration: underline; }
161
+ .modal-close { display: inline; }
162
+ .list:first-child .back { display: none }
75
163
  }
76
- .asset-name {
77
- display: none;
78
- width: 50%;
79
- position: absolute;
80
- line-height: 1;
81
- padding: 0em .3em .3em .375em;
82
- top: .55em; bottom: .55em; left: 4.5em;
83
- z-index: 1;
84
- border-radius: 3px;
85
- &:focus {
86
- outline: none !important;
87
- border: 1px solid $stableColor;
88
- box-shadow: 0 0 10px $lightColor;
164
+ }
165
+
166
+
167
+ /* TABLET LAYOUT */
168
+
169
+ @media #{ $tablet } {
170
+ .module.assets .list:not(:first-child) .items { font-size: 1em; }
171
+
172
+ // ITEM
173
+ .module.assets .list:not(:first-child) .items .item.asset {
174
+ padding-top: 19px; padding-bottom: 19px;
175
+ .item-title {
176
+ display: inline; cursor: pointer;
177
+ &:hover { color: $positiveColor; text-decoration: underline; }
89
178
  }
179
+ .item-subtitle { @include absolutePosition(1.55em 1em inherit inherit); }
180
+ .asset-name { top: 16px; }
90
181
  }
91
- .asset-checkbox {
92
- position: relative; top: -.25em;
93
- margin-right: 1em;
182
+
183
+ // MODAL MODE
184
+ .module.assets.module-modal { @include absolutePosition(1.5em 1.5em 0em 1.5em) }
185
+
186
+
187
+ // GRID MODE
188
+ .assets-switch-mode {
189
+ @include absolutePosition(inherit inherit inherit 1em);
190
+ display: inline;
191
+ font-size: .9em;
192
+ &:before { content: 'Grid'; color: $lightColor; margin-right: .25em; }
193
+ &:after { content: 'List'; color: $stableColor; }
194
+ }
195
+ .module.assets.grid-mode .assets-switch-mode {
196
+ &:before { color: $stableColor; }
197
+ &:after { color: $lightColor; }
94
198
  }
95
- .asset-icon {
96
- display: inline-block; width: 16px; height: 16px;
97
- margin-right: 1em;
98
- background-color: $lightColor; }
99
199
 
100
- &.edit-name {
101
- .asset-name { display: block; }
200
+ .module.assets.grid-mode {
201
+ .list:not(:first-child) .items {
202
+ padding: .25em .25em 5em .75em;
203
+ .item.asset { margin: 0 .75em 1.5em; float: left; display: inline-block; }
204
+
205
+ .item.asset {
206
+ padding-left: 1em; width: 222px; height: 222px;
207
+ @include noBorder(); border: 1px solid $contrastColor; border-radius: 4px;
208
+
209
+ // checkbox: position + decoration
210
+ .asset-checkbox {
211
+ top: 10px; left: 10px; width: 24px; height: 24px; background: $white;
212
+ box-shadow: 0 1px 0 rgba(0,0,0,0.05),1px 0 0 rgba(0,0,0,0.05); border-bottom-right-radius: 4px;
213
+ }
214
+
215
+ // thumbnail
216
+ .asset-icon {
217
+ @include absolutePosition(10px inherit inherit 10px);
218
+ width: 200px; height: 150px; background: $white; border-radius: 0;
219
+ &:after { @include absolutePosition(0px 0px 0px 0px); box-shadow: 0 0 1px rgba(0,0,0,0.2) inset; content: ''; display: block; }
220
+ }
221
+
222
+ .asset-thumbnail-small { display: none; }
223
+ .asset-thumbnail-medium { display: block; }
224
+
225
+ // name & subtitle
226
+ .item-title { display: block; margin-top: 9.45em; }
227
+ .item-subtitle { display: block; position: initial; margin-top: .3em; }
228
+ &.edit-name .asset-name { top: inherit; bottom: 27px; left: 10px; right: 10px; }
229
+ }
230
+
231
+ // asset type labels
232
+ .asset-text .asset-icon { @include iconLabel('Text'); }
233
+ .asset-archive .asset-icon { @include iconLabel('Archive'); }
234
+ .asset-audio .asset-icon { @include iconLabel('Audio'); }
235
+ .asset-video .asset-icon { @include iconLabel('Video'); }
236
+ .asset-other .asset-icon { @include iconLabel('File'); }
237
+ }
102
238
  }
103
239
  }
104
240
 
105
241
 
106
242
 
107
243
 
244
+
@@ -0,0 +1,11 @@
1
+ .input-loft-image {
2
+ &.has-value { min-height: 11em; }
3
+
4
+ input { margin-bottom: .25em; }
5
+ .image img { max-height: 6em; margin: .25em .75em .25em 0; float: left; }
6
+ .choose, .remove { color: $positiveColor; display: inline-block; }
7
+
8
+ &.has-value .choose { margin-top: .25em; }
9
+ &.has-value .remove { display: inline-block; }
10
+ .remove { display: none; font-size: .8em; margin-top: .5em; }
11
+ }
@@ -6,11 +6,15 @@ module Loft
6
6
  include CarrierWave::MiniMagick
7
7
 
8
8
  def store_dir
9
- "assets/loft/#{ model._number }"
9
+ "loft/#{ model._number }"
10
10
  end
11
11
 
12
- version :small_150, if: :is_image? do
13
- process :resize_to_fill => [150, 150]
12
+ version :_200x150_2x, if: :is_image? do
13
+ process :resize_to_fill => [400, 300]
14
+ end
15
+
16
+ version :_40x40_2x, if: :is_image? do
17
+ process :resize_to_fill => [80, 80]
14
18
  end
15
19
 
16
20
  def is_image? new_file
@@ -3,5 +3,5 @@ require 'carrierwave/mongoid'
3
3
  module Loft
4
4
  require 'loft/engine'
5
5
  require 'mongoid/loft_asset'
6
- require 'uploaders/asset_file_uploader'
6
+ require 'concerns/asset_file_uploader'
7
7
  end
@@ -1,3 +1,3 @@
1
1
  module Loft
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -26,7 +26,7 @@ module Mongoid
26
26
 
27
27
  # increment value, used by uploader
28
28
  field :_number, type: Integer
29
- increments :number
29
+ increments :_number
30
30
 
31
31
  # uploaders
32
32
  mount_uploader :file, AssetFileUploader
@@ -54,11 +54,11 @@ module Mongoid
54
54
  end
55
55
 
56
56
 
57
- def list_item_thumbnail
57
+ def item_thumbnail
58
58
  if is_image? and file?
59
- file.small_150.url
59
+ { medium: file._200x150_2x.url, small: file._40x40_2x.url }
60
60
  else
61
- ''
61
+ {}
62
62
  end
63
63
  end
64
64
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loft
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Kravets
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-24 00:00:00.000000000 Z
11
+ date: 2015-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid_search
@@ -88,22 +88,28 @@ executables: []
88
88
  extensions: []
89
89
  extra_rdoc_files: []
90
90
  files:
91
+ - ".gitignore"
91
92
  - CONTRIBUTING.md
92
93
  - Gemfile
93
94
  - LICENSE.md
94
95
  - README.md
95
96
  - Rakefile
97
+ - app/assets/images/loft/library@3x.png
98
+ - app/assets/javascripts/chr/form/input-loft-image.coffee
96
99
  - app/assets/javascripts/chr/loft/asset-item.coffee
97
100
  - app/assets/javascripts/chr/loft/group-actions.coffee
98
101
  - app/assets/javascripts/chr/loft/module.coffee
102
+ - app/assets/javascripts/chr/loft/type-item.coffee
103
+ - app/assets/javascripts/chr/redactor/loft.coffee
99
104
  - app/assets/javascripts/loft.coffee
100
105
  - app/assets/stylesheets/_loft.scss
106
+ - app/assets/stylesheets/chr/form/_input-loft-image.scss
101
107
  - app/uploaders/asset_file_uploader.rb
108
+ - lib/concerns/asset_file_uploader.rb
102
109
  - lib/loft.rb
103
110
  - lib/loft/engine.rb
104
111
  - lib/loft/version.rb
105
112
  - lib/mongoid/loft_asset.rb
106
- - lib/uploaders/asset_file_uploader.rb
107
113
  - loft.gemspec
108
114
  homepage: http://slatestudio.com
109
115
  licenses: