Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ globals:
MSImmutableStarShape: false
MSImmutableTriangleShape: false
MSImmutableRectangleShape: false
MSSliceLayer: false
MSImmutableSliceLayer: false
MSExportFormat: false

rules:
###########
Expand Down
4 changes: 3 additions & 1 deletion CHANGELOG.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"unreleased": [
"[New] Add `verticalAlignment` property to Text",
"[New] Add support for Slices",
"[New] Add `exportFormats` property on Layer",
"[Improved] No need to specify the type when there is no choice (like Document.pages can only contain Pages, Layer.exportFormats can only contain ExportFormats, etc.)",
"[New] Add UI.getInputFromUser method and deprecate the other input methods",
"[New] Add some `getParent*` methods on Layer"
],
Expand Down
3 changes: 3 additions & 0 deletions Source/dom/WrappedObject.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ export class WrappedObject {
* @param {Object} object - The Sketch model object to wrap.
*/
static fromNative(sketchObject) {
if (!sketchObject) {
return sketchObject
}
return new this({
sketchObject,
})
Expand Down
2 changes: 2 additions & 0 deletions Source/dom/enums.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ export const Types = {
SharedStyle: 'SharedStyle',
DataOverride: 'DataOverride',
ShapePath: 'ShapePath',
Slice: 'Slice',
ExportFormat: 'ExportFormat',
}
1 change: 1 addition & 0 deletions Source/dom/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { Page } from './layers/Page'
export { SymbolMaster } from './layers/SymbolMaster'
export { SymbolInstance } from './layers/SymbolInstance'
export { HotSpot } from './layers/HotSpot'
export { Slice } from './layers/Slice'

export { Types } from './enums'
export { wrapObject as fromNative } from './wrapNativeObject'
Expand Down
4 changes: 2 additions & 2 deletions Source/dom/layers/Group.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { toArray } from 'util'
import { DefinedPropertiesKey } from '../WrappedObject'
import { StyledLayer } from './StyledLayer'
import { Rectangle } from '../models/Rectangle'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { toArray } from '../utils'
import { wrapNativeObject, wrapObject } from '../wrapNativeObject'

/**
Expand Down Expand Up @@ -45,7 +45,7 @@ export class Group extends StyledLayer {
if (this.isImmutable()) {
return this
}
this._object.resizeToFitChildrenWithOption_(0)
this._object.fixGeometryWithOptions(0)
return this
}
}
Expand Down
2 changes: 2 additions & 0 deletions Source/dom/layers/HotSpot.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ HotSpot.type = Types.HotSpot
HotSpot[DefinedPropertiesKey] = { ...Layer[DefinedPropertiesKey] }
Factory.registerClass(HotSpot, MSHotspotLayer)
Factory.registerClass(HotSpot, MSImmutableHotspotLayer)

delete HotSpot[DefinedPropertiesKey].exportFormats
3 changes: 1 addition & 2 deletions Source/dom/layers/Image.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ImageData } from '../models/ImageData'
import { Rectangle } from '../models/Rectangle'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { wrapObject } from '../wrapNativeObject'

/**
* Represents an image layer.
Expand Down Expand Up @@ -36,7 +35,7 @@ Factory.registerClass(Image, MSImmutableBitmapLayer)

Image.define('image', {
get() {
return wrapObject(this._object.image())
return ImageData.fromNative(this._object.image())
},
set(image) {
if (this.isImmutable()) {
Expand Down
24 changes: 24 additions & 0 deletions Source/dom/layers/Layer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { toArray } from 'util'
import { WrappedObject, DefinedPropertiesKey } from '../WrappedObject'
import { Factory } from '../Factory'
import { Rectangle } from '../models/Rectangle'
import { wrapObject, wrapNativeObject } from '../wrapNativeObject'
import { Flow } from '../models/Flow'
import { ExportFormat } from '../models/ExportFormat'
import { Types } from '../enums'

/**
* Abstract class that represents a Sketch layer.
Expand Down Expand Up @@ -283,3 +286,24 @@ Layer.define('locked', {
this._object.setIsLocked(locked)
},
})

Layer.define('exportFormats', {
get() {
return toArray(this._object.exportOptions().exportFormats() || []).map(
ExportFormat.fromNative.bind(ExportFormat)
)
},
set(exportFormats) {
if (this.isImmutable()) {
return
}

this._object
.exportOptions()
.setExportFormats(
toArray(exportFormats).map(
e => wrapObject(e, Types.ExportFormat).sketchObject
)
)
},
})
1 change: 1 addition & 0 deletions Source/dom/layers/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ delete Page[DefinedPropertiesKey].flow
delete Page[DefinedPropertiesKey].style
delete Page[DefinedPropertiesKey].locked
delete Page[DefinedPropertiesKey].hidden
delete Page[DefinedPropertiesKey].exportFormats

// override setting up the parent as it's needs to a be a Document
Page.define('parent', {
Expand Down
28 changes: 28 additions & 0 deletions Source/dom/layers/Slice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { DefinedPropertiesKey } from '../WrappedObject'
import { Layer } from './Layer'
import { Rectangle } from '../models/Rectangle'
import { Types } from '../enums'
import { Factory } from '../Factory'

/**
* Represents a slice.
*/
export class Slice extends Layer {
constructor(group = {}) {
if (!group.sketchObject) {
// eslint-disable-next-line no-param-reassign
group.sketchObject = Factory.createNative(Slice)
.alloc()
.initWithFrame(new Rectangle(0, 0, 100, 100).asCGRect())
}

super(group)
}
}

Slice.type = Types.Slice
Slice[DefinedPropertiesKey] = { ...Layer[DefinedPropertiesKey] }
Factory.registerClass(Slice, MSSliceLayer)
Factory.registerClass(Slice, MSImmutableSliceLayer)

delete Slice[DefinedPropertiesKey].flow
2 changes: 1 addition & 1 deletion Source/dom/layers/StyledLayer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { isNativeObject } from 'util'
import { DefinedPropertiesKey } from '../WrappedObject'
import { Factory } from '../Factory'
import { Layer } from './Layer'
import { Style } from '../style/Style'
import { isNativeObject } from '../utils'
import { SharedStyle } from '../models/SharedStyle'
import { wrapObject } from '../wrapNativeObject'

Expand Down
2 changes: 1 addition & 1 deletion Source/dom/layers/SymbolInstance.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { toArray } from 'util'
import { DefinedPropertiesKey } from '../WrappedObject'
import { StyledLayer } from './StyledLayer'
import { Rectangle } from '../models/Rectangle'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { wrapObject } from '../wrapNativeObject'
import { toArray } from '../utils'
import { Override } from '../models/Override'
import { ImageData } from '../models/ImageData'

Expand Down
2 changes: 1 addition & 1 deletion Source/dom/layers/SymbolMaster.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { toArray } from 'util'
import { DefinedPropertiesKey } from '../WrappedObject'
import { Artboard } from './Artboard'
import { Rectangle } from '../models/Rectangle'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { wrapObject } from '../wrapNativeObject'
import { toArray } from '../utils'

/**
* A Sketch symbol master.
Expand Down
2 changes: 1 addition & 1 deletion Source/dom/layers/__tests__/Image.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ test('should create an empty image', (context, document) => {
log(image)
expect(image.type).toBe('Image')
expect(image.parent).toEqual(page)
expect(image.image).toBe(undefined)
expect(image.image).toBe(null)
})
21 changes: 21 additions & 0 deletions Source/dom/layers/__tests__/Layer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,27 @@ test('should lock the layer', () => {
expect(group.locked).toBe(true)
})

test('should change the exportFormats', () => {
const group = new Group()
expect(group.exportFormats).toEqual([])

group.exportFormats = [
{
size: '2x',
suffix: '@2x',
},
]
expect(group.exportFormats.map(e => e.toJSON())).toEqual([
{
type: 'ExportFormat',
fileFormat: 'png',
prefix: undefined,
suffix: '@2x',
size: '2x',
},
])
})

test('should get the different parents', (context, document) => {
const page = document.selectedPage
expect(page.parent).toEqual(document)
Expand Down
10 changes: 10 additions & 0 deletions Source/dom/layers/__tests__/Slice.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* globals expect, test */

import { Slice } from '../..'

test('should create an slice', () => {
const slice = new Slice({ name: 'Test' })
// check that an artboard can be logged
log(slice)
expect(slice.type).toBe('Slice')
})
5 changes: 3 additions & 2 deletions Source/dom/models/Document.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { toArray } from 'util'
import { WrappedObject, DefinedPropertiesKey } from '../WrappedObject'
import { Page } from '../layers/Page'
import { Selection } from './Selection'
import { toArray, getURLFromPath } from '../utils'
import { getURLFromPath } from '../utils'
import { wrapObject } from '../wrapNativeObject'
import { Types } from '../enums'
import { Factory } from '../Factory'
Expand Down Expand Up @@ -388,7 +389,7 @@ Document.define('pages', {
this._object.removePages_detachInstances(this._object.pages(), true)

toArray(pages)
.map(wrapObject)
.map(p => wrapObject(p, Types.Page))
.forEach(page => {
page.parent = this // eslint-disable-line
})
Expand Down
109 changes: 109 additions & 0 deletions Source/dom/models/ExportFormat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { DefinedPropertiesKey, WrappedObject } from '../WrappedObject'
import { Types } from '../enums'
import { Factory } from '../Factory'

/**
* An MSExportFormat. This is not exposed, only used by the exportOptions of the Layer
*/
export class ExportFormat extends WrappedObject {
constructor(exportFormat = {}) {
if (!exportFormat.sketchObject) {
// eslint-disable-next-line no-param-reassign
exportFormat.sketchObject = MSExportFormat.formatWithScale_name_fileFormat(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can use [MSExportOptions nextMostAppropriateExportFormatToAdd:] here, so as not to hardcode the values for the initial exportFormat?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum I'm not sure. I get why we want that in the UI but IMO it would be weird to have a different fileformat depending on which one was added before

1,
'',
'png'
)
}

super(exportFormat)
}
}

ExportFormat.type = Types.ExportFormat
ExportFormat[DefinedPropertiesKey] = { ...WrappedObject[DefinedPropertiesKey] }
Factory.registerClass(ExportFormat, MSExportFormat)

delete ExportFormat[DefinedPropertiesKey].id

ExportFormat.define('fileFormat', {
get() {
return String(this._object.fileFormat())
},
set(fileFormat) {
if (!MSExportFormat.validFormats().containsObject(fileFormat)) {
throw new Error('File format not supported')
}

this._object.setFileFormat(fileFormat)
},
})

ExportFormat.define('prefix', {
get() {
if (this._object.namingScheme() == 0) {
// we have a suffix
return undefined
}
return String(this._object.name())
},
set(name) {
this._object.setNamingScheme(1)
this._object.setName(name)
},
})

ExportFormat.define('suffix', {
get() {
if (this._object.namingScheme() == 1) {
// we have a prefix
return undefined
}
return String(this._object.name())
},
set(name) {
this._object.setNamingScheme(0)
this._object.setName(name)
},
})

ExportFormat.define('size', {
get() {
switch (this._object.visibleScaleType()) {
case 0:
return `${this._object.scale()}x`
case 1:
return `${this._object.absoluteSize()}w`
case 2:
return `${this._object.absoluteSize()}h`
default:
throw new Error('unknown visibleScaleType')
}
},
set(_size) {
const size = String(_size)
if (
size.endsWith('w') ||
size.endsWith('width') ||
(size.endsWith('px') && this._object.visibleScaleType() == 1)
) {
this._object.setVisibleScaleType(1)
this._object.setScale(0)
this._object.setAbsoluteSize(parseInt(size, 10))
} else if (
size.endsWith('h') ||
size.endsWith('height') ||
(size.endsWith('px') && this._object.visibleScaleType() == 2)
) {
this._object.setVisibleScaleType(2)
this._object.setScale(0)
this._object.setAbsoluteSize(parseInt(size, 10))
} else if (size.endsWith('x')) {
this._object.setVisibleScaleType(0)
this._object.setScale(parseInt(size, 10))
this._object.setAbsoluteSize(0)
} else {
throw new Error('could not parse the size')
}
},
})
3 changes: 2 additions & 1 deletion Source/dom/models/Flow.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { isNativeObject } from 'util'
import { DefinedPropertiesKey, WrappedObject } from '../WrappedObject'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { isWrappedObject, isNativeObject } from '../utils'
import { isWrappedObject } from '../utils'
import { wrapObject } from '../wrapNativeObject'

// Mapping between animation type names and values.
Expand Down
3 changes: 2 additions & 1 deletion Source/dom/models/ImageData.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { isNativeObject } from 'util'
import { DefinedPropertiesKey, WrappedObject } from '../WrappedObject'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { isWrappedObject, isNativeObject } from '../utils'
import { isWrappedObject } from '../utils'

/**
* An MSImageData. This is not exposed, only used by Image
Expand Down
3 changes: 2 additions & 1 deletion Source/dom/models/Library.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { toArray } from 'util'
import { WrappedObject, DefinedPropertiesKey } from '../WrappedObject'
import { Document } from './Document'
import { toArray, getURLFromPath, getDocumentData } from '../utils'
import { getURLFromPath, getDocumentData } from '../utils'
import { Types } from '../enums'
import { Factory } from '../Factory'
import { wrapObject } from '../wrapNativeObject'
Expand Down
Loading