loft 0.1.0 → 0.1.1

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.
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: