playroom 0.34.2 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # playroom
2
2
 
3
+ ## 0.35.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ad60e01: Add support for specifying default subsets of themes and screen widths via the config.
8
+
9
+ #### Example usage
10
+
11
+ ```js
12
+ // playroom.config.js
13
+ module.exports = {
14
+ ...,
15
+ defaultVisibleWidths: [
16
+ // subset of widths to display on first load
17
+ ],
18
+ defaultVisibleThemes: [
19
+ // subset of themes to display on first load
20
+ ],
21
+ }
22
+ ```
23
+
24
+ - f45dd04: Add ability to customise tab titles via a "Title" section in the settings panel.
25
+
26
+ ### Patch Changes
27
+
28
+ - f491105: Fix bug in "Wrap selection in tag" command that caused the start cursor to occasionally be placed in the wrong postion.
29
+
3
30
  ## 0.34.2
4
31
 
5
32
  ### Patch Changes
package/README.md CHANGED
@@ -80,6 +80,12 @@ module.exports = {
80
80
  // Custom webpack config goes here...
81
81
  }),
82
82
  iframeSandbox: 'allow-scripts',
83
+ defaultVisibleWidths: [
84
+ // subset of widths to display on first load
85
+ ],
86
+ defaultVisibleThemes: [
87
+ // subset of themes to display on first load
88
+ ],
83
89
  };
84
90
  ```
85
91
 
@@ -216,6 +216,21 @@ describe('Keymaps', () => {
216
216
  `);
217
217
  });
218
218
 
219
+ it('should ignore surrounding whitespace when wrapping a single line selection', () => {
220
+ typeCode(' ');
221
+ typeCode('{leftArrow}');
222
+ selectToEndOfLine();
223
+
224
+ typeCode(`{shift+${modifierKey}+,}`);
225
+ typeCode('span');
226
+
227
+ assertCodePaneContains(dedent`
228
+ <span> <div>First line</div></span>
229
+ <div>Second line</div>
230
+ <div>Third line</div>
231
+ `);
232
+ });
233
+
219
234
  it('should wrap a multi-line selection', () => {
220
235
  typeCode('{shift+downArrow}');
221
236
  selectToEndOfLine();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playroom",
3
- "version": "0.34.2",
3
+ "version": "0.35.0",
4
4
  "description": "Design with code, powered by your own component library",
5
5
  "main": "utils/index.js",
6
6
  "types": "utils/index.d.ts",
@@ -72,6 +72,7 @@
72
72
  "query-string": "^7.1.3",
73
73
  "re-resizable": "^6.9.9",
74
74
  "react-docgen-typescript": "^2.2.2",
75
+ "react-helmet": "^6.1.0",
75
76
  "react-use": "^17.4.0",
76
77
  "read-pkg-up": "^7.0.1",
77
78
  "scope-eval": "^1.0.0",
@@ -87,6 +88,7 @@
87
88
  "@changesets/cli": "^2.25.2",
88
89
  "@octokit/rest": "^19.0.5",
89
90
  "@types/jest": "^29.2.4",
91
+ "@types/react-helmet": "^6.1.6",
90
92
  "concurrently": "^7.6.0",
91
93
  "cypress": "^12.0.2",
92
94
  "eslint": "^8.44.0",
@@ -34,12 +34,11 @@ export default class CatchErrors extends Component<Props, State> {
34
34
  }
35
35
 
36
36
  // Ensure the stack only contains user-provided components
37
- const componentStack = errorInfo
38
- ? errorInfo.componentStack
39
- .split('\n')
40
- .filter((line: string) => /RenderCode/.test(line))
41
- .map((line: string) => line.replace(/ \(created by .*/g, ''))
42
- : [];
37
+ const componentStack =
38
+ errorInfo?.componentStack
39
+ ?.split('\n')
40
+ .filter((line: string) => /RenderCode/.test(line))
41
+ .map((line: string) => line.replace(/ \(created by .*/g, '')) ?? [];
43
42
 
44
43
  // Ignore the RenderCode container component
45
44
  const lines = componentStack.slice(0, componentStack.length - 1);
@@ -36,10 +36,13 @@ export const wrapInTag = (cm: Editor) => {
36
36
  existingIndent,
37
37
  });
38
38
 
39
+ const startCursorCharacterPosition =
40
+ from.ch + 1 + (isMultiLineSelection ? existingIndent : 0);
39
41
  const newStartCursor = new Pos(
40
42
  from.line + linesAdded,
41
- from.ch + existingIndent + 1
43
+ startCursorCharacterPosition
42
44
  );
45
+
43
46
  const newEndCursor = isMultiLineSelection
44
47
  ? new Pos(to.line + linesAdded + 2, from.ch + existingIndent + 2)
45
48
  : new Pos(to.line + linesAdded, to.ch + 4);
@@ -90,7 +90,7 @@ export default ({ availableWidths, availableThemes }: FramesPanelProps) => {
90
90
 
91
91
  return (
92
92
  <ToolbarPanel data-testid="frame-panel">
93
- <Stack space="large" dividers>
93
+ <Stack space="xxxlarge">
94
94
  <div data-testid="widthsPreferences">
95
95
  <FrameHeading
96
96
  showReset={hasFilteredWidths}
@@ -9,10 +9,12 @@ import CatchErrors from './CatchErrors/CatchErrors';
9
9
  import RenderCode from './RenderCode/RenderCode';
10
10
 
11
11
  import * as styles from './Preview.css';
12
+ import { Helmet } from 'react-helmet';
12
13
 
13
14
  interface PreviewState {
14
15
  code?: string;
15
16
  themeName?: string;
17
+ title?: string;
16
18
  }
17
19
 
18
20
  export interface PreviewProps {
@@ -25,7 +27,7 @@ export interface PreviewProps {
25
27
  }>;
26
28
  }
27
29
  export default ({ themes, components, FrameComponent }: PreviewProps) => {
28
- const { themeName, code } = useParams((rawParams): PreviewState => {
30
+ const { themeName, code, title } = useParams((rawParams): PreviewState => {
29
31
  if (rawParams.code) {
30
32
  const result = JSON.parse(
31
33
  lzString.decompressFromEncodedURIComponent(String(rawParams.code)) ?? ''
@@ -34,6 +36,7 @@ export default ({ themes, components, FrameComponent }: PreviewProps) => {
34
36
  return {
35
37
  code: compileJsx(result.code),
36
38
  themeName: result.theme,
39
+ title: result.title,
37
40
  };
38
41
  }
39
42
 
@@ -44,6 +47,11 @@ export default ({ themes, components, FrameComponent }: PreviewProps) => {
44
47
 
45
48
  return (
46
49
  <CatchErrors code={code}>
50
+ <Helmet>
51
+ <title>
52
+ {title ? `${title} | Playroom Preview` : 'Playroom Preview'}
53
+ </title>
54
+ </Helmet>
47
55
  <div className={styles.renderContainer}>
48
56
  <FrameComponent
49
57
  themeName={themeName || '__PLAYROOM__NO_THEME__'}
@@ -29,7 +29,7 @@ export default ({ themes, visibleThemes }: PreviewPanelProps) => {
29
29
 
30
30
  return (
31
31
  <ToolbarPanel data-testid="preview-panel">
32
- <Stack space="medium">
32
+ <Stack space="xxlarge">
33
33
  <Heading as="h4" level="3">
34
34
  Preview
35
35
  </Heading>
@@ -98,3 +98,22 @@ export const label = style([
98
98
  },
99
99
  },
100
100
  ]);
101
+
102
+ export const textField = style([
103
+ sprinkles({
104
+ font: 'large',
105
+ width: 'full',
106
+ paddingX: 'large',
107
+ boxSizing: 'border-box',
108
+ borderRadius: 'medium',
109
+ }),
110
+ {
111
+ color: colorPaletteVars.foreground.neutral,
112
+ height: vars.touchableSize,
113
+ background: colorPaletteVars.background.surface,
114
+ '::placeholder': {
115
+ color: colorPaletteVars.foreground.neutralSoft,
116
+ },
117
+ border: `1px solid ${colorPaletteVars.border.standard}`,
118
+ },
119
+ ]);
@@ -1,4 +1,5 @@
1
1
  import React, { useContext, type ReactChild } from 'react';
2
+ import { Helmet } from 'react-helmet';
2
3
  import { Heading } from '../Heading/Heading';
3
4
  import { ToolbarPanel } from '../ToolbarPanel/ToolbarPanel';
4
5
  import {
@@ -73,101 +74,144 @@ const KeyboardShortcut = ({
73
74
  );
74
75
  };
75
76
 
77
+ const getTitle = (title: string | undefined) => {
78
+ if (title) {
79
+ return `${title} | Playroom`;
80
+ }
81
+
82
+ const configTitle = window?.__playroomConfig__.title;
83
+
84
+ if (configTitle) {
85
+ return `${configTitle} | Playroom`;
86
+ }
87
+
88
+ return 'Playroom';
89
+ };
90
+
76
91
  export default React.memo(() => {
77
- const [{ editorPosition, colorScheme }, dispatch] = useContext(StoreContext);
92
+ const [{ editorPosition, colorScheme, title }, dispatch] =
93
+ useContext(StoreContext);
78
94
 
79
95
  const keybindings = getKeyBindings();
80
96
 
97
+ const displayedTitle = getTitle(title);
98
+
81
99
  return (
82
- <ToolbarPanel data-testid="frame-panel">
83
- <Stack space="large" dividers>
84
- <fieldset className={styles.fieldset}>
85
- <legend>
86
- <Heading level="3">Editor Position</Heading>
87
- </legend>
88
- <div className={styles.radioContainer}>
89
- {['Bottom', 'Right'].map((option) => (
90
- <div key={option}>
91
- <input
92
- type="radio"
93
- name="editorPosition"
94
- id={`editorPosition${option}`}
95
- value={option.toLowerCase()}
96
- title={option}
97
- checked={option.toLowerCase() === editorPosition}
98
- onChange={() =>
99
- dispatch({
100
- type: 'updateEditorPosition',
101
- payload: {
102
- position: option.toLowerCase() as EditorPosition,
103
- },
104
- })
105
- }
106
- className={styles.realRadio}
107
- />
108
- <label
109
- htmlFor={`editorPosition${option}`}
110
- className={styles.label}
111
- title={option}
112
- >
113
- <span className={styles.labelText}>
114
- {positionIcon[option.toLowerCase() as EditorPosition]}
115
- </span>
116
- </label>
117
- </div>
118
- ))}
119
- </div>
120
- </fieldset>
121
-
122
- <fieldset className={styles.fieldset}>
123
- <legend>
124
- <Heading level="3">Color Scheme</Heading>
125
- </legend>
126
- <div className={styles.radioContainer}>
127
- {['Light', 'Dark', 'System'].map((option) => (
128
- <div key={option}>
129
- <input
130
- type="radio"
131
- name="colorScheme"
132
- id={`colorScheme${option}`}
133
- value={option.toLowerCase()}
134
- title={option}
135
- checked={option.toLowerCase() === colorScheme}
136
- onChange={() =>
137
- dispatch({
138
- type: 'updateColorScheme',
139
- payload: {
140
- colorScheme: option.toLowerCase() as ColorScheme,
141
- },
142
- })
143
- }
144
- className={styles.realRadio}
145
- />
146
- <label
147
- htmlFor={`colorScheme${option}`}
148
- className={styles.label}
149
- title={option}
150
- >
151
- <span className={styles.labelText}>
152
- {colorModeIcon[option.toLowerCase() as ColorScheme]}
153
- </span>
154
- </label>
155
- </div>
100
+ <>
101
+ {title === undefined ? null : (
102
+ <Helmet>
103
+ <title>{displayedTitle}</title>
104
+ </Helmet>
105
+ )}
106
+ <ToolbarPanel data-testid="settings-panel">
107
+ <Stack space="xxxlarge">
108
+ <label>
109
+ <Stack space="medium">
110
+ <Heading level="3">Title</Heading>
111
+ <input
112
+ type="text"
113
+ id="playroomTitleField"
114
+ placeholder="Enter a title for this Playroom..."
115
+ className={styles.textField}
116
+ value={title}
117
+ onChange={(e) =>
118
+ dispatch({
119
+ type: 'updateTitle',
120
+ payload: { title: e.target.value },
121
+ })
122
+ }
123
+ />
124
+ </Stack>
125
+ </label>
126
+
127
+ <fieldset className={styles.fieldset}>
128
+ <legend>
129
+ <Heading level="3">Editor Position</Heading>
130
+ </legend>
131
+ <div className={styles.radioContainer}>
132
+ {['Bottom', 'Right'].map((option) => (
133
+ <div key={option}>
134
+ <input
135
+ type="radio"
136
+ name="editorPosition"
137
+ id={`editorPosition${option}`}
138
+ value={option.toLowerCase()}
139
+ title={option}
140
+ checked={option.toLowerCase() === editorPosition}
141
+ onChange={() =>
142
+ dispatch({
143
+ type: 'updateEditorPosition',
144
+ payload: {
145
+ position: option.toLowerCase() as EditorPosition,
146
+ },
147
+ })
148
+ }
149
+ className={styles.realRadio}
150
+ />
151
+ <label
152
+ htmlFor={`editorPosition${option}`}
153
+ className={styles.label}
154
+ title={option}
155
+ >
156
+ <span className={styles.labelText}>
157
+ {positionIcon[option.toLowerCase() as EditorPosition]}
158
+ </span>
159
+ </label>
160
+ </div>
161
+ ))}
162
+ </div>
163
+ </fieldset>
164
+
165
+ <fieldset className={styles.fieldset}>
166
+ <legend>
167
+ <Heading level="3">Color Scheme</Heading>
168
+ </legend>
169
+ <div className={styles.radioContainer}>
170
+ {['Light', 'Dark', 'System'].map((option) => (
171
+ <div key={option}>
172
+ <input
173
+ type="radio"
174
+ name="colorScheme"
175
+ id={`colorScheme${option}`}
176
+ value={option.toLowerCase()}
177
+ title={option}
178
+ checked={option.toLowerCase() === colorScheme}
179
+ onChange={() =>
180
+ dispatch({
181
+ type: 'updateColorScheme',
182
+ payload: {
183
+ colorScheme: option.toLowerCase() as ColorScheme,
184
+ },
185
+ })
186
+ }
187
+ className={styles.realRadio}
188
+ />
189
+ <label
190
+ htmlFor={`colorScheme${option}`}
191
+ className={styles.label}
192
+ title={option}
193
+ >
194
+ <span className={styles.labelText}>
195
+ {colorModeIcon[option.toLowerCase() as ColorScheme]}
196
+ </span>
197
+ </label>
198
+ </div>
199
+ ))}
200
+ </div>
201
+ </fieldset>
202
+
203
+ <Stack space="xlarge">
204
+ <Heading level="3">Keyboard Shortcuts</Heading>
205
+ {Object.entries(keybindings).map(([description, keybinding]) => (
206
+ <KeyboardShortcut
207
+ description={description}
208
+ keybinding={keybinding}
209
+ key={description}
210
+ />
156
211
  ))}
157
- </div>
158
- </fieldset>
159
-
160
- <Stack space="medium">
161
- <Heading level="3">Keyboard Shortcuts</Heading>
162
- {Object.entries(keybindings).map(([description, keybinding]) => (
163
- <KeyboardShortcut
164
- description={description}
165
- keybinding={keybinding}
166
- key={description}
167
- />
168
- ))}
212
+ </Stack>
169
213
  </Stack>
170
- </Stack>
171
- </ToolbarPanel>
214
+ </ToolbarPanel>
215
+ </>
172
216
  );
173
217
  });
@@ -1,5 +1,4 @@
1
- import { calc } from '@vanilla-extract/css-utils';
2
- import { style, createVar } from '@vanilla-extract/css';
1
+ import { style, createVar, styleVariants } from '@vanilla-extract/css';
3
2
  import { vars } from '../sprinkles.css';
4
3
 
5
4
  const size = createVar();
@@ -12,38 +11,8 @@ export const gap = style({
12
11
  },
13
12
  });
14
13
 
15
- export const xxsmall = style({
14
+ export const spaceScale = styleVariants(vars.space, (space) => ({
16
15
  vars: {
17
- [size]: vars.grid,
16
+ [size]: space,
18
17
  },
19
- });
20
-
21
- export const xsmall = style({
22
- vars: {
23
- [size]: calc(vars.grid).multiply(2).toString(),
24
- },
25
- });
26
-
27
- export const small = style({
28
- vars: {
29
- [size]: calc(vars.grid).multiply(3).toString(),
30
- },
31
- });
32
-
33
- export const medium = style({
34
- vars: {
35
- [size]: calc(vars.grid).multiply(4).toString(),
36
- },
37
- });
38
-
39
- export const large = style({
40
- vars: {
41
- [size]: calc(vars.grid).multiply(6).toString(),
42
- },
43
- });
44
-
45
- export const xlarge = style({
46
- vars: {
47
- [size]: calc(vars.grid).multiply(12).toString(),
48
- },
49
- });
18
+ }));
@@ -15,14 +15,7 @@ type ReactNodeNoStrings =
15
15
 
16
16
  interface Props {
17
17
  children: ReactNodeNoStrings;
18
- space:
19
- | 'none'
20
- | 'xxsmall'
21
- | 'xsmall'
22
- | 'small'
23
- | 'medium'
24
- | 'large'
25
- | 'xlarge';
18
+ space: keyof typeof styles.spaceScale;
26
19
  dividers?: boolean;
27
20
  }
28
21
 
@@ -31,7 +24,7 @@ export const Stack = ({ children, space, dividers = false }: Props) => (
31
24
  {Children.toArray(children).map((item, index) => (
32
25
  <div
33
26
  key={index}
34
- className={classnames(styles.gap, space !== 'none' && styles[space])}
27
+ className={classnames(styles.gap, styles.spaceScale[space])}
35
28
  >
36
29
  {dividers && index > 0 ? (
37
30
  <div className={styles.gap}>
@@ -42,6 +42,7 @@ export const vars = createGlobalTheme(':root', {
42
42
  large: '12px',
43
43
  xlarge: '16px',
44
44
  xxlarge: '20px',
45
+ xxxlarge: '28px',
45
46
  gutter: '40px',
46
47
  },
47
48
  });
@@ -40,6 +40,7 @@ interface DebounceUpdateUrl {
40
40
  code?: string;
41
41
  themes?: string[];
42
42
  widths?: number[];
43
+ title?: string;
43
44
  }
44
45
 
45
46
  export interface CursorPosition {
@@ -55,6 +56,7 @@ interface StatusMessage {
55
56
  type ToolbarPanel = 'snippets' | 'frames' | 'preview' | 'settings';
56
57
  interface State {
57
58
  code: string;
59
+ title?: string;
58
60
  previewRenderCode?: string;
59
61
  previewEditorCode?: string;
60
62
  highlightLineNumber?: number;
@@ -104,7 +106,8 @@ type Action =
104
106
  | { type: 'updateVisibleThemes'; payload: { themes: string[] } }
105
107
  | { type: 'resetVisibleThemes' }
106
108
  | { type: 'updateVisibleWidths'; payload: { widths: number[] } }
107
- | { type: 'resetVisibleWidths' };
109
+ | { type: 'resetVisibleWidths' }
110
+ | { type: 'updateTitle'; payload: { title: string } };
108
111
 
109
112
  const resetPreview = ({
110
113
  previewRenderCode,
@@ -383,6 +386,16 @@ const createReducer =
383
386
  return restState;
384
387
  }
385
388
 
389
+ case 'updateTitle': {
390
+ const { title } = action.payload;
391
+ store.setItem('title', title);
392
+
393
+ return {
394
+ ...state,
395
+ title,
396
+ };
397
+ }
398
+
386
399
  default:
387
400
  return state;
388
401
  }
@@ -439,12 +452,14 @@ export const StoreProvider = ({
439
452
  let codeFromQuery: State['code'];
440
453
  let themesFromQuery: State['visibleThemes'];
441
454
  let widthsFromQuery: State['visibleWidths'];
455
+ let titleFromQuery: State['title'];
442
456
 
443
457
  if (params.code) {
444
458
  const {
445
459
  code: parsedCode,
446
460
  themes: parsedThemes,
447
461
  widths: parsedWidths,
462
+ title: parsedTitle,
448
463
  } = JSON.parse(
449
464
  lzString.decompressFromEncodedURIComponent(String(params.code)) ?? ''
450
465
  );
@@ -452,6 +467,7 @@ export const StoreProvider = ({
452
467
  codeFromQuery = parsedCode;
453
468
  themesFromQuery = parsedThemes;
454
469
  widthsFromQuery = parsedWidths;
470
+ titleFromQuery = parsedTitle;
455
471
  }
456
472
 
457
473
  Promise.all([
@@ -462,6 +478,7 @@ export const StoreProvider = ({
462
478
  store.getItem<number[]>('visibleWidths'),
463
479
  store.getItem<string[]>('visibleThemes'),
464
480
  store.getItem<ColorScheme>('colorScheme'),
481
+ store.getItem<string | undefined>('title'),
465
482
  ]).then(
466
483
  ([
467
484
  storedCode,
@@ -471,14 +488,21 @@ export const StoreProvider = ({
471
488
  storedVisibleWidths,
472
489
  storedVisibleThemes,
473
490
  storedColorScheme,
491
+ storedTitle,
474
492
  ]) => {
475
493
  const code = codeFromQuery || storedCode || exampleCode;
476
494
  const editorPosition = storedPosition;
477
495
  const editorHeight = storedHeight;
478
496
  const editorWidth = storedWidth;
479
- const visibleWidths = widthsFromQuery || storedVisibleWidths;
497
+ const visibleWidths =
498
+ widthsFromQuery ||
499
+ storedVisibleWidths ||
500
+ playroomConfig?.defaultVisibleWidths;
480
501
  const visibleThemes =
481
- hasThemesConfigured && (themesFromQuery || storedVisibleThemes);
502
+ hasThemesConfigured &&
503
+ (themesFromQuery ||
504
+ storedVisibleThemes ||
505
+ playroomConfig?.defaultVisibleThemes);
482
506
  const colorScheme = storedColorScheme;
483
507
 
484
508
  dispatch({
@@ -491,6 +515,7 @@ export const StoreProvider = ({
491
515
  ...(visibleThemes ? { visibleThemes } : {}),
492
516
  ...(visibleWidths ? { visibleWidths } : {}),
493
517
  ...(colorScheme ? { colorScheme } : {}),
518
+ title: titleFromQuery ?? storedTitle ?? undefined,
494
519
  ready: true,
495
520
  },
496
521
  });
@@ -521,11 +546,13 @@ export const StoreProvider = ({
521
546
  code: state.code,
522
547
  themes: state.visibleThemes,
523
548
  widths: state.visibleWidths,
549
+ title: state.title,
524
550
  });
525
551
  }, [
526
552
  state.code,
527
553
  state.visibleThemes,
528
554
  state.visibleWidths,
555
+ state.title,
529
556
  debouncedCodeUpdate,
530
557
  ]);
531
558
 
package/src/index.d.ts CHANGED
@@ -15,6 +15,8 @@ interface PlayroomConfig {
15
15
  iframeSandbox?: string;
16
16
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
17
17
  reactDocgenTypescriptConfig?: import('react-docgen-typescript').ParserOptions;
18
+ defaultVisibleThemes?: string[];
19
+ defaultVisibleWidths?: number[];
18
20
  }
19
21
 
20
22
  interface InternalPlayroomConfig extends PlayroomConfig {
@@ -9,7 +9,7 @@ const baseUrl = window.location.href
9
9
  .split('index.html')[0];
10
10
 
11
11
  export default (theme: string) => {
12
- const [{ code }] = useContext(StoreContext);
12
+ const [{ code, title }] = useContext(StoreContext);
13
13
 
14
14
  const isThemed = theme !== '__PLAYROOM__NO_THEME__';
15
15
 
@@ -18,5 +18,6 @@ export default (theme: string) => {
18
18
  code,
19
19
  theme: isThemed ? theme : undefined,
20
20
  paramType: playroomConfig.paramType,
21
+ title,
21
22
  });
22
23
  };
package/utils/index.d.ts CHANGED
@@ -13,6 +13,7 @@ interface CompressParamsOptions {
13
13
  themes?: string[];
14
14
  widths?: number[];
15
15
  theme?: string;
16
+ title?: string;
16
17
  }
17
18
  export const compressParams: (options: CompressParamsOptions) => string;
18
19
 
@@ -22,6 +23,7 @@ interface CreateUrlOptions {
22
23
  themes?: string[];
23
24
  widths?: number[];
24
25
  paramType?: ParamType;
26
+ title?: string;
25
27
  }
26
28
 
27
29
  export const createUrl: (options: CreateUrlOptions) => string;
@@ -31,6 +33,7 @@ interface CreatePreviewUrlOptions {
31
33
  code?: string;
32
34
  theme?: string;
33
35
  paramType?: ParamType;
36
+ title?: string;
34
37
  }
35
38
 
36
39
  export const createPreviewUrl: (options: CreatePreviewUrlOptions) => string;
package/utils/index.js CHANGED
@@ -1,21 +1,29 @@
1
1
  const lzString = require('lz-string');
2
2
 
3
- const compressParams = ({ code, themes, widths, theme }) => {
3
+ const compressParams = ({ code, themes, widths, theme, title }) => {
4
4
  const data = JSON.stringify({
5
5
  ...(code ? { code } : {}),
6
6
  ...(themes ? { themes } : {}),
7
7
  ...(widths ? { widths } : {}),
8
8
  ...(theme ? { theme } : {}),
9
+ ...(title ? { title } : {}),
9
10
  });
10
11
 
11
12
  return lzString.compressToEncodedURIComponent(data);
12
13
  };
13
14
 
14
- const createUrl = ({ baseUrl, code, themes, widths, paramType = 'hash' }) => {
15
+ const createUrl = ({
16
+ baseUrl,
17
+ code,
18
+ themes,
19
+ widths,
20
+ title,
21
+ paramType = 'hash',
22
+ }) => {
15
23
  let path = '';
16
24
 
17
- if (code || themes || widths) {
18
- const compressedData = compressParams({ code, themes, widths });
25
+ if (code || themes || widths || title) {
26
+ const compressedData = compressParams({ code, themes, widths, title });
19
27
 
20
28
  path = `${paramType === 'hash' ? '#' : ''}?code=${compressedData}`;
21
29
  }
@@ -29,11 +37,17 @@ const createUrl = ({ baseUrl, code, themes, widths, paramType = 'hash' }) => {
29
37
  return path;
30
38
  };
31
39
 
32
- const createPreviewUrl = ({ baseUrl, code, theme, paramType = 'hash' }) => {
40
+ const createPreviewUrl = ({
41
+ baseUrl,
42
+ code,
43
+ theme,
44
+ title,
45
+ paramType = 'hash',
46
+ }) => {
33
47
  let path = '';
34
48
 
35
- if (code || theme) {
36
- const compressedData = compressParams({ code, theme });
49
+ if (code || theme || title) {
50
+ const compressedData = compressParams({ code, theme, title });
37
51
 
38
52
  path = `/preview/${paramType === 'hash' ? '#' : ''}?code=${compressedData}`;
39
53
  }