, returns whether the ControlPanelLayer is currently hidden)
73 |
74 | # Known issues
75 |
76 | # Creating multiple ControlPanelLayers with different scale factors will result in unexpected input field effects.
77 | ###
78 |
79 | defaults =
80 | specs: {}
81 | scaleFactor: 1
82 | draggable: true
83 | textColor: "white"
84 | inputBackgroundColor: "rgba(255,255,255,0.8)"
85 | inputTextColor: "black"
86 | backgroundColor: "rgba(0,0,0,0.5)"
87 | buttonTextColor: "black"
88 | buttonColor: "white"
89 | width: 350
90 | showGuides: false
91 | hidden: false
92 | commitAction: () ->
93 | closeAction: () ->
94 |
95 | class ControlPanelLayer extends Layer
96 | constructor: (@options={}) ->
97 | @options = _.assign({}, defaults, @options)
98 | super @options
99 |
100 | rowHeight = 32 * @options.scaleFactor
101 | panelTopMargin = 15 * @options.scaleFactor
102 | panelBottomMargin = 15 * @options.scaleFactor
103 | panelSideMargin = 30 * @options.scaleFactor
104 | panelButtonMargin = 15 * @options.scaleFactor
105 | panelRowHeight = 34 * @options.scaleFactor
106 | minimumPanelHeight = 2 * panelRowHeight + panelTopMargin + panelBottomMargin
107 | panelLabelSize = 16 * @options.scaleFactor
108 | panelLabelMargin = 6 * @options.scaleFactor
109 | panelTipSize = 12 * @options.scaleFactor
110 | panelTipMargin = -10 * @options.scaleFactor
111 | radioButtonSize = 20 * @options.scaleFactor
112 | radioButtonMarkSize = 12 * @options.scaleFactor
113 | radioButtonTopMargin = 6 * @options.scaleFactor
114 | inputWidth = 50 * @options.scaleFactor
115 | inputTopMargin = panelLabelSize/4
116 | inputTopOffet = if @options.scaleFactor == 1 then -3 else 0
117 | svgTopOffset = if @options.scaleFactor == 1 then 2 else 0
118 | commitButtonHeight = 30 * @options.scaleFactor
119 | commitButtonTopMargin = 5 * @options.scaleFactor
120 | codeVariableColor = "#ed6a43"
121 | codeBracketColor = "#a71d5d"
122 | codeBodyColor = if @options.buttonTextColor == "black" then "#24292e" else @options.buttonTextColor
123 | closeButtonSize = 24 * @options.scaleFactor
124 | closeButtonMargin = 8 * @options.scaleFactor
125 | closeButtonGlyphMargin = 4 * @options.scaleFactor
126 | closeGlyphHeight = 2 * @options.scaleFactor
127 | closeGlyphWidth = closeButtonSize - closeButtonGlyphMargin * 2
128 | closeGlyphTop = closeButtonSize/2 - 1 * @options.scaleFactor
129 | closeGlyphRotationX = closeButtonSize/2
130 | closeGlyphRotationY = closeButtonSize/2
131 | inputInsetShadowColor = new Color(@options.inputBackgroundColor).darken(30)
132 | alertString = "Add specs with specs: <mySpecs>
"
133 | commitString = "Commit
"
134 |
135 | rowCount = Object.keys(@options.specs).length + 1 # allow for Commit button
136 | rows = []
137 | @.name = "controlPanel"
138 | @.width = @options.width * @options.scaleFactor
139 | @.height = panelRowHeight * rowCount + panelTopMargin + panelBottomMargin
140 | @.borderRadius = 10 * @options.scaleFactor
141 | @.shadowBlur = 20 * @options.scaleFactor
142 | @.shadowColor = "rgba(0,0,0,0.3)"
143 | @.backgroundColor = @options.backgroundColor
144 | @.draggable = @options.draggable
145 | @.draggable.momentum = false
146 |
147 | labelWidth = @.width - 125 * @options.scaleFactor
148 |
149 | inputCSS = """
150 | input[type='text'] {
151 | color: #{@options.inputTextColor};
152 | background-color: #{@options.inputBackgroundColor};
153 | font-family: -apple-system, Helvetica, Arial, sans-serif;
154 | font-weight: 500;
155 | text-align: right;
156 | font-size: #{panelLabelSize}px;
157 | margin-top: #{inputTopMargin}px;
158 | padding: #{panelLabelSize/8}px;
159 | appearance: none;
160 | width: #{inputWidth - panelLabelSize/8}px;
161 | box-shadow: inset 0px 1px 2px 0 #{inputInsetShadowColor};
162 | border-radius: #{3 * @options.scaleFactor}px;
163 | position: relative;
164 | top: #{inputTopOffet}px;
165 | }"""
166 |
167 | Utils.insertCSS(inputCSS)
168 |
169 | keyIndex = 0
170 | for row of @options.specs
171 | do (row) =>
172 | tipRequired = if @options.specs[row].tip != "" and @options.specs[row].tip != undefined then true else false
173 |
174 | rowBlock = new Layer
175 | name: "row" + keyIndex
176 | parent: @
177 | x: panelSideMargin
178 | y: if keyIndex > 0 then rows[keyIndex - 1].maxY else panelTopMargin + keyIndex * panelRowHeight
179 | height: if tipRequired then panelRowHeight * 1.5 else panelRowHeight
180 | width: labelWidth
181 | backgroundColor: "clear"
182 |
183 | rows.push(rowBlock)
184 |
185 | label = new Layer
186 | name: _.camelCase(@options.specs[row].label + "Label")
187 | parent: rowBlock
188 | height: panelRowHeight
189 | width: labelWidth
190 | y: panelLabelMargin
191 | backgroundColor: "clear"
192 | html: "#{@options.specs[row].label}
"
193 | style:
194 | "font-size": panelLabelSize + "px"
195 | "font-weight": "500"
196 | "text-align": "right"
197 | "color": @options.textColor
198 |
199 | @[label.name] = label
200 |
201 | if tipRequired
202 | tip = new Layer
203 | name: _.camelCase(@options.specs[row].label + "Tip")
204 | parent: rowBlock
205 | height: panelRowHeight * 0.4
206 | width: labelWidth
207 | y: label.maxY + panelTipMargin
208 | backgroundColor: "clear"
209 | html: "#{@options.specs[row].tip}
"
210 | style:
211 | "font-size": panelTipSize + "px"
212 | "font-weight": "500"
213 | "text-align": "right"
214 | "color": @options.textColor
215 |
216 | @[tip.name] = tip
217 |
218 | idString = _.camelCase(@options.specs[row].label + "Input")
219 | switch typeof @options.specs[row].value
220 | when "boolean"
221 | input = new Layer
222 | name: idString
223 | parent: rowBlock
224 | x: Align.right(inputWidth)
225 | y: Align.top(radioButtonTopMargin)
226 | height: radioButtonSize
227 | width: radioButtonSize
228 | borderRadius: radioButtonSize/2
229 | borderColor: @options.textColor
230 | borderWidth: 1 * @options.scaleFactor
231 | backgroundColor: "clear"
232 |
233 | inputMark = new Layer
234 | name: "mark"
235 | parent: input
236 | width: radioButtonMarkSize
237 | height: radioButtonMarkSize
238 | x: (radioButtonSize-radioButtonMarkSize)/2
239 | y: (radioButtonSize-radioButtonMarkSize)/2
240 | borderRadius: radioButtonMarkSize/2
241 | backgroundColor: @options.textColor
242 | visible: @options.specs[row].value
243 |
244 | input.mark = inputMark
245 | input.row = row
246 |
247 | input.onClick =>
248 | if @options.specs[input.row].value == true
249 | @options.specs[input.row].value = false
250 | input.mark.visible = false
251 | else
252 | @options.specs[input.row].value = true
253 | input.mark.visible = true
254 |
255 | else
256 | input = new Layer
257 | name: idString
258 | parent: rowBlock
259 | x: Align.right((@.width - labelWidth)/2)
260 | y: Align.top
261 | color: @options.textColor
262 | html: ""
263 | height: panelRowHeight
264 | width: inputWidth
265 | backgroundColor: "clear"
266 |
267 | @[input.name] = input
268 |
269 | if @options.showGuides == true
270 | guide = new Layer
271 | name: "guide"
272 | parent: rowBlock
273 | width: @.width
274 | x: -panelSideMargin
275 | backgroundColor: "red"
276 | height: 1
277 | y: panelLabelSize * 1.3
278 | opacity: 0.5
279 |
280 | ++keyIndex
281 |
282 |
283 | @.height = Math.max(minimumPanelHeight, @.contentFrame().height + panelTopMargin + panelBottomMargin + commitButtonHeight + commitButtonTopMargin)
284 |
285 | closeButton = new Layer
286 | name: "closeButton"
287 | parent: @
288 | width: closeButtonSize
289 | height: closeButtonSize
290 | borderRadius: closeButtonSize/2
291 | backgroundColor: "rgba(0,0,0,0.15)"
292 | borderWidth: 1 * @options.scaleFactor
293 | borderColor: "rgba(255,255,255,0.5)"
294 | x: closeButtonMargin
295 | y: closeButtonMargin
296 |
297 | @.closeButton = closeButton
298 |
299 | closeButton.onClick =>
300 | @.hide()
301 | @options.closeAction()
302 |
303 | closeButtonStroke1 = new Layer
304 | name: "closeButtonStroke1"
305 | parent: closeButton
306 | width: closeGlyphWidth
307 | height: closeGlyphHeight
308 | x: (closeButtonSize-closeGlyphWidth)/2
309 | y: (closeButtonSize-closeGlyphHeight)/2
310 | rotation: 45
311 | borderRadius: closeGlyphHeight/2
312 | backgroundColor: "white"
313 |
314 | closeButtonStroke2 = new Layer
315 | name: "closeButtonStroke2"
316 | parent: closeButton
317 | width: closeGlyphWidth
318 | height: closeGlyphHeight
319 | x: (closeButtonSize-closeGlyphWidth)/2
320 | y: (closeButtonSize-closeGlyphHeight)/2
321 | rotation: -45
322 | borderRadius: closeGlyphHeight/2
323 | backgroundColor: "white"
324 |
325 | commitButton = new Layer
326 | name: "commitButton"
327 | parent: @
328 | width: @.width - panelButtonMargin * 2
329 | height: commitButtonHeight
330 | x: Align.center
331 | y: Align.bottom(- panelBottomMargin)
332 | backgroundColor: @options.buttonColor
333 | html: if Object.keys(@options.specs).length == 0 then alertString else commitString
334 | borderRadius: 5 * @options.scaleFactor
335 |
336 | @.commitButton = commitButton
337 |
338 | commitButton.onClick =>
339 | for row of @options.specs
340 | do(row) =>
341 | idString = _.camelCase(@options.specs[row].label + "Input")
342 | switch typeof @options.specs[row].value
343 | when "string"
344 | typedValue = document.getElementById(idString).value
345 | when "number"
346 | typedValue = +document.getElementById(idString).value
347 | when "boolean"
348 | typedValue = @options.specs[row].value
349 | else
350 | typedValue = document.getElementById(idString).value
351 | @options.specs[row].value = typedValue
352 | @options.commitAction()
353 |
354 | if @options.hidden == true
355 | @.hide()
356 |
357 | hide: () =>
358 | @options.hidden = true
359 | @.animate
360 | properties:
361 | opacity: 0
362 | time:
363 | 0.25
364 | Utils.delay 0.25, =>
365 | @.visible = false
366 |
367 | show: () =>
368 | @.visible = true
369 | @options.hidden = false
370 | @.animate
371 | properties:
372 | opacity: 1
373 | time:
374 | 0.25
375 |
376 | @define 'specs', get: () -> @options.specs
377 | @define 'hidden', get: () -> @options.hidden
378 | module.exports = ControlPanelLayer
379 |
--------------------------------------------------------------------------------
/example.framer/modules/ControlPanelLayer.coffee:
--------------------------------------------------------------------------------
1 | ###
2 | # USING THE CONTROLPANELLAYER MODULE
3 |
4 | # Require the module
5 | ControlPanelLayer = require "ControlPanelLayer"
6 |
7 | myControlPanel = new ControlPanelLayer
8 | scaleFactor:
9 | specs: