Skip to content

Commit e36ec0e

Browse files
macintoshhelpermathieudutour
authored andcommitted
Prototype flows with HotSpot and Artboard ids (#436)
* implement viewport preset prop for <Artboard> * revert API.md table formatting * implement HotSpot component with artboard stable seeds * add Artboard isHome prop for prototype flow * add prototype docs for HotSpot and Artboard * add HotSpot to docs contents * fix HotSpot propTypes * Port HotSpot to View & get objectID from index * remove redundant HotSpot from docs * fix generateID index * revert Artboard id prop & re-add generateID hardcoded * clean up prototype docs
1 parent f702eee commit e36ec0e

File tree

8 files changed

+104
-5
lines changed

8 files changed

+104
-5
lines changed

__tests__/jest/jsonUtils/models.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ describe('generateID', () => {
1111
it('is unique', () => {
1212
expect(generateID()).not.toBe(generateID());
1313
});
14+
it('seed generates different ID', () => {
15+
expect(generateID('test')).not.toBe(generateID('test'));
16+
});
17+
it('hardcoded seed generates same ID', () => {
18+
expect(generateID('test', true)).toBe(generateID('test', true));
19+
});
1420
});
1521

1622
const BLACK = {

docs/API.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ Wrapper for Sketch's Artboards. Requires a [`<Page>`](#page) component as a pare
144144
| `children` | `Node` | | |
145145
| `style` | [`Style`](/docs/styling.md) | | |
146146
| `viewport` | `Viewport` | | Object: { name: string, width: number, height: number} |
147+
| `isHome` | `Boolean` | | Is prototype home screen if true |
147148

148149
#### Examples
149150

@@ -328,8 +329,11 @@ View primitives
328329
| `name` | `String` | | The name to be displayed in the Sketch Layer List |
329330
| `children` | `Node` | | |
330331
| `style` | [`Style`](/docs/styling.md) | | |
332+
| `flow` | `Flow` | | Object: { target: string, targetId: string, animationType: string } |
331333

332-
#### Example
334+
#### Examples
335+
336+
**Example with children**
333337

334338
```js
335339
<View
@@ -346,6 +350,37 @@ View primitives
346350
</View>
347351
```
348352

353+
**Example using `flow` prop for prototyping destination**
354+
355+
```js
356+
<Document>
357+
<Artboard name="Home">
358+
<View
359+
name="Menu Button"
360+
style={{
361+
height: 100,
362+
backgroundColor: '#01ffae',
363+
}}
364+
flow={{
365+
target: 'menu' // From <Artboard name" or can be "back"
366+
// targetId: uuid (can be used to reference existing artboards/uuids)
367+
// animationType: string (constants can be used from require('sketch') API, or hardcoded)
368+
}}
369+
>
370+
<Text>Open menu!</Text>
371+
</View>
372+
</Artboard>
373+
<Artboard name="Menu">
374+
<View
375+
name="Go back"
376+
flow={{ target: 'back' }} /* "back" used instead of <Artboard> id */
377+
>
378+
<Text>Go back!</Text>
379+
</View>
380+
</Artboard>
381+
</Document>
382+
```
383+
349384
## Platform
350385

351386
### `OS`

src/components/Artboard.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export default class Artboard extends React.Component {
1717
// TODO(lmr): do some nice warning stuff like RN does
1818
style: or([PropTypes.shape(ViewStylePropTypes), PropTypes.number]),
1919
name: PropTypes.string,
20+
isHome: PropTypes.bool,
2021
children: PropTypes.node,
2122
viewport: PropTypes.shape(ViewportPropTypes),
2223
};
@@ -31,6 +32,7 @@ export default class Artboard extends React.Component {
3132
style={StyleSheet.flatten(this.props.style)}
3233
name={this.props.name}
3334
viewport={this.props.viewport}
35+
isHome={this.props.isHome}
3436
>
3537
{this.props.children}
3638
</artboard>

src/components/View.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export const ViewPropTypes = {
1919
...ShadowsPropTypes,
2020
}),
2121
),
22+
flow: PropTypes.shape({
23+
targetId: PropTypes.string,
24+
target: PropTypes.string,
25+
animationType: PropTypes.string,
26+
}),
2227
children: PropTypes.node,
2328
};
2429

@@ -37,6 +42,7 @@ export default class View extends React.Component {
3742
style={StyleSheet.flatten(this.props.style)}
3843
resizingConstraint={this.props.resizingConstraint}
3944
shadows={this.props.shadows}
45+
flow={this.props.flow}
4046
>
4147
{this.props.children}
4248
</view>

src/jsonUtils/hotspotLayer.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// @flow
2+
import type { SJObjectId } from 'sketchapp-json-flow-types';
3+
4+
import { generateID } from './models';
5+
6+
type SJFlow = {
7+
_class: 'MSImmutableFlowConnection',
8+
animationType: number,
9+
destinationArtboardID?: SJObjectId | 'back',
10+
};
11+
12+
const animationTypes = {
13+
none: -1,
14+
slideFromRight: 0,
15+
slideFromLeft: 1,
16+
slideFromBottom: 2,
17+
slideFromTop: 3,
18+
};
19+
20+
const BackTarget = 'back';
21+
22+
const getArtboard = target => {
23+
if (target === BackTarget) {
24+
return BackTarget;
25+
}
26+
return generateID(`artboard:${target}`, true);
27+
};
28+
29+
const hotspotLayer = ({
30+
targetId,
31+
target,
32+
animationType,
33+
}: {
34+
targetId?: string,
35+
target?: string,
36+
animationType?: string,
37+
}): { flow: SJFlow } => ({
38+
flow: {
39+
_class: 'MSImmutableFlowConnection',
40+
animationType: (animationType && animationTypes[animationType]) || -1,
41+
destinationArtboardID: target ? getArtboard(target) : targetId,
42+
},
43+
});
44+
45+
export default hotspotLayer;

src/jsonUtils/models.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,18 @@ function generateIdNumber() {
5757
// Keep track of previous seeds
5858
const previousSeeds = {};
5959

60-
export function generateID(seed?: ?string): string {
61-
let _seed: ?string;
60+
export function generateID(seed?: ?string, hardcoded?: boolean): string {
61+
let _seed: ?string = seed;
6262

6363
if (seed) {
6464
if (!previousSeeds[seed]) {
6565
previousSeeds[seed] = 0;
6666
}
6767
previousSeeds[seed] += 1;
6868

69-
_seed = `${seed}${previousSeeds[seed]}`;
69+
if (!hardcoded) {
70+
_seed = `${seed}${previousSeeds[seed]}`;
71+
}
7072
}
7173

7274
return e7(_seed);

src/renderers/ArtboardRenderer.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default class ArtboardRenderer extends SketchRenderer {
1818

1919
return {
2020
_class: 'artboard',
21-
do_objectID: generateID(),
21+
do_objectID: generateID(`artboard:${props.name}`, true),
2222
frame: makeRect(layout.left, layout.top, layout.width, layout.height),
2323
// "layerListExpandedType": 0,
2424
name: props.name || 'Artboard',
@@ -27,6 +27,7 @@ export default class ArtboardRenderer extends SketchRenderer {
2727
isVisible: true,
2828
backgroundColor: color || makeColorFromCSS('white'),
2929
hasBackgroundColor: color !== undefined,
30+
...(props.isHome && { isFlowHome: true }),
3031
...(props.viewport && {
3132
presetDictionary: {
3233
allowResizedMatching: 0,

src/renderers/SketchRenderer.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @flow
22
import layerGroup from '../jsonUtils/layerGroup';
3+
import hotspotLayer from '../jsonUtils/hotspotLayer';
34
import type { LayoutInfo, ViewStyle, TextStyle, TreeNode } from '../types';
45
import processTransform from '../utils/processTransform';
56

@@ -31,6 +32,7 @@ export default class SketchRenderer {
3132
),
3233
name: props.name || this.getDefaultGroupName(props),
3334
...transform,
35+
...(props.flow && hotspotLayer(props.flow)),
3436
};
3537
}
3638

0 commit comments

Comments
 (0)