|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 47 KiB |
@ -0,0 +1,77 @@ |
|||||
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> |
||||
|
|
||||
|
## getId |
||||
|
|
||||
|
Get layer id. |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
## getIndex |
||||
|
|
||||
|
Get layer index. |
||||
|
|
||||
|
Returns **[Number][2]** |
||||
|
|
||||
|
## getValues |
||||
|
|
||||
|
Get layer values. |
||||
|
|
||||
|
### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.camelCase` **[Boolean][4]?** Return property names in camelCase. |
||||
|
|
||||
|
Returns **[Object][3]** |
||||
|
|
||||
|
## getLabel |
||||
|
|
||||
|
Get layer label. |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
## isSelected |
||||
|
|
||||
|
Check if the layer is selected. |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
## select |
||||
|
|
||||
|
Select the layer. |
||||
|
|
||||
|
## remove |
||||
|
|
||||
|
Remove the layer. |
||||
|
|
||||
|
## move |
||||
|
|
||||
|
Move layer to a new index. |
||||
|
|
||||
|
### Parameters |
||||
|
|
||||
|
* `index` **[Number][2]** New index |
||||
|
|
||||
|
## getStylePreview |
||||
|
|
||||
|
Get style object for the preview. |
||||
|
|
||||
|
### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options. Same of `PropertyStack.getStyleFromLayer` (optional, default `{}`) |
||||
|
|
||||
|
Returns **[Object][3]** Style object |
||||
|
|
||||
|
## hasPreview |
||||
|
|
||||
|
Check if the property has the preview enabled for this layer. |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String |
||||
|
|
||||
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number |
||||
|
|
||||
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object |
||||
|
|
||||
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean |
||||
@ -0,0 +1,164 @@ |
|||||
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> |
||||
|
|
||||
|
## Property |
||||
|
|
||||
|
|
||||
|
|
||||
|
### Properties |
||||
|
|
||||
|
* `id` **[String][1]** Property id, eg. `my-property-id`. |
||||
|
* `property` **[String][1]** Related CSS property name, eg. `text-align`. |
||||
|
* `default` **[String][1]** Defaul value of the property. |
||||
|
* `label` **[String][1]** Label to use in UI, eg. `Text Align`. |
||||
|
* `onChange` **[Function][2]?** Change callback. |
||||
|
```js |
||||
|
onChange: ({ property, from, to }) => { |
||||
|
console.log(`Changed property`, property.getName(), { from, to }); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### getId |
||||
|
|
||||
|
Get property id. |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
### getType |
||||
|
|
||||
|
Get the property type. |
||||
|
The type of the property is defined on property creation and based on its value the proper Property class is assigned. |
||||
|
The default type is `base`. |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
### getName |
||||
|
|
||||
|
Get name (the CSS property name). |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
### getLabel |
||||
|
|
||||
|
Get property label. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.locale` **[Boolean][4]** Use the locale string from i18n module (optional, default `true`) |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
### getValue |
||||
|
|
||||
|
Get property value. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.noDefault` **[Boolean][4]** Avoid returning the default value (optional, default `false`) |
||||
|
|
||||
|
Returns **[String][1]** |
||||
|
|
||||
|
### hasValue |
||||
|
|
||||
|
Check if the property has value. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.noParent` **[Boolean][4]** Ignore the value if it comes from the parent target. (optional, default `false`) |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
### hasValueParent |
||||
|
|
||||
|
Indicates if the current value is coming from a parent target (eg. another CSSRule). |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
### getStyle |
||||
|
|
||||
|
Get the CSS style object of the property. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.camelCase` **[Boolean][4]?** Return property name in camelCase. |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
// In case the property is `color` with a value of `red`. |
||||
|
console.log(property.getStyle()); |
||||
|
// { color: 'red' }; |
||||
|
``` |
||||
|
|
||||
|
Returns **[Object][3]** |
||||
|
|
||||
|
### getDefaultValue |
||||
|
|
||||
|
Get the default value. |
||||
|
|
||||
|
Returns **[string][1]** |
||||
|
|
||||
|
### upValue |
||||
|
|
||||
|
Update the value. |
||||
|
The change is also propagated to the selected targets (eg. CSS rule). |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `value` **[String][1]** New value |
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.partial` **[Boolean][4]** If `true` the update on targets won't be considered complete (not stored in UndoManager) (optional, default `false`) |
||||
|
* `opts.noTarget` **[Boolean][4]** If `true` the change won't be propagated to selected targets. (optional, default `false`) |
||||
|
|
||||
|
### isVisible |
||||
|
|
||||
|
Check if the property is visible |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
### clear |
||||
|
|
||||
|
Clear the value. |
||||
|
The change is also propagated to the selected targets (eg. the css property is cleared). |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `opts` **[Object][3]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.noTarget` **[Boolean][4]** If `true` the change won't be propagated to selected targets. (optional, default `false`) |
||||
|
|
||||
|
### canClear |
||||
|
|
||||
|
Indicates if the current value comes directly from the selected target and so can be cleared. |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
### getParent |
||||
|
|
||||
|
If the current property is a sub-property, this will return the parent Property. |
||||
|
|
||||
|
Returns **([[Property][5]] | null)** |
||||
|
|
||||
|
### isFull |
||||
|
|
||||
|
Indicates if the property is full-width in UI. |
||||
|
|
||||
|
Returns **[Boolean][4]** |
||||
|
|
||||
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String |
||||
|
|
||||
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function |
||||
|
|
||||
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object |
||||
|
|
||||
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean |
||||
|
|
||||
|
[5]: #property |
||||
@ -0,0 +1,113 @@ |
|||||
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> |
||||
|
|
||||
|
## PropertyComposite |
||||
|
|
||||
|
**Extends Property** |
||||
|
|
||||
|
[Property]: property.html |
||||
|
|
||||
|
### Properties |
||||
|
|
||||
|
* `properties` **[Array][1]<[Object][2]>** Array of sub properties, eg. `[{ type: 'number', property: 'margin-top' }, ...]` |
||||
|
* `detached` **[Boolean][3]?** Indicate if the final CSS property is splitted (detached: `margin-top: X; margin-right: Y; ...`) or combined (not detached: `margin: X Y ...;`) |
||||
|
* `separator` **([String][4] | [RegExp][5])?** Value used to split property values, default `" "`. |
||||
|
* `join` **[String][4]?** Value used to join property values, default `" "`. |
||||
|
* `fromStyle` **[Function][6]?** Custom logic for getting property values from the target style object. |
||||
|
```js |
||||
|
fromStyle: (style) => { |
||||
|
const margins = parseMarginShorthand(style.margin); |
||||
|
return { |
||||
|
'margin-top': margins.top, |
||||
|
// ... |
||||
|
}; |
||||
|
} |
||||
|
``` |
||||
|
* `toStyle` **[Function][6]?** Custom logic for creating the CSS style object to apply on selected targets. |
||||
|
```js |
||||
|
toStyle: (values) => { |
||||
|
const top = values['margin-top'] || 0; |
||||
|
const right = values['margin-right'] || 0; |
||||
|
// ... |
||||
|
return { |
||||
|
margin: `${top} ${right} ...`, |
||||
|
}; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### getProperties |
||||
|
|
||||
|
Get properties. |
||||
|
|
||||
|
Returns **[Array][1]<[Property]>** |
||||
|
|
||||
|
### getProperty |
||||
|
|
||||
|
Get property by id. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `id` **[String][4]** Property id. |
||||
|
|
||||
|
Returns **([Property] | null)** |
||||
|
|
||||
|
### getPropertyAt |
||||
|
|
||||
|
Get property at index. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `index` **[Number][7]** |
||||
|
|
||||
|
Returns **([Property] | null)** |
||||
|
|
||||
|
### isDetached |
||||
|
|
||||
|
Check if the property is detached. |
||||
|
|
||||
|
Returns **[Boolean][3]** |
||||
|
|
||||
|
### getValues |
||||
|
|
||||
|
Get current values of properties. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `opts` **[Object][2]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.byName` **[Boolean][3]** Use property names as a key instead of the id. (optional, default `false`) |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
// In case the property is `margin` with sub properties like `margin-top`, `margin-right`, etc. |
||||
|
console.log(property.getValues()); |
||||
|
// { 'margin-top': '10px', 'margin-right': '20px', ... }; |
||||
|
``` |
||||
|
|
||||
|
Returns **[Object][2]** |
||||
|
|
||||
|
### getSeparator |
||||
|
|
||||
|
Get property separator. |
||||
|
|
||||
|
Returns **[RegExp][5]** |
||||
|
|
||||
|
### getJoin |
||||
|
|
||||
|
Get the join value. |
||||
|
|
||||
|
Returns **[String][4]** |
||||
|
|
||||
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array |
||||
|
|
||||
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object |
||||
|
|
||||
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean |
||||
|
|
||||
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String |
||||
|
|
||||
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp |
||||
|
|
||||
|
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function |
||||
|
|
||||
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number |
||||
@ -0,0 +1,66 @@ |
|||||
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> |
||||
|
|
||||
|
## PropertyNumber |
||||
|
|
||||
|
**Extends Property** |
||||
|
|
||||
|
### Properties |
||||
|
|
||||
|
* `units` **[Array][1]<[String][2]>** Array of units, eg. `['px', '%']` |
||||
|
* `min` **[Number][3]** Minimum value. |
||||
|
* `max` **[Number][3]** Maximum value. |
||||
|
* `step` **[Number][3]** Step value. |
||||
|
|
||||
|
### getUnits |
||||
|
|
||||
|
Get property units. |
||||
|
|
||||
|
Returns **[Array][1]<[String][2]>** |
||||
|
|
||||
|
### getUnit |
||||
|
|
||||
|
Get property unit value. |
||||
|
|
||||
|
Returns **[String][2]** |
||||
|
|
||||
|
### getMin |
||||
|
|
||||
|
Get min value. |
||||
|
|
||||
|
Returns **[Number][3]** |
||||
|
|
||||
|
### getMax |
||||
|
|
||||
|
Get max value. |
||||
|
|
||||
|
Returns **[Number][3]** |
||||
|
|
||||
|
### getStep |
||||
|
|
||||
|
Get step value. |
||||
|
|
||||
|
Returns **[Number][3]** |
||||
|
|
||||
|
### upUnit |
||||
|
|
||||
|
Update property unit value. |
||||
|
The change is also propagated to the selected targets. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `unit` **[String][2]** New unit value |
||||
|
* `opts` **[Object][4]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.noTarget` **[Boolean][5]** If `true` the change won't be propagated to selected targets. (optional, default `false`) |
||||
|
|
||||
|
Returns **[String][2]** |
||||
|
|
||||
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array |
||||
|
|
||||
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String |
||||
|
|
||||
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number |
||||
|
|
||||
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object |
||||
|
|
||||
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean |
||||
@ -0,0 +1,78 @@ |
|||||
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> |
||||
|
|
||||
|
## PropertySelect |
||||
|
|
||||
|
**Extends Property** |
||||
|
|
||||
|
### Properties |
||||
|
|
||||
|
* `options` **[Array][1]<[Object][2]>** Array of option definitions. |
||||
|
```js |
||||
|
options: [ |
||||
|
{ id: '100', label: 'Set 100' }, |
||||
|
{ id: '200', label: 'Set 200' }, |
||||
|
] |
||||
|
``` |
||||
|
|
||||
|
### getOptions |
||||
|
|
||||
|
Get available options. |
||||
|
|
||||
|
Returns **[Array][1]<[Object][2]>** Array of options |
||||
|
|
||||
|
### getOption |
||||
|
|
||||
|
Get current selected option or by id. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `id` **[String][3]?** Option id. |
||||
|
|
||||
|
Returns **([Object][2] | null)** |
||||
|
|
||||
|
### setOptions |
||||
|
|
||||
|
Update options. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `value` **[Array][1]<[Object][2]>** New array of options, eg. `[{ id: 'val-1', label: 'Value 1' }]` (optional, default `[]`) |
||||
|
|
||||
|
### addOption |
||||
|
|
||||
|
Add new option. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `value` **[Object][2]** Option object, eg. `{ id: 'val-1', label: 'Value 1' }` |
||||
|
|
||||
|
### getOptionId |
||||
|
|
||||
|
Get the option id from the option object. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `option` **[Object][2]** Option object |
||||
|
|
||||
|
Returns **[String][3]** Option id |
||||
|
|
||||
|
### getOptionLabel |
||||
|
|
||||
|
Get option label. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `id` **([String][3] | [Object][2])** Option id or the option object |
||||
|
* `opts` **[Object][2]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.locale` **[Boolean][4]** Use the locale string from i18n module (optional, default `true`) |
||||
|
|
||||
|
Returns **[String][3]** Option label |
||||
|
|
||||
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array |
||||
|
|
||||
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object |
||||
|
|
||||
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String |
||||
|
|
||||
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean |
||||
@ -0,0 +1,214 @@ |
|||||
|
<!-- Generated by documentation.js. Update this documentation by updating the source code. --> |
||||
|
|
||||
|
## PropertyStack |
||||
|
|
||||
|
**Extends PropertyComposite** |
||||
|
|
||||
|
[Layer]: layer.html |
||||
|
|
||||
|
### Properties |
||||
|
|
||||
|
* `preview` **[Boolean][1]?** Indicate if the layer should display a preview. |
||||
|
* `layerSeparator` **([String][2] | [RegExp][3])?** The separator used to split layer values. |
||||
|
* `layerJoin` **[String][2]?** Value used to join layer values. |
||||
|
* `layerLabel` **[Function][4]?** Custom logic for creating layer labels. |
||||
|
```js |
||||
|
layerLabel: (layer) => { |
||||
|
const values = layer.getValues(); |
||||
|
return `A: ${values['prop-a']} B: ${values['prop-b']}`; |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### getLayers |
||||
|
|
||||
|
Get all available layers. |
||||
|
|
||||
|
Returns **[Array][5]<[Layer]>** |
||||
|
|
||||
|
### getLayer |
||||
|
|
||||
|
Get layer by index. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `index` **[Number][6]** Layer index position. (optional, default `0`) |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
// Get the first layer |
||||
|
const layerFirst = property.getLayer(0); |
||||
|
// Get the last layer |
||||
|
const layers = this.getLayers(); |
||||
|
const layerLast = property.getLayer(layers.length - 1); |
||||
|
``` |
||||
|
|
||||
|
Returns **([Layer] | null)** |
||||
|
|
||||
|
### getSelectedLayer |
||||
|
|
||||
|
Get selected layer. |
||||
|
|
||||
|
Returns **([Layer] | null)** |
||||
|
|
||||
|
### selectLayer |
||||
|
|
||||
|
Select layer. |
||||
|
Without a selected layer any update made on inner properties has no effect. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `layer` **[Layer]** Layer to select |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
const layer = property.getLayer(0); |
||||
|
property.selectLayer(layer); |
||||
|
``` |
||||
|
|
||||
|
### selectLayerAt |
||||
|
|
||||
|
Select layer by index. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `index` **[Number][6]** Index of the layer to select. (optional, default `0`) |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
property.selectLayerAt(1); |
||||
|
``` |
||||
|
|
||||
|
### moveLayer |
||||
|
|
||||
|
Move layer by index. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `layer` **[Layer]** Layer to move. |
||||
|
* `index` **[Number][6]** New layer index. (optional, default `0`) |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
const layer = property.getLayer(1); |
||||
|
property.moveLayer(layer, 0); |
||||
|
``` |
||||
|
|
||||
|
### addLayer |
||||
|
|
||||
|
Add new layer to the stack. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `props` **[Object][7]** Custom property values to use in a new layer. (optional, default `{}`) |
||||
|
* `opts` **[Object][7]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.at` **[Number][6]?** Position index (by default the layer will be appended at the end). |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
// Add new layer at the beginning of the stack with custom values |
||||
|
property.addLayer({ 'sub-prop1': 'value1', 'sub-prop2': 'value2' }, { at: 0 }); |
||||
|
``` |
||||
|
|
||||
|
Returns **[Layer]** Added layer. |
||||
|
|
||||
|
### removeLayer |
||||
|
|
||||
|
Remove layer. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `layer` **[Layer]** Layer to remove. |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
const layer = property.getLayer(0); |
||||
|
property.removeLayer(layer); |
||||
|
``` |
||||
|
|
||||
|
Returns **[Layer]** Removed layer |
||||
|
|
||||
|
### removeLayerAt |
||||
|
|
||||
|
Remove layer by index. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `index` **[Number][6]** Index of the layer to remove (optional, default `0`) |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
property.removeLayerAt(0); |
||||
|
``` |
||||
|
|
||||
|
Returns **([Layer] | null)** Removed layer |
||||
|
|
||||
|
### getLayerLabel |
||||
|
|
||||
|
Get the layer label. The label can be customized with the `layerLabel` property. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `layer` **[Layer]** |
||||
|
|
||||
|
#### Examples |
||||
|
|
||||
|
```javascript |
||||
|
const layer = this.getLayer(1); |
||||
|
const label = this.getLayerLabel(layer); |
||||
|
``` |
||||
|
|
||||
|
Returns **[String][2]** |
||||
|
|
||||
|
### getStyleFromLayer |
||||
|
|
||||
|
Get style object from the layer. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `layer` **[Layer]** |
||||
|
* `opts` **[Object][7]** Options (optional, default `{}`) |
||||
|
|
||||
|
* `opts.camelCase` **[Boolean][1]?** Return property names in camelCase. |
||||
|
* `opts.number` **[Object][7]?** Limit the result of the number types, eg. `number: { min: -3, max: 3 }` |
||||
|
|
||||
|
Returns **[Object][7]** Style object |
||||
|
|
||||
|
### getStylePreview |
||||
|
|
||||
|
Get preview style object from the layer. |
||||
|
If the property has `preview: false` the returned object will be empty. |
||||
|
|
||||
|
#### Parameters |
||||
|
|
||||
|
* `layer` **[Layer]** |
||||
|
* `opts` **[Object][7]** Options. Same of `getStyleFromLayer` (optional, default `{}`) |
||||
|
|
||||
|
Returns **[Object][7]** Style object |
||||
|
|
||||
|
### getLayerSeparator |
||||
|
|
||||
|
Get layer separator. |
||||
|
|
||||
|
Returns **[RegExp][3]** |
||||
|
|
||||
|
[1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean |
||||
|
|
||||
|
[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String |
||||
|
|
||||
|
[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/RegExp |
||||
|
|
||||
|
[4]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function |
||||
|
|
||||
|
[5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array |
||||
|
|
||||
|
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number |
||||
|
|
||||
|
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object |
||||
@ -0,0 +1 @@ |
|||||
|
export { View as default } from 'backbone'; |
||||
@ -1,2 +1,3 @@ |
|||||
export { default as Model } from './Model'; |
export { default as Model } from './Model'; |
||||
export { default as Collection } from './Collection'; |
export { default as Collection } from './Collection'; |
||||
|
export { default as View } from './View'; |
||||
|
|||||
@ -1,29 +1,91 @@ |
|||||
export default { |
export default { |
||||
stylePrefix: 'sm-', |
// Default sectors and properties
|
||||
|
sectors: [ |
||||
sectors: [], |
{ |
||||
|
name: 'General', |
||||
|
open: false, |
||||
|
properties: ['display', 'float', 'position', 'top', 'right', 'left', 'bottom'], |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Flex', |
||||
|
open: false, |
||||
|
properties: [ |
||||
|
'flex-direction', |
||||
|
'flex-wrap', |
||||
|
'justify-content', |
||||
|
'align-items', |
||||
|
'align-content', |
||||
|
'order', |
||||
|
'flex-basis', |
||||
|
'flex-grow', |
||||
|
'flex-shrink', |
||||
|
'align-self', |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Dimension', |
||||
|
open: false, |
||||
|
properties: ['width', 'height', 'max-width', 'min-height', 'margin', 'padding'], |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Typography', |
||||
|
open: false, |
||||
|
properties: [ |
||||
|
'font-family', |
||||
|
'font-size', |
||||
|
'font-weight', |
||||
|
'letter-spacing', |
||||
|
'color', |
||||
|
'line-height', |
||||
|
'text-align', |
||||
|
'text-shadow', |
||||
|
], |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Decorations', |
||||
|
open: false, |
||||
|
properties: ['background-color', 'border-radius', 'border', 'box-shadow', 'background'], |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Extra', |
||||
|
open: false, |
||||
|
properties: ['opacity', 'transition'], |
||||
|
}, |
||||
|
], |
||||
|
|
||||
// Specify the element to use as a container, string (query) or HTMLElement
|
// Specify the element to use as a container, string (query) or HTMLElement
|
||||
// With the empty value, nothing will be rendered
|
// With the empty value, nothing will be rendered
|
||||
appendTo: '', |
appendTo: '', |
||||
|
|
||||
|
// Style prefix
|
||||
|
stylePrefix: 'sm-', |
||||
|
|
||||
|
// Avoid rendering the default style manager.
|
||||
|
custom: false, |
||||
|
|
||||
// Hide the property in case it's not stylable for the
|
// Hide the property in case it's not stylable for the
|
||||
// selected component (each component has 'stylable' property)
|
// selected component (each component has 'stylable' property)
|
||||
|
// @deprecated
|
||||
hideNotStylable: true, |
hideNotStylable: true, |
||||
|
|
||||
// Highlight changed properties of the selected component
|
// Highlight changed properties of the selected component
|
||||
|
// @deprecated
|
||||
highlightChanged: true, |
highlightChanged: true, |
||||
|
|
||||
// Highlight computed properties of the selected component
|
// Highlight computed properties of the selected component
|
||||
|
// @deprecated
|
||||
highlightComputed: true, |
highlightComputed: true, |
||||
|
|
||||
// Show computed properties of the selected component, if this value
|
// Show computed properties of the selected component, if this value
|
||||
// is set to false, highlightComputed will not take effect
|
// is set to false, highlightComputed will not take effect
|
||||
|
// @deprecated
|
||||
showComputed: true, |
showComputed: true, |
||||
|
|
||||
// Adds the possibility to clear property value from the target style
|
// Adds the possibility to clear property value from the target style
|
||||
|
// @deprecated
|
||||
clearProperties: true, |
clearProperties: true, |
||||
|
|
||||
// Properties not to take in account for computed styles
|
// Properties not to take in account for computed styles
|
||||
avoidComputed: ['width', 'height'] |
// @deprecated
|
||||
|
avoidComputed: ['width', 'height'], |
||||
}; |
}; |
||||
|
|||||
@ -1,68 +1,111 @@ |
|||||
import Backbone from 'backbone'; |
import { Model } from 'common'; |
||||
import Properties from './Properties'; |
import { camelCase } from 'utils/mixins'; |
||||
|
|
||||
export default Backbone.Model.extend({ |
export default class Layer extends Model { |
||||
defaults: { |
defaults() { |
||||
index: '', |
return { |
||||
value: '', |
values: {}, |
||||
values: {}, |
}; |
||||
active: false, |
} |
||||
preview: false, |
|
||||
properties: [] |
|
||||
}, |
|
||||
|
|
||||
initialize() { |
initialize() { |
||||
const prp = this.get('properties'); |
const cl = this.collection; |
||||
var value = this.get('value'); |
this.prop = cl?.prop; |
||||
this.set( |
} |
||||
'properties', |
|
||||
prp instanceof Properties ? prp : new Properties(prp) |
|
||||
); |
|
||||
const props = this.get('properties'); |
|
||||
props.forEach(this.onPropAdd, this); |
|
||||
this.listenTo(props, 'add', this.onPropAdd); |
|
||||
|
|
||||
// If there is no value I'll try to get it from values
|
/** |
||||
// I need value setted to make preview working
|
* Get layer id. |
||||
if (!value) { |
* @returns {String} |
||||
var val = ''; |
*/ |
||||
var values = this.get('values'); |
getId() { |
||||
|
return this.cid; |
||||
|
} |
||||
|
|
||||
for (var prop in values) { |
/** |
||||
val += ' ' + values[prop]; |
* Get layer index. |
||||
} |
* @returns {Number} |
||||
|
*/ |
||||
|
getIndex() { |
||||
|
const coll = this.collection; |
||||
|
return coll ? coll.indexOf(this) : -1; |
||||
|
} |
||||
|
|
||||
this.set('value', val.trim()); |
/** |
||||
} |
* Get layer values. |
||||
}, |
* @param {Object} [opts={}] Options |
||||
|
* @param {Boolean} [opts.camelCase] Return property names in camelCase. |
||||
|
* @returns {Object} |
||||
|
*/ |
||||
|
getValues(opts = {}) { |
||||
|
const values = this.get('values'); |
||||
|
|
||||
onPropAdd(prop) { |
return opts.camelCase |
||||
const coll = this.collection; |
? Object.keys(values).reduce((res, key) => { |
||||
prop.parent = coll && coll.property; |
res[camelCase(key)] = values[key]; |
||||
}, |
return res; |
||||
|
}, {}) |
||||
|
: values; |
||||
|
} |
||||
|
|
||||
/** |
/** |
||||
* Get property at some index |
* Get layer label. |
||||
* @param {Number} index |
* @returns {String} |
||||
* @return {Object} |
|
||||
*/ |
*/ |
||||
getPropertyAt(index) { |
getLabel() { |
||||
return this.get('properties').at(index); |
return this.prop?.getLayerLabel(this); |
||||
}, |
} |
||||
|
|
||||
getPropertyValue(property) { |
/** |
||||
let result = ''; |
* Check if the layer is selected. |
||||
this.get('properties').each(prop => { |
* @returns {Boolean} |
||||
if (prop.get('property') == property) { |
*/ |
||||
result = prop.getFullValue(); |
isSelected() { |
||||
} |
return this.prop?.getSelectedLayer() === this; |
||||
}); |
} |
||||
return result; |
|
||||
}, |
|
||||
|
|
||||
getFullValue() { |
/** |
||||
let result = []; |
* Select the layer. |
||||
this.get('properties').each(prop => result.push(prop.getFullValue())); |
*/ |
||||
return result.join(' ').trim(); |
select() { |
||||
|
return this.prop?.selectLayer(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Remove the layer. |
||||
|
*/ |
||||
|
remove() { |
||||
|
return this.prop?.removeLayer(this); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Move layer to a new index. |
||||
|
* @param {Number} index New index |
||||
|
*/ |
||||
|
move(index) { |
||||
|
return this.prop?.moveLayer(this, index); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get style object for the preview. |
||||
|
* @param {Object} [opts={}] Options. Same of `PropertyStack.getStyleFromLayer` |
||||
|
* @returns {Object} Style object |
||||
|
*/ |
||||
|
getStylePreview(opts = {}) { |
||||
|
return this.prop?.getStylePreview(this, opts); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Check if the property has the preview enabled for this layer. |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
hasPreview() { |
||||
|
return !!this.prop?.get('preview'); |
||||
|
} |
||||
|
|
||||
|
upValues(props = {}) { |
||||
|
return this.set('values', { |
||||
|
...this.getValues(), |
||||
|
...props, |
||||
|
}); |
||||
} |
} |
||||
}); |
} |
||||
|
|||||
@ -1,123 +1,10 @@ |
|||||
import { isUndefined } from 'underscore'; |
import { Collection } from 'common'; |
||||
import Backbone from 'backbone'; |
|
||||
import Layer from './Layer'; |
import Layer from './Layer'; |
||||
|
|
||||
export default Backbone.Collection.extend({ |
export default class Layers extends Collection { |
||||
model: Layer, |
initialize(p, opts = {}) { |
||||
|
this.prop = opts.prop; |
||||
initialize() { |
|
||||
this.idx = 1; |
|
||||
this.on('add', this.onAdd); |
|
||||
this.on('reset', this.onReset); |
|
||||
}, |
|
||||
|
|
||||
onAdd(model, c, opts) { |
|
||||
if (!opts.noIncrement) model.set('index', this.idx++); |
|
||||
opts.active && this.active(this.indexOf(model)); |
|
||||
}, |
|
||||
|
|
||||
onReset() { |
|
||||
this.idx = 1; |
|
||||
}, |
|
||||
|
|
||||
getSeparator() { |
|
||||
const { property } = this; |
|
||||
return property ? property.get('layerSeparator') : ', '; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get layers from a value string (for not detached properties), |
|
||||
* example of input: |
|
||||
* `layer1Value, layer2Value, layer3Value, ...` |
|
||||
* @param {string} value |
|
||||
* @return {Array} |
|
||||
* @private |
|
||||
*/ |
|
||||
getLayersFromValue(value) { |
|
||||
const layers = []; |
|
||||
// Remove spaces inside functions, eg:
|
|
||||
// From: 1px 1px rgba(2px, 2px, 2px), 2px 2px rgba(3px, 3px, 3px)
|
|
||||
// To: 1px 1px rgba(2px,2px,2px), 2px 2px rgba(3px,3px,3px)
|
|
||||
value.replace(/\(([\w\s,.]*)\)/g, match => { |
|
||||
var cleaned = match.replace(/,\s*/g, ','); |
|
||||
value = value.replace(match, cleaned); |
|
||||
}); |
|
||||
const layerValues = value ? value.split(this.getSeparator()) : []; |
|
||||
layerValues.forEach(layerValue => { |
|
||||
layers.push({ properties: this.properties.parseValue(layerValue) }); |
|
||||
}); |
|
||||
return layers; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get layers from a style object (for detached properties), |
|
||||
* example of input: |
|
||||
* { |
|
||||
* subPropname1: sub-propvalue11, sub-propvalue12, sub-propvalue13, ... |
|
||||
* subPropname2: sub-propvalue21, sub-propvalue22, sub-propvalue23, ... |
|
||||
* subPropname3: sub-propvalue31, sub-propvalue32, sub-propvalue33, ... |
|
||||
* } |
|
||||
* @param {Object} styleObj |
|
||||
* @return {Array} |
|
||||
* @private |
|
||||
*/ |
|
||||
getLayersFromStyle(styleObj) { |
|
||||
const layers = []; |
|
||||
const properties = this.properties; |
|
||||
|
|
||||
properties.each(propModel => { |
|
||||
const style = styleObj[propModel.get('property')]; |
|
||||
const values = style ? style.split(', ') : []; |
|
||||
values.forEach((value, i) => { |
|
||||
value = propModel.parseValue(value.trim()).value; |
|
||||
const layer = layers[i]; |
|
||||
const propertyObj = { ...propModel.attributes, ...{ value } }; |
|
||||
|
|
||||
if (layer) { |
|
||||
layer.properties.push(propertyObj); |
|
||||
} else { |
|
||||
layers[i] = { |
|
||||
properties: [propertyObj] |
|
||||
}; |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
// Now whit all layers in, will check missing properties
|
|
||||
layers.forEach(layer => { |
|
||||
const layerProprs = layer.properties.map(prop => prop.property); |
|
||||
properties.each(propModel => { |
|
||||
const propertyName = propModel.get('property'); |
|
||||
|
|
||||
if (layerProprs.indexOf(propertyName) < 0) { |
|
||||
layer.properties.push({ ...propModel.attributes }); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
return layers; |
|
||||
}, |
|
||||
|
|
||||
active(index) { |
|
||||
this.each(layer => layer.set('active', 0)); |
|
||||
const layer = this.at(index); |
|
||||
layer && layer.set('active', 1); |
|
||||
}, |
|
||||
|
|
||||
getFullValue() { |
|
||||
let result = []; |
|
||||
this.each(layer => result.push(layer.getFullValue())); |
|
||||
return result.join(this.getSeparator()); |
|
||||
}, |
|
||||
|
|
||||
getPropertyValues(property, defValue) { |
|
||||
const result = []; |
|
||||
this.each(layer => { |
|
||||
const value = layer.getPropertyValue(property); |
|
||||
value |
|
||||
? result.push(value) |
|
||||
: !isUndefined(defValue) && result.push(defValue); |
|
||||
}); |
|
||||
return result.join(', '); |
|
||||
} |
} |
||||
}); |
} |
||||
|
|
||||
|
Layers.prototype.model = Layer; |
||||
|
|||||
@ -1,101 +1,324 @@ |
|||||
|
import { isString, isUndefined, keys } from 'underscore'; |
||||
import Property from './Property'; |
import Property from './Property'; |
||||
|
import Properties from './Properties'; |
||||
|
|
||||
export default Property.extend({ |
export const isNumberType = type => type === 'integer' || type === 'number'; |
||||
defaults: { |
|
||||
...Property.prototype.defaults, |
/** |
||||
// 'background' is a good example where to make a difference
|
* |
||||
// between detached and not
|
* [Property]: property.html |
||||
//
|
* |
||||
// - NOT detached (default)
|
* |
||||
// background: url(..) no-repeat center ...;
|
* @typedef PropertyComposite |
||||
// - Detached
|
* @property {Array<Object>} properties Array of sub properties, eg. `[{ type: 'number', property: 'margin-top' }, ...]` |
||||
// background-image: url();
|
* @property {Boolean} [detached=false] Indicate if the final CSS property is splitted (detached: `margin-top: X; margin-right: Y; ...`) or combined (not detached: `margin: X Y ...;`) |
||||
// background-repeat: repeat;
|
* @property {String|RegExp} [separator=' '] Value used to split property values, default `" "`. |
||||
// ...
|
* @property {String} [join=' '] Value used to join property values, default `" "`. |
||||
detached: 0, |
* @property {Function} [fromStyle] Custom logic for getting property values from the target style object. |
||||
|
* \n |
||||
// Array of sub properties
|
* ```js
|
||||
properties: [], |
* fromStyle: (style) => { |
||||
|
* const margins = parseMarginShorthand(style.margin); |
||||
// Separator between properties
|
* return { |
||||
separator: ' ' |
* 'margin-top': margins.top, |
||||
}, |
* // ...
|
||||
|
* }; |
||||
|
* } |
||||
|
* ``` |
||||
|
* @property {Function} [toStyle] Custom logic for creating the CSS style object to apply on selected targets. |
||||
|
* \n |
||||
|
* ```js
|
||||
|
* toStyle: (values) => { |
||||
|
* const top = values['margin-top'] || 0; |
||||
|
* const right = values['margin-right'] || 0; |
||||
|
* // ...
|
||||
|
* return { |
||||
|
* margin: `${top} ${right} ...`, |
||||
|
* }; |
||||
|
* } |
||||
|
* ``` |
||||
|
*/ |
||||
|
export default class PropertyComposite extends Property { |
||||
|
defaults() { |
||||
|
return { |
||||
|
...Property.getDefaults(), |
||||
|
detached: false, |
||||
|
properties: [], |
||||
|
separator: ' ', |
||||
|
join: null, |
||||
|
fromStyle: null, |
||||
|
toStyle: null, |
||||
|
full: true, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
initialize(props = {}, opts = {}) { |
initialize(props = {}, opts = {}) { |
||||
Property.callParentInit(Property, this, props, opts); |
Property.callParentInit(Property, this, props, opts); |
||||
const properties = this.get('properties') || []; |
const { em } = this; |
||||
const Properties = require('./Properties').default; |
const properties = new Properties(this.get('properties') || [], { em, parentProp: this }); |
||||
this.set('properties', new Properties(properties)); |
this.set('properties', properties, { silent: 1 }); |
||||
this.listenTo(this, 'change:value', this.updateValues); |
this.listenTo(properties, 'change', this.__upProperties); |
||||
Property.callInit(this, props, opts); |
Property.callInit(this, props, opts); |
||||
}, |
} |
||||
|
|
||||
/** |
/** |
||||
* Clear the value |
* Get properties. |
||||
* @return {this} |
* @returns {Array<[Property]>} |
||||
*/ |
*/ |
||||
clearValue(opts = {}) { |
getProperties() { |
||||
this.get('properties').each(property => property.clearValue()); |
return [...this.get('properties').models]; |
||||
return Property.prototype.clearValue.apply(this, arguments); |
} |
||||
}, |
|
||||
|
|
||||
/** |
/** |
||||
* Update property values |
* Get property by id. |
||||
|
* @param {String} id Property id. |
||||
|
* @returns {[Property]|null} |
||||
*/ |
*/ |
||||
updateValues() { |
getProperty(id) { |
||||
const values = this.getFullValue().split(this.getSplitSeparator()); |
return this.get('properties').filter(prop => prop.getId() === id || prop.getName() === id)[0] || null; |
||||
this.get('properties').each((property, i) => { |
} |
||||
const len = values.length; |
|
||||
// Try to get value from a shorthand:
|
|
||||
// 11px -> 11px 11px 11px 11xp
|
|
||||
// 11px 22px -> 11px 22px 11px 22xp
|
|
||||
const value = |
|
||||
values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)]; |
|
||||
// There some issue with UndoManager
|
|
||||
//property.setValue(value, 0, {fromParent: 1});
|
|
||||
}); |
|
||||
}, |
|
||||
|
|
||||
/** |
/** |
||||
* Split by sperator but avoid it inside parenthesis |
* Get property at index. |
||||
* @return {RegExp} |
* @param {Number} index |
||||
|
* @returns {[Property]|null} |
||||
*/ |
*/ |
||||
getSplitSeparator() { |
getPropertyAt(index) { |
||||
return new RegExp(`${this.get('separator')}(?![^\\(]*\\))`); |
return this.get('properties').at(index); |
||||
}, |
} |
||||
|
|
||||
|
/** |
||||
|
* Check if the property is detached. |
||||
|
* @returns {Boolean} |
||||
|
*/ |
||||
|
isDetached() { |
||||
|
return !!this.get('detached'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get current values of properties. |
||||
|
* @param {Object} [opts={}] Options |
||||
|
* @param {Boolean} [opts.byName=false] Use property names as a key instead of the id. |
||||
|
* @returns {Object} |
||||
|
* @example |
||||
|
* // In case the property is `margin` with sub properties like `margin-top`, `margin-right`, etc.
|
||||
|
* console.log(property.getValues()); |
||||
|
* // { 'margin-top': '10px', 'margin-right': '20px', ... };
|
||||
|
*/ |
||||
|
getValues({ byName } = {}) { |
||||
|
return this.getProperties().reduce((res, prop) => { |
||||
|
const key = byName ? prop.getName() : prop.getId(); |
||||
|
res[key] = `${prop.__getFullValue()}`; |
||||
|
return res; |
||||
|
}, {}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get property separator. |
||||
|
* @returns {RegExp} |
||||
|
*/ |
||||
|
getSeparator() { |
||||
|
return this.getSplitSeparator(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the join value. |
||||
|
* @returns {String} |
||||
|
*/ |
||||
|
getJoin() { |
||||
|
return this.__getJoin(); |
||||
|
} |
||||
|
|
||||
/** |
/** |
||||
* Returns default value |
* Get style object from current properties |
||||
* @param {Boolean} defaultProps Force to get defaults from properties |
* @param {Object} [opts={}] Options |
||||
* @return {string} |
* @param {Boolean} [opts.camelCase] Return property names in camelCase. |
||||
|
* @returns {Object} Style object |
||||
|
* @private |
||||
*/ |
*/ |
||||
getDefaultValue(defaultProps) { |
getStyleFromProps(opts = {}) { |
||||
let value = this.get('defaults'); |
const name = this.getName(); |
||||
|
const join = this.__getJoin(); |
||||
|
const toStyle = this.get('toStyle'); |
||||
|
let values = this.getValues(); |
||||
|
let style = {}; |
||||
|
|
||||
if (value && !defaultProps) { |
if (toStyle) { |
||||
return value; |
style = toStyle(values, { join, name, property: this }); |
||||
|
} else { |
||||
|
values = this.getValues({ byName: true }); |
||||
|
|
||||
|
if (this.isDetached()) { |
||||
|
style = values; |
||||
|
} else { |
||||
|
const value = this.getProperties() |
||||
|
.map(p => p.__getFullValue({ withDefault: 1 })) |
||||
|
.filter(Boolean) |
||||
|
.join(join); |
||||
|
style = { [name]: value }; |
||||
|
} |
||||
} |
} |
||||
|
|
||||
value = ''; |
if (this.isDetached()) { |
||||
const properties = this.get('properties'); |
style[name] = ''; |
||||
properties.each((prop, index) => (value += `${prop.getDefaultValue()} `)); |
} else { |
||||
return value.trim(); |
style[name] = style[name] || ''; |
||||
}, |
style = { |
||||
|
...style, |
||||
|
...this.getProperties().reduce((acc, prop) => { |
||||
|
acc[prop.getName()] = ''; |
||||
|
return acc; |
||||
|
}, {}), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
getFullValue() { |
return opts.camelCase |
||||
if (this.get('detached')) { |
? Object.keys(style).reduce((res, key) => { |
||||
return ''; |
res[camelCase(key)] = style[key]; |
||||
|
return res; |
||||
|
}, {}) |
||||
|
: style; |
||||
|
} |
||||
|
|
||||
|
getSplitSeparator() { |
||||
|
return new RegExp(`${this.get('separator')}(?![^\\(]*\\))`); |
||||
|
} |
||||
|
|
||||
|
__upProperties(p, opts = {}) { |
||||
|
if (opts.__up || opts.__clearIn) return; |
||||
|
|
||||
|
const parentProp = this.__getParentProp(); |
||||
|
if (parentProp) return parentProp.__upProperties(this, opts); |
||||
|
|
||||
|
this.__upTargetsStyleProps(opts, p); |
||||
|
} |
||||
|
|
||||
|
__upTargetsStyleProps(opts = {}, prop) { |
||||
|
let style = this.getStyleFromProps(); |
||||
|
|
||||
|
if (this.isDetached() && prop) { |
||||
|
const name = prop.getName(); |
||||
|
style = { [name]: style[name] }; |
||||
} |
} |
||||
|
|
||||
return this.get('properties').getFullValue(); |
this.__upTargetsStyle(style, opts); |
||||
}, |
} |
||||
|
|
||||
/** |
_up(props, opts = {}) { |
||||
* Get property at some index |
this.__setProperties(this.__getSplitValue(props.value), opts); |
||||
* @param {Number} index |
return Property.prototype._up.call(this, props, opts); |
||||
* @return {Object} |
} |
||||
*/ |
|
||||
getPropertyAt(index) { |
getStyle(opts) { |
||||
return this.get('properties').at(index); |
return this.getStyleFromProps(opts); |
||||
|
} |
||||
|
|
||||
|
__getFullValue(opts = {}) { |
||||
|
if (this.isDetached() || opts.__clear) return ''; |
||||
|
|
||||
|
return this.getStyleFromProps()[this.getName()] || ''; |
||||
|
} |
||||
|
|
||||
|
__getJoin() { |
||||
|
const join = this.get('join'); |
||||
|
return isString(join) ? join : this.get('separator'); |
||||
|
} |
||||
|
|
||||
|
__styleHasProps(style = {}) { |
||||
|
const name = this.getName(); |
||||
|
const props = this.getProperties(); |
||||
|
const nameProps = props.map(prop => prop.getName()); |
||||
|
const allNameProps = [name, ...nameProps]; |
||||
|
return allNameProps.some(prop => !isUndefined(style[prop]) && style[prop] !== ''); |
||||
|
} |
||||
|
|
||||
|
__splitValue(value, sep) { |
||||
|
return value |
||||
|
.split(sep) |
||||
|
.map(value => value.trim()) |
||||
|
.filter(Boolean); |
||||
|
} |
||||
|
|
||||
|
__splitStyleName(style, name, sep) { |
||||
|
return this.__splitValue(style[name] || '', sep); |
||||
|
} |
||||
|
|
||||
|
__getSplitValue(value = '', { byName } = {}) { |
||||
|
const props = this.getProperties(); |
||||
|
const props4Nums = props.length === 4 && props.every(prop => isNumberType(prop.getType())); |
||||
|
const values = this.__splitValue(value, this.getSplitSeparator()); |
||||
|
const result = {}; |
||||
|
|
||||
|
props.forEach((prop, i) => { |
||||
|
const value = values[i]; |
||||
|
let res = !isUndefined(value) ? value : ''; // : prop.getDefaultValue();
|
||||
|
|
||||
|
if (props4Nums) { |
||||
|
// Try to get value from a shorthand:
|
||||
|
// 11px -> 11px 11px 11px 11xp
|
||||
|
// 11px 22px -> 11px 22px 11px 22xp
|
||||
|
const len = values.length; |
||||
|
res = values[i] || values[(i % len) + (len != 1 && len % 2 ? 1 : 0)] || res; |
||||
|
} |
||||
|
|
||||
|
const key = byName ? prop.getName() : prop.getId(); |
||||
|
result[key] = res || ''; |
||||
|
}); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
__getPropsFromStyle(style = {}, opts = {}) { |
||||
|
if (!this.__styleHasProps(style)) return null; |
||||
|
|
||||
|
const { byName } = opts; |
||||
|
const props = this.getProperties(); |
||||
|
const sep = this.getSplitSeparator(); |
||||
|
const fromStyle = this.get('fromStyle'); |
||||
|
let result = fromStyle ? fromStyle(style, { property: this, separator: sep }) : {}; |
||||
|
|
||||
|
if (!fromStyle) { |
||||
|
// Get props from the main property
|
||||
|
result = this.__getSplitValue(style[this.getName()] || '', { byName }); |
||||
|
|
||||
|
// Get props from the inner properties
|
||||
|
props.forEach(prop => { |
||||
|
const value = style[prop.getName()]; |
||||
|
const key = byName ? prop.getName() : prop.getId(); |
||||
|
if (!isUndefined(value) && value !== '') result[key] = value; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
__setProperties(values = {}, opts = {}) { |
||||
|
this.getProperties().forEach(prop => { |
||||
|
const value = values[prop.getId()]; |
||||
|
prop.__getFullValue() !== value && prop.upValue(value, opts); |
||||
|
}); |
||||
|
|
||||
|
// Keep track of the values, otherwise clear() will not trigger changes.
|
||||
|
const valuesStr = keys(values) |
||||
|
.map(k => values[k]) |
||||
|
.join(' '); |
||||
|
this.set('value', valuesStr, { silent: true }); |
||||
|
} |
||||
|
|
||||
|
clear() { |
||||
|
this.getProperties().map(p => p.clear({ __clearIn: !this.isDetached() })); |
||||
|
return Property.prototype.clear.call(this); |
||||
|
} |
||||
|
|
||||
|
hasValue(opts) { |
||||
|
return this.getProperties().some(prop => prop.hasValue(opts)); |
||||
|
} |
||||
|
|
||||
|
getFullValue() { |
||||
|
return this.__getFullValue(); |
||||
|
} |
||||
|
|
||||
|
__canClearProp(prop) { |
||||
|
return this.isDetached() && prop.hasValue({ noParent: true }); |
||||
} |
} |
||||
}); |
} |
||||
|
|||||
@ -1,76 +0,0 @@ |
|||||
import { isUndefined } from 'underscore'; |
|
||||
import Property from './Property'; |
|
||||
import InputNumber from 'domain_abstract/ui/InputNumber'; |
|
||||
import { hasWin } from 'utils/mixins'; |
|
||||
|
|
||||
export default Property.extend({ |
|
||||
defaults: { |
|
||||
...Property.prototype.defaults, |
|
||||
// Array of units, eg. ['px', '%']
|
|
||||
units: [], |
|
||||
|
|
||||
// Selected unit, eg. 'px'
|
|
||||
unit: '', |
|
||||
|
|
||||
// Integer value steps
|
|
||||
step: 1, |
|
||||
|
|
||||
// Minimum value
|
|
||||
min: '', |
|
||||
|
|
||||
// Maximum value
|
|
||||
max: '' |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get property units. |
|
||||
* @returns {Array<String>} |
|
||||
*/ |
|
||||
getUnits() { |
|
||||
return this.get('units') || []; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Get property unit value. |
|
||||
* @returns {String} |
|
||||
*/ |
|
||||
getValueUnit() { |
|
||||
return this.get('unit'); |
|
||||
}, |
|
||||
|
|
||||
initialize(props = {}, opts = {}) { |
|
||||
Property.callParentInit(Property, this, props, opts); |
|
||||
const unit = this.get('unit'); |
|
||||
const units = this.get('units'); |
|
||||
this.input = hasWin() && new InputNumber({ model: this }); |
|
||||
|
|
||||
if (units.length && !unit) { |
|
||||
this.set('unit', units[0]); |
|
||||
} |
|
||||
Property.callInit(this, props, opts); |
|
||||
}, |
|
||||
|
|
||||
clearValue(opts = {}) { |
|
||||
this.set({ value: undefined, unit: undefined }, opts); |
|
||||
return this; |
|
||||
}, |
|
||||
|
|
||||
parseValue(val) { |
|
||||
const parsed = Property.prototype.parseValue.apply(this, arguments); |
|
||||
const { value, unit } = this.input.validateInputValue(parsed.value, { |
|
||||
deepCheck: 1 |
|
||||
}); |
|
||||
parsed.value = value; |
|
||||
parsed.unit = unit; |
|
||||
return parsed; |
|
||||
}, |
|
||||
|
|
||||
getFullValue() { |
|
||||
let value = this.get('value'); |
|
||||
let unit = this.get('unit'); |
|
||||
value = !isUndefined(value) ? value : ''; |
|
||||
unit = !isUndefined(unit) && value ? unit : ''; |
|
||||
value = `${value}${unit}`; |
|
||||
return Property.prototype.getFullValue.apply(this, [value]); |
|
||||
} |
|
||||
}); |
|
||||
@ -0,0 +1,116 @@ |
|||||
|
import { isUndefined } from 'underscore'; |
||||
|
import Property from './Property'; |
||||
|
import InputNumber from 'domain_abstract/ui/InputNumber'; |
||||
|
import { hasWin } from 'utils/mixins'; |
||||
|
|
||||
|
/** |
||||
|
* @typedef PropertyNumber |
||||
|
* @property {Array<String>} units Array of units, eg. `['px', '%']` |
||||
|
* @property {Number} min Minimum value. |
||||
|
* @property {Number} max Maximum value. |
||||
|
* @property {Number} step Step value. |
||||
|
* |
||||
|
*/ |
||||
|
export default class PropertyNumber extends Property { |
||||
|
defaults() { |
||||
|
return { |
||||
|
...Property.getDefaults(), |
||||
|
units: [], |
||||
|
unit: '', |
||||
|
min: '', |
||||
|
max: '', |
||||
|
step: 1, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get property units. |
||||
|
* @returns {Array<String>} |
||||
|
*/ |
||||
|
getUnits() { |
||||
|
return this.get('units') || []; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get property unit value. |
||||
|
* @returns {String} |
||||
|
*/ |
||||
|
getUnit() { |
||||
|
return this.get('unit'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get min value. |
||||
|
* @returns {Number} |
||||
|
*/ |
||||
|
getMin() { |
||||
|
return this.get('min'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get max value. |
||||
|
* @returns {Number} |
||||
|
*/ |
||||
|
getMax() { |
||||
|
return this.get('max'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get step value. |
||||
|
* @returns {Number} |
||||
|
*/ |
||||
|
getStep() { |
||||
|
return this.get('step'); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Update property unit value. |
||||
|
* The change is also propagated to the selected targets. |
||||
|
* @param {String} unit New unit value |
||||
|
* @param {Object} [opts={}] Options |
||||
|
* @param {Boolean} [opts.noTarget=false] If `true` the change won't be propagated to selected targets. |
||||
|
* @returns {String} |
||||
|
*/ |
||||
|
upUnit(unit, opts) { |
||||
|
return this._up({ unit }, opts); |
||||
|
} |
||||
|
|
||||
|
initialize(props = {}, opts = {}) { |
||||
|
Property.callParentInit(Property, this, props, opts); |
||||
|
const unit = this.get('unit'); |
||||
|
const units = this.get('units'); |
||||
|
this.input = hasWin() && new InputNumber({ model: this }); |
||||
|
|
||||
|
if (units.length && !unit) { |
||||
|
this.set('unit', units[0], { silent: 1 }); |
||||
|
} |
||||
|
Property.callInit(this, props, opts); |
||||
|
} |
||||
|
|
||||
|
__getClearProps() { |
||||
|
return { |
||||
|
...Property.prototype.__getClearProps(), |
||||
|
unit: '', |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
parseValue(val, opts = {}) { |
||||
|
const parsed = Property.prototype.parseValue.apply(this, arguments); |
||||
|
const { value, unit } = this.input.validateInputValue(parsed.value, { |
||||
|
deepCheck: 1, |
||||
|
...opts, |
||||
|
}); |
||||
|
parsed.value = value; |
||||
|
parsed.unit = unit; |
||||
|
return parsed; |
||||
|
} |
||||
|
|
||||
|
getFullValue() { |
||||
|
let value = this.get('value'); |
||||
|
let unit = this.get('unit'); |
||||
|
value = !isUndefined(value) ? value : ''; |
||||
|
unit = !isUndefined(unit) && value ? unit : ''; |
||||
|
value = `${value}${unit}`; |
||||
|
return Property.prototype.getFullValue.apply(this, [value]); |
||||
|
} |
||||
|
} |
||||
@ -1,37 +1,10 @@ |
|||||
import Property from './Property'; |
import Property from './PropertySelect'; |
||||
|
|
||||
export default Property.extend({ |
export default class PropertyRadio extends Property { |
||||
defaults: () => ({ |
defaults() { |
||||
...Property.prototype.defaults, |
return { |
||||
// Array of options, eg. [{name: 'Label ', value: '100'}]
|
...Property.getDefaults(), |
||||
options: [], |
full: 1, |
||||
full: 1 |
}; |
||||
}), |
|
||||
|
|
||||
initialize(...args) { |
|
||||
Property.prototype.initialize.apply(this, args); |
|
||||
this.listenTo(this, 'change:options', this.onOptionChange); |
|
||||
}, |
|
||||
|
|
||||
onOptionChange() { |
|
||||
this.set('list', this.get('options')); |
|
||||
}, |
|
||||
|
|
||||
getOptions() { |
|
||||
const { options, list } = this.attributes; |
|
||||
return options && options.length ? options : list; |
|
||||
}, |
|
||||
|
|
||||
setOptions(opts = []) { |
|
||||
this.set('options', opts); |
|
||||
return this; |
|
||||
}, |
|
||||
|
|
||||
addOption(opt) { |
|
||||
if (opt) { |
|
||||
const opts = this.getOptions(); |
|
||||
this.setOptions([...opts, opt]); |
|
||||
} |
|
||||
return this; |
|
||||
} |
} |
||||
}); |
} |
||||
|
|||||
@ -1,8 +1,99 @@ |
|||||
import Property from './PropertyRadio'; |
import Property from './Property'; |
||||
|
import { isDef } from 'utils/mixins'; |
||||
export default Property.extend({ |
import { isString } from 'underscore'; |
||||
defaults: () => ({ |
|
||||
...Property.prototype.defaults(), |
/** |
||||
full: 0 |
* @typedef PropertySelect |
||||
}) |
* @property {Array<Object>} options Array of option definitions. |
||||
}); |
* \n |
||||
|
* ```js
|
||||
|
* options: [ |
||||
|
* { id: '100', label: 'Set 100' }, |
||||
|
* { id: '200', label: 'Set 200' }, |
||||
|
* ] |
||||
|
* ``` |
||||
|
*/ |
||||
|
export default class PropertySelect extends Property { |
||||
|
defaults() { |
||||
|
return { |
||||
|
...Property.getDefaults(), |
||||
|
options: [], |
||||
|
full: 0, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get available options. |
||||
|
* @returns {Array<Object>} Array of options |
||||
|
*/ |
||||
|
getOptions() { |
||||
|
// support old list property
|
||||
|
const { options, list } = this.attributes; |
||||
|
return (options && options.length ? options : list) || []; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get current selected option or by id. |
||||
|
* @param {String} [id] Option id. |
||||
|
* @returns {Object | null} |
||||
|
*/ |
||||
|
getOption(id) { |
||||
|
const idSel = id || this.getValue(); |
||||
|
return this.getOptions().filter(o => this.getOptionId(o) === idSel)[0] || null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Update options. |
||||
|
* @param {Array<Object>} value New array of options, eg. `[{ id: 'val-1', label: 'Value 1' }]` |
||||
|
*/ |
||||
|
setOptions(value = []) { |
||||
|
this.set('options', value); |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Add new option. |
||||
|
* @param {Object} value Option object, eg. `{ id: 'val-1', label: 'Value 1' }` |
||||
|
*/ |
||||
|
addOption(value) { |
||||
|
if (value) { |
||||
|
const opts = this.getOptions(); |
||||
|
this.setOptions([...opts, value]); |
||||
|
} |
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get the option id from the option object. |
||||
|
* @param {Object} option Option object |
||||
|
* @returns {String} Option id |
||||
|
*/ |
||||
|
getOptionId(option) { |
||||
|
return isDef(option.id) ? option.id : option.value; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get option label. |
||||
|
* @param {String|Object} id Option id or the option object |
||||
|
* @param {Object} [opts={}] Options |
||||
|
* @param {Boolean} [opts.locale=true] Use the locale string from i18n module |
||||
|
* @returns {String} Option label |
||||
|
*/ |
||||
|
getOptionLabel(id, opts = {}) { |
||||
|
const { locale = true } = opts; |
||||
|
const option = (isString(id) ? this.getOption(id) : id) || {}; |
||||
|
const optId = this.getOptionId(option); |
||||
|
const label = option.label || option.name || optId; |
||||
|
const propId = this.getId(); |
||||
|
return (locale && this.em?.t(`styleManager.options.${propId}.${optId}`)) || label; |
||||
|
} |
||||
|
|
||||
|
initialize(...args) { |
||||
|
Property.prototype.initialize.apply(this, args); |
||||
|
this.listenTo(this, 'change:options', this.__onOptionChange); |
||||
|
} |
||||
|
|
||||
|
__onOptionChange() { |
||||
|
this.set('list', this.get('options')); |
||||
|
} |
||||
|
} |
||||
|
|||||
@ -1,8 +1,10 @@ |
|||||
import Property from './PropertyInteger'; |
import Property from './PropertyNumber'; |
||||
|
|
||||
export default Property.extend({ |
export default class PropertySlider extends Property { |
||||
defaults: { |
defaults() { |
||||
...Property.prototype.defaults, |
return { |
||||
showInput: 1 |
...Property.getDefaults(), |
||||
|
showInput: 1, |
||||
|
}; |
||||
} |
} |
||||
}); |
} |
||||
|
|||||
@ -1,89 +1,480 @@ |
|||||
import { keys } from 'underscore'; |
import { keys, isUndefined, isArray, isString, isNumber } from 'underscore'; |
||||
import Property from './PropertyComposite'; |
import PropertyComposite, { isNumberType } from './PropertyComposite'; |
||||
|
import PropertyBase from './Property'; |
||||
import Layers from './Layers'; |
import Layers from './Layers'; |
||||
|
import { camelCase } from 'utils/mixins'; |
||||
|
|
||||
export default Property.extend({ |
const VALUES_REG = /,(?![^\(]*\))/; |
||||
defaults: { |
const PARTS_REG = /\s(?![^(]*\))/; |
||||
...Property.prototype.defaults, |
|
||||
// Array of layers (which contain properties)
|
|
||||
layers: [], |
|
||||
|
|
||||
// The separator used to join layer values
|
/** |
||||
layerSeparator: ', ', |
* |
||||
|
* [Layer]: layer.html |
||||
// Prepend new layers in the list
|
* |
||||
prepend: 0, |
* |
||||
|
* @typedef PropertyStack |
||||
// Layer preview
|
* @property {Boolean} [preview=false] Indicate if the layer should display a preview. |
||||
preview: 0 |
* @property {String|RegExp} [layerSeparator=', '] The separator used to split layer values. |
||||
}, |
* @property {String} [layerJoin=', '] Value used to join layer values. |
||||
|
* @property {Function} [layerLabel] Custom logic for creating layer labels. |
||||
|
* \n |
||||
|
* ```js
|
||||
|
* layerLabel: (layer) => { |
||||
|
* const values = layer.getValues(); |
||||
|
* return `A: ${values['prop-a']} B: ${values['prop-b']}`; |
||||
|
* } |
||||
|
* ``` |
||||
|
* |
||||
|
*/ |
||||
|
export default class PropertyStack extends PropertyComposite { |
||||
|
defaults() { |
||||
|
return { |
||||
|
...PropertyComposite.getDefaults(), |
||||
|
layers: [], |
||||
|
layerSeparator: ', ', |
||||
|
layerJoin: '', |
||||
|
prepend: 0, |
||||
|
preview: false, |
||||
|
layerLabel: null, |
||||
|
selectedLayer: null, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
initialize(props = {}, opts = {}) { |
initialize(props = {}, opts = {}) { |
||||
Property.callParentInit(Property, this, props, opts); |
PropertyComposite.callParentInit(PropertyComposite, this, props, opts); |
||||
const layers = this.get('layers'); |
const layers = this.get('layers'); |
||||
const layersColl = new Layers(layers); |
const layersColl = new Layers(layers, { prop: this }); |
||||
layersColl.property = this; |
layersColl.property = this; |
||||
layersColl.properties = this.get('properties'); |
layersColl.properties = this.get('properties'); |
||||
this.set('layers', layersColl); |
this.set('layers', layersColl, { silent: true }); |
||||
Property.callInit(this, props, opts); |
this.on('change:selectedLayer', this.__upSelected); |
||||
}, |
this.listenTo(layersColl, 'add remove', this.__upLayers); |
||||
|
PropertyComposite.callInit(this, props, opts); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get all available layers. |
||||
|
* @returns {Array<[Layer]>} |
||||
|
*/ |
||||
getLayers() { |
getLayers() { |
||||
|
return this.__getLayers().models; |
||||
|
} |
||||
|
|
||||
|
__getLayers() { |
||||
return this.get('layers'); |
return this.get('layers'); |
||||
}, |
} |
||||
|
|
||||
getCurrentLayer() { |
/** |
||||
return this.getLayers().filter(layer => layer.get('active'))[0]; |
* Get layer by index. |
||||
}, |
* @param {Number} [index=0] Layer index position. |
||||
|
* @returns {[Layer]|null} |
||||
getFullValue() { |
* @example |
||||
return this.get('detached') ? '' : this.get('layers').getFullValue(); |
* // Get the first layer
|
||||
}, |
* const layerFirst = property.getLayer(0); |
||||
|
* // Get the last layer
|
||||
getValueFromStyle(styles = {}) { |
* const layers = this.getLayers(); |
||||
const layers = this.getLayers().getLayersFromStyle(styles); |
* const layerLast = property.getLayer(layers.length - 1); |
||||
return new Layers(layers).getFullValue(); |
*/ |
||||
}, |
getLayer(index = 0) { |
||||
|
return this.__getLayers().at(index) || null; |
||||
clearValue() { |
} |
||||
this.getLayers().reset(); |
|
||||
return Property.prototype.clearValue.apply(this, arguments); |
/** |
||||
}, |
* Get selected layer. |
||||
|
* @returns {[Layer] | null} |
||||
getValueFromTarget(target) { |
*/ |
||||
const { detached, property, properties } = this.attributes; |
getSelectedLayer() { |
||||
const style = target.getStyle(); |
const layer = this.get('selectedLayer'); |
||||
const validStyles = {}; |
return layer && layer.getIndex() >= 0 ? layer : null; |
||||
|
} |
||||
properties.forEach(prop => { |
|
||||
const name = prop.get('property'); |
/** |
||||
const value = style[name]; |
* Select layer. |
||||
if (value) validStyles[name] = value; |
* Without a selected layer any update made on inner properties has no effect. |
||||
|
* @param {[Layer]} layer Layer to select |
||||
|
* @example |
||||
|
* const layer = property.getLayer(0); |
||||
|
* property.selectLayer(layer); |
||||
|
*/ |
||||
|
selectLayer(layer) { |
||||
|
return this.set('selectedLayer', layer, { __select: true }); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Select layer by index. |
||||
|
* @param {Number} index Index of the layer to select. |
||||
|
* @example |
||||
|
* property.selectLayerAt(1); |
||||
|
*/ |
||||
|
selectLayerAt(index = 0) { |
||||
|
const layer = this.getLayer(index); |
||||
|
return layer && this.selectLayer(layer); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Move layer by index. |
||||
|
* @param {[Layer]} layer Layer to move. |
||||
|
* @param {Number} index New layer index. |
||||
|
* @example |
||||
|
* const layer = property.getLayer(1); |
||||
|
* property.moveLayer(layer, 0); |
||||
|
*/ |
||||
|
moveLayer(layer, index = 0) { |
||||
|
const currIndex = layer ? layer.getIndex() : -1; |
||||
|
|
||||
|
if (currIndex >= 0 && isNumber(index) && index >= 0 && index < this.getLayers().length && currIndex !== index) { |
||||
|
this.removeLayer(layer); |
||||
|
this.__getLayers().add(layer, { at: index }); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Add new layer to the stack. |
||||
|
* @param {Object} [props={}] Custom property values to use in a new layer. |
||||
|
* @param {Object} [opts={}] Options |
||||
|
* @param {Number} [opts.at] Position index (by default the layer will be appended at the end). |
||||
|
* @returns {[Layer]} Added layer. |
||||
|
* @example |
||||
|
* // Add new layer at the beginning of the stack with custom values
|
||||
|
* property.addLayer({ 'sub-prop1': 'value1', 'sub-prop2': 'value2' }, { at: 0 }); |
||||
|
*/ |
||||
|
addLayer(props = {}, opts = {}) { |
||||
|
const values = {}; |
||||
|
this.getProperties().forEach(prop => { |
||||
|
const key = prop.getId(); |
||||
|
const value = props[key]; |
||||
|
values[key] = isUndefined(value) ? prop.getDefaultValue() : value; |
||||
}); |
}); |
||||
|
const layer = this.get('layers').push({ values }, opts); |
||||
|
|
||||
|
return layer; |
||||
|
} |
||||
|
|
||||
return !detached |
/** |
||||
? style[property] |
* Remove layer. |
||||
: keys(validStyles).length |
* @param {[Layer]} layer Layer to remove. |
||||
? validStyles |
* @returns {[Layer]} Removed layer |
||||
: ''; |
* @example |
||||
}, |
* const layer = property.getLayer(0); |
||||
|
* property.removeLayer(layer); |
||||
|
*/ |
||||
|
removeLayer(layer) { |
||||
|
return this.get('layers').remove(layer); |
||||
|
} |
||||
|
|
||||
/** |
/** |
||||
* This method allows to customize layers returned from the target |
* Remove layer by index. |
||||
* @param {Object} target |
* @param {Number} index Index of the layer to remove |
||||
* @return {Array} Should return an array of layers |
* @returns {[Layer]|null} Removed layer |
||||
* @example |
* @example |
||||
* // return example
|
* property.removeLayerAt(0); |
||||
* [ |
*/ |
||||
* { |
removeLayerAt(index = 0) { |
||||
* properties: [ |
const layer = this.getLayer(index); |
||||
* { property: 'width', ... } |
return layer ? this.removeLayer(layer) : null; |
||||
* { property: 'height', ... } |
} |
||||
* ] |
|
||||
* } |
/** |
||||
* ] |
* Get the layer label. The label can be customized with the `layerLabel` property. |
||||
|
* @param {[Layer]} layer |
||||
|
* @returns {String} |
||||
|
* @example |
||||
|
* const layer = this.getLayer(1); |
||||
|
* const label = this.getLayerLabel(layer); |
||||
|
*/ |
||||
|
getLayerLabel(layer) { |
||||
|
let result = ''; |
||||
|
|
||||
|
if (layer) { |
||||
|
const layerLabel = this.get('layerLabel'); |
||||
|
const values = layer.getValues(); |
||||
|
const index = layer.getIndex(); |
||||
|
|
||||
|
if (layerLabel) { |
||||
|
result = layerLabel(layer, { index, values, property: this }); |
||||
|
} else { |
||||
|
const parts = []; |
||||
|
this.getProperties().map(prop => { |
||||
|
parts.push(values[prop.getId()]); |
||||
|
}); |
||||
|
result = parts.filter(Boolean).join(' '); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get style object from the layer. |
||||
|
* @param {[Layer]} layer |
||||
|
* @param {Object} [opts={}] Options |
||||
|
* @param {Boolean} [opts.camelCase] Return property names in camelCase. |
||||
|
* @param {Object} [opts.number] Limit the result of the number types, eg. `number: { min: -3, max: 3 }` |
||||
|
* @returns {Object} Style object |
||||
|
*/ |
||||
|
getStyleFromLayer(layer, opts = {}) { |
||||
|
const join = this.__getJoin(); |
||||
|
const joinLayers = this.__getJoinLayers(); |
||||
|
const toStyle = this.get('toStyle'); |
||||
|
const name = this.getName(); |
||||
|
const values = layer.getValues(); |
||||
|
let style; |
||||
|
|
||||
|
if (toStyle) { |
||||
|
style = toStyle(values, { join, joinLayers, name, layer, property: this }); |
||||
|
} else { |
||||
|
const result = this.getProperties().map(prop => { |
||||
|
const name = prop.getName(); |
||||
|
const val = values[prop.getId()]; |
||||
|
let value = isUndefined(val) ? prop.getDefaultValue() : val; |
||||
|
|
||||
|
// Limit number values if necessary (useful for previews)
|
||||
|
if (opts.number && isNumberType(prop.getType())) { |
||||
|
const newVal = prop.parseValue(val, opts.number); |
||||
|
value = `${newVal.value}${newVal.unit}`; |
||||
|
} |
||||
|
|
||||
|
return { name, value }; |
||||
|
}); |
||||
|
style = this.isDetached() |
||||
|
? result.reduce((acc, item) => { |
||||
|
acc[item.name] = item.value; |
||||
|
return acc; |
||||
|
}, {}) |
||||
|
: { |
||||
|
[this.getName()]: result.map(r => r.value).join(join), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
return opts.camelCase |
||||
|
? Object.keys(style).reduce((res, key) => { |
||||
|
res[camelCase(key)] = style[key]; |
||||
|
return res; |
||||
|
}, {}) |
||||
|
: style; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get preview style object from the layer. |
||||
|
* If the property has `preview: false` the returned object will be empty. |
||||
|
* @param {[Layer]} layer |
||||
|
* @param {Object} [opts={}] Options. Same of `getStyleFromLayer` |
||||
|
* @returns {Object} Style object |
||||
|
*/ |
||||
|
getStylePreview(layer, opts = {}) { |
||||
|
let result = {}; |
||||
|
const preview = this.get('preview'); |
||||
|
|
||||
|
if (preview) { |
||||
|
result = this.getStyleFromLayer(layer, opts); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get layer separator. |
||||
|
* @return {RegExp} |
||||
|
*/ |
||||
|
getLayerSeparator() { |
||||
|
const sep = this.get('layerSeparator'); |
||||
|
return isString(sep) ? new RegExp(`${sep}(?![^\\(]*\\))`) : sep; |
||||
|
} |
||||
|
|
||||
|
__upProperties(prop, opts = {}) { |
||||
|
const layer = this.getSelectedLayer(); |
||||
|
if (!layer) return; |
||||
|
layer.upValues({ [prop.getId()]: prop.__getFullValue() }); |
||||
|
if (opts.__up) return; |
||||
|
this.__upTargetsStyleProps(opts); |
||||
|
} |
||||
|
|
||||
|
__upLayers(m, c, o) { |
||||
|
this.__upTargetsStyleProps(o || c); |
||||
|
} |
||||
|
|
||||
|
__upTargets(p, opts = {}) { |
||||
|
if (opts.__select) return; |
||||
|
return PropertyBase.prototype.__upTargets.call(this, p, opts); |
||||
|
} |
||||
|
|
||||
|
__upTargetsStyleProps(opts = {}) { |
||||
|
this.__upTargetsStyle(this.getStyleFromLayers(), opts); |
||||
|
} |
||||
|
|
||||
|
__upTargetsStyle(style, opts) { |
||||
|
return PropertyBase.prototype.__upTargetsStyle.call(this, style, opts); |
||||
|
} |
||||
|
|
||||
|
__upSelected({ noEvent } = {}, opts = {}) { |
||||
|
const sm = this.em.get('StyleManager'); |
||||
|
const selected = this.getSelectedLayer(); |
||||
|
const values = selected?.getValues(); |
||||
|
|
||||
|
// Update properties by layer value
|
||||
|
values && |
||||
|
this.getProperties().forEach(prop => { |
||||
|
const value = values[prop.getId()]; |
||||
|
prop.__getFullValue() !== value && prop.upValue(value, { ...opts, __up: true }); |
||||
|
}); |
||||
|
|
||||
|
!noEvent && sm.__trgEv(sm.events.layerSelect, { property: this }); |
||||
|
} |
||||
|
|
||||
|
_up(props, opts = {}) { |
||||
|
const { __layers = [], ...rest } = props; |
||||
|
// Detached props will update their layers later in sm.__upProp
|
||||
|
!this.isDetached() && this.__setLayers(__layers); |
||||
|
this.__upSelected({ noEvent: true }, opts); |
||||
|
return PropertyBase.prototype._up.call(this, rest, opts); |
||||
|
} |
||||
|
|
||||
|
__setLayers(newLayers = []) { |
||||
|
const layers = this.__getLayers(); |
||||
|
const layersNew = newLayers.map(values => ({ values })); |
||||
|
|
||||
|
if (layers.length === layersNew.length) { |
||||
|
layersNew.map((layer, n) => layers.at(n)?.upValues(layer.values)); |
||||
|
} else { |
||||
|
this.__getLayers().reset(layersNew); |
||||
|
} |
||||
|
|
||||
|
this.__upSelected({ noEvent: true }); |
||||
|
} |
||||
|
|
||||
|
__parseValue(value) { |
||||
|
const result = this.parseValue(value); |
||||
|
result.__layers = value |
||||
|
.split(VALUES_REG) |
||||
|
.map(v => v.trim()) |
||||
|
.map(v => this.__parseLayer(v)) |
||||
|
.filter(Boolean); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
__parseLayer(value) { |
||||
|
const parseFn = this.get('parseLayer'); |
||||
|
const values = value.split(PARTS_REG); |
||||
|
const properties = this.getProperties(); |
||||
|
return parseFn |
||||
|
? parseFn({ value, values }) |
||||
|
: properties.reduce((acc, prop, i) => { |
||||
|
const value = values[i]; |
||||
|
acc[prop.getId()] = !isUndefined(value) ? value : prop.getDefaultValue(); |
||||
|
return acc; |
||||
|
}, {}); |
||||
|
} |
||||
|
|
||||
|
__getLayersFromStyle(style = {}) { |
||||
|
if (!this.__styleHasProps(style)) return null; |
||||
|
|
||||
|
const name = this.getName(); |
||||
|
const props = this.getProperties(); |
||||
|
const sep = this.getLayerSeparator(); |
||||
|
const fromStyle = this.get('fromStyle'); |
||||
|
let result = fromStyle ? fromStyle(style, { property: this, separatorLayers: sep }) : []; |
||||
|
|
||||
|
if (!fromStyle) { |
||||
|
// Get layers from the main property
|
||||
|
const layers = this.__splitStyleName(style, name, sep) |
||||
|
.map(value => value.split(this.getSplitSeparator())) |
||||
|
.map(parts => { |
||||
|
const result = {}; |
||||
|
props.forEach((prop, i) => { |
||||
|
const value = parts[i]; |
||||
|
result[prop.getId()] = !isUndefined(value) ? value : prop.getDefaultValue(); |
||||
|
}); |
||||
|
return result; |
||||
|
}); |
||||
|
// Get layers from the inner properties
|
||||
|
props.forEach(prop => { |
||||
|
const id = prop.getId(); |
||||
|
this.__splitStyleName(style, prop.getName(), sep) |
||||
|
.map(value => ({ [id]: value || prop.getDefaultValue() })) |
||||
|
.forEach((inLayer, i) => { |
||||
|
layers[i] = layers[i] ? { ...layers[i], ...inLayer } : inLayer; |
||||
|
}); |
||||
|
}); |
||||
|
result = layers; |
||||
|
} |
||||
|
|
||||
|
return isArray(result) ? result : [result]; |
||||
|
} |
||||
|
|
||||
|
getStyle(opts) { |
||||
|
return this.getStyleFromLayers(opts); |
||||
|
} |
||||
|
|
||||
|
getStyleFromLayers(opts) { |
||||
|
let result = {}; |
||||
|
const name = this.getName(); |
||||
|
const layers = this.getLayers(); |
||||
|
const props = this.getProperties(); |
||||
|
const styles = layers.map(l => this.getStyleFromLayer(l, opts)); |
||||
|
styles.forEach(style => { |
||||
|
keys(style).map(key => { |
||||
|
if (!result[key]) result[key] = []; |
||||
|
result[key].push(style[key]); |
||||
|
}); |
||||
|
}); |
||||
|
keys(result).map(key => { |
||||
|
result[key] = result[key].join(this.__getJoinLayers()); |
||||
|
}); |
||||
|
|
||||
|
if (this.isDetached()) { |
||||
|
result[name] = ''; |
||||
|
!layers.length && |
||||
|
props.map(prop => { |
||||
|
result[prop.getName()] = ''; |
||||
|
}); |
||||
|
} else { |
||||
|
const style = props.reduce((acc, prop) => { |
||||
|
acc[prop.getName()] = ''; |
||||
|
return acc; |
||||
|
}, {}); |
||||
|
result[name] = result[name] || ''; |
||||
|
result = { ...result, ...style }; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
__getJoinLayers() { |
||||
|
const join = this.get('layerJoin'); |
||||
|
const sep = this.get('layerSeparator'); |
||||
|
|
||||
|
return join || (isString(sep) ? sep : join); |
||||
|
} |
||||
|
|
||||
|
__getFullValue() { |
||||
|
if (this.get('detached')) return ''; |
||||
|
const style = this.getStyleFromLayers(); |
||||
|
|
||||
|
return style[this.getName()]; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Extended |
||||
|
* @private |
||||
*/ |
*/ |
||||
getLayersFromTarget(target) { |
hasValue(opts = {}) { |
||||
return; |
const { noParent } = opts; |
||||
|
const parentValue = noParent && this.getParentTarget(); |
||||
|
return this.getLayers().length > 0 && !parentValue; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Extended |
||||
|
* @private |
||||
|
*/ |
||||
|
clear(opts = {}) { |
||||
|
this.__getLayers().reset(); |
||||
|
this.__upTargetsStyleProps(opts); |
||||
|
return PropertyBase.prototype.clear.call(this); |
||||
|
} |
||||
|
|
||||
|
__canClearProp() { |
||||
|
return false; |
||||
} |
} |
||||
}); |
} |
||||
|
|||||
@ -1,188 +1,120 @@ |
|||||
import { isString, each } from 'underscore'; |
import { View } from 'backbone'; |
||||
import Backbone from 'backbone'; |
import { keys } from 'underscore'; |
||||
import PropertiesView from './PropertiesView'; |
|
||||
|
export default class LayerView extends View { |
||||
export default Backbone.View.extend({ |
events() { |
||||
events: { |
return { |
||||
click: 'active', |
click: 'select', |
||||
'click [data-close-layer]': 'removeItem', |
'click [data-close-layer]': 'removeItem', |
||||
'mousedown [data-move-layer]': 'initSorter', |
'mousedown [data-move-layer]': 'initSorter', |
||||
'touchstart [data-move-layer]': 'initSorter' |
'touchstart [data-move-layer]': 'initSorter', |
||||
}, |
}; |
||||
|
} |
||||
|
|
||||
template(model) { |
template() { |
||||
const { pfx, ppfx, em } = this; |
const { pfx, ppfx, em } = this; |
||||
const label = `${em && em.t('styleManager.layer')} ${model.get('index')}`; |
const icons = em?.getConfig('icons'); |
||||
|
const iconClose = icons?.close || ''; |
||||
|
const iconMove = icons?.move || ''; |
||||
|
|
||||
return ` |
return ` |
||||
<div id="${pfx}move" class="${ppfx}no-touch-actions" data-move-layer> |
<div class="${pfx}label-wrp"> |
||||
<i class="fa fa-arrows"></i> |
<div id="${pfx}move" class="${ppfx}no-touch-actions" data-move-layer> |
||||
</div> |
${iconMove} |
||||
<div id="${pfx}label">${label}</div> |
</div> |
||||
<div id="${pfx}preview-box"> |
<div id="${pfx}label" data-label></div> |
||||
<div id="${pfx}preview" data-preview></div> |
<div id="${pfx}preview-box" class="${pfx}layer-preview" style="display: none" data-preview-box> |
||||
</div> |
<div id="${pfx}preview" class="${pfx}layer-preview-cnt" data-preview></div> |
||||
<div id="${pfx}close-layer" class="${pfx}btn-close" data-close-layer> |
</div> |
||||
⨯ |
<div id="${pfx}close-layer" class="${pfx}btn-close" data-close-layer> |
||||
|
${iconClose} |
||||
|
</div> |
||||
</div> |
</div> |
||||
<div id="${pfx}inputs" data-properties></div> |
<div id="${pfx}inputs" data-properties></div> |
||||
<div style="clear:both"></div> |
|
||||
`;
|
`;
|
||||
}, |
} |
||||
|
|
||||
initialize(o = {}) { |
initialize(o = {}) { |
||||
let model = this.model; |
const { model } = this; |
||||
this.stackModel = o.stackModel; |
const config = o.config || {}; |
||||
this.config = o.config || {}; |
this.em = config.em; |
||||
this.em = this.config.em; |
this.config = config; |
||||
this.pfx = this.config.stylePrefix || ''; |
this.sorter = o.sorter; |
||||
this.ppfx = this.config.pStylePrefix || ''; |
this.pfx = config.stylePrefix || ''; |
||||
this.sorter = o.sorter || null; |
this.ppfx = config.pStylePrefix || ''; |
||||
this.propsConfig = o.propsConfig || {}; |
this.propertyView = o.propertyView; |
||||
this.customPreview = o.onPreview; |
const pModel = this.propertyView.model; |
||||
this.listenTo(model, 'destroy remove', this.remove); |
this.listenTo(model, 'destroy remove', this.remove); |
||||
this.listenTo(model, 'change:active', this.updateVisibility); |
this.listenTo(model, 'change:values', this.updateLabel); |
||||
this.listenTo(model.get('properties'), 'change', this.updatePreview); |
this.listenTo(pModel, 'change:selectedLayer', this.updateVisibility); |
||||
|
|
||||
// For the sorter
|
// For the sorter
|
||||
model.view = this; |
model.view = this; |
||||
model.set({ droppable: 0, draggable: 1 }); |
model.set({ droppable: 0, draggable: 1 }); |
||||
this.$el.data('model', model); |
this.$el.data('model', model); |
||||
}, |
} |
||||
|
|
||||
/** |
initSorter() { |
||||
* Delegate sorting |
this.sorter?.startSort(this.el); |
||||
* @param {Event} e |
} |
||||
* */ |
|
||||
initSorter(e) { |
|
||||
if (this.sorter) this.sorter.startSort(this.el); |
|
||||
}, |
|
||||
|
|
||||
removeItem(ev) { |
removeItem(ev) { |
||||
ev && ev.stopPropagation(); |
ev && ev.stopPropagation(); |
||||
this.remove(); |
this.model.remove(); |
||||
}, |
} |
||||
|
|
||||
remove(opts = {}) { |
|
||||
const { model, props } = this; |
|
||||
const coll = model.collection; |
|
||||
const stackModel = this.stackModel; |
|
||||
|
|
||||
Backbone.View.prototype.remove.apply(this, arguments); |
|
||||
coll && coll.contains(model) && coll.remove(model); |
|
||||
|
|
||||
if (stackModel && stackModel.set) { |
|
||||
stackModel.set({ stackIndex: null }, { silent: true }); |
|
||||
!opts.fromTarget && stackModel.trigger('updateValue'); |
|
||||
} |
|
||||
|
|
||||
props && props.remove(); |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Default method for changing preview box |
|
||||
* @param {Collection} props |
|
||||
* @param {Element} $el |
|
||||
*/ |
|
||||
onPreview(value) { |
|
||||
const { stackModel } = this; |
|
||||
const detach = stackModel && stackModel.get('detached'); |
|
||||
const values = value.split(' '); |
|
||||
const lim = 3; |
|
||||
const result = []; |
|
||||
const resultObj = {}; |
|
||||
|
|
||||
this.model.get('properties').each((prop, index) => { |
|
||||
const property = prop.get('property'); |
|
||||
let value = detach ? prop.getFullValue() : values[index] || ''; |
|
||||
|
|
||||
if (value) { |
|
||||
if (prop.get('type') == 'integer') { |
|
||||
let valueInt = parseInt(value, 10); |
|
||||
let unit = value.replace(valueInt, ''); |
|
||||
valueInt = !isNaN(valueInt) ? valueInt : 0; |
|
||||
valueInt = valueInt > lim ? lim : valueInt; |
|
||||
valueInt = valueInt < -lim ? -lim : valueInt; |
|
||||
value = valueInt + unit; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
result.push(value); |
|
||||
resultObj[property] = value; |
|
||||
}); |
|
||||
|
|
||||
return detach ? resultObj : result.join(' '); |
|
||||
}, |
|
||||
|
|
||||
updatePreview() { |
|
||||
const stackModel = this.stackModel; |
|
||||
const customPreview = this.customPreview; |
|
||||
const previewEl = this.getPreviewEl(); |
|
||||
const value = this.model.getFullValue(); |
|
||||
const preview = customPreview |
|
||||
? customPreview(value) |
|
||||
: this.onPreview(value); |
|
||||
|
|
||||
if (preview && stackModel && previewEl) { |
select() { |
||||
const { style } = previewEl; |
this.model.select(); |
||||
if (isString(preview)) { |
} |
||||
style[stackModel.get('property')] = preview; |
|
||||
} else { |
|
||||
let prvStr = []; |
|
||||
each(preview, (val, prop) => prvStr.push(`${prop}:${val}`)); |
|
||||
previewEl.setAttribute('style', prvStr.join(';')); |
|
||||
} |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
getPropertiesWrapper() { |
getPropertiesWrapper() { |
||||
if (!this.propsWrapEl) { |
if (!this.propsWrapEl) this.propsWrapEl = this.el.querySelector('[data-properties]'); |
||||
this.propsWrapEl = this.el.querySelector('[data-properties]'); |
|
||||
} |
|
||||
return this.propsWrapEl; |
return this.propsWrapEl; |
||||
}, |
} |
||||
|
|
||||
getPreviewEl() { |
getPreviewEl() { |
||||
if (!this.previewEl) { |
if (!this.previewEl) this.previewEl = this.el.querySelector('[data-preview]'); |
||||
this.previewEl = this.el.querySelector('[data-preview]'); |
|
||||
} |
|
||||
return this.previewEl; |
return this.previewEl; |
||||
}, |
} |
||||
|
|
||||
|
getLabelEl() { |
||||
|
if (!this.labelEl) this.labelEl = this.el.querySelector('[data-label]'); |
||||
|
return this.labelEl; |
||||
|
} |
||||
|
|
||||
active() { |
updateLabel() { |
||||
const model = this.model; |
const { model } = this; |
||||
const collection = model.collection; |
const label = model.getLabel(); |
||||
collection.active(collection.indexOf(model)); |
this.getLabelEl().innerHTML = label; |
||||
}, |
|
||||
|
if (model.hasPreview()) { |
||||
|
const prvEl = this.getPreviewEl(); |
||||
|
const style = model.getStylePreview({ number: { min: -3, max: 3 } }); |
||||
|
const styleStr = keys(style) |
||||
|
.map(k => `${k}:${style[k]}`) |
||||
|
.join(';'); |
||||
|
prvEl.setAttribute('style', styleStr); |
||||
|
} |
||||
|
} |
||||
|
|
||||
updateVisibility() { |
updateVisibility() { |
||||
const pfx = this.pfx; |
const { pfx, model, propertyView } = this; |
||||
const wrapEl = this.getPropertiesWrapper(); |
const wrapEl = this.getPropertiesWrapper(); |
||||
const active = this.model.get('active'); |
const isSelected = model.isSelected(); |
||||
wrapEl.style.display = active ? '' : 'none'; |
wrapEl.style.display = isSelected ? '' : 'none'; |
||||
this.$el[active ? 'addClass' : 'removeClass'](`${pfx}active`); |
this.$el[isSelected ? 'addClass' : 'removeClass'](`${pfx}active`); |
||||
}, |
isSelected && wrapEl.appendChild(propertyView.props.el); |
||||
|
} |
||||
|
|
||||
render() { |
render() { |
||||
const propsConfig = this.propsConfig; |
const { el, pfx, model } = this; |
||||
const { model, el, pfx } = this; |
el.innerHTML = this.template(); |
||||
const preview = model.get('preview'); |
el.className = `${pfx}layer`; |
||||
const properties = new PropertiesView({ |
if (model.hasPreview()) { |
||||
collection: model.get('properties'), |
el.querySelector(`[data-preview-box]`).style.display = ''; |
||||
config: { ...this.config, fromLayer: 1 }, |
} |
||||
target: propsConfig.target, |
this.updateLabel(); |
||||
customValue: propsConfig.customValue, |
|
||||
propTarget: propsConfig.propTarget, |
|
||||
onChange: propsConfig.onChange |
|
||||
}); |
|
||||
const propsEl = properties.render().el; |
|
||||
|
|
||||
el.innerHTML = this.template(model); |
|
||||
el.className = `${pfx}layer${!preview ? ` ${pfx}no-preview` : ''}`; |
|
||||
this.props = properties; |
|
||||
this.getPropertiesWrapper().appendChild(propsEl); |
|
||||
this.updateVisibility(); |
this.updateVisibility(); |
||||
this.updatePreview(); |
|
||||
return this; |
return this; |
||||
} |
} |
||||
}); |
} |
||||
|
|||||