@workday/canvas-kit-docs 12.0.0-alpha.918-next.0 → 12.0.0-alpha.919-next.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.
@@ -43,16 +43,17 @@ A note to the reader:
43
43
  - [Component Updates](#component-updates)
44
44
  - [Styling API and CSS Tokens](#styling-api-and-css-tokens)
45
45
  - [Avatar](#avatar)
46
+ - [Collections](#collections)
46
47
  - [Combobox](#combmbox)
47
48
  - [Form Field](#form-field)
48
49
  - [Form Field Group](#form-field-group)
49
50
  - [Form Field Field](#form-field-field)
50
51
  - [Menu Item](#menu-item)
52
+ - [MultiSelect](#multiselect)
51
53
  - [Search Form](#search-form)
52
54
  - [Select](#select)
53
55
  - [Text Area](#text-area)
54
56
  - [Text Input](#text-input)
55
- - [Collections](#collections)
56
57
  - [Utility Updates](#utility-updates)
57
58
  - [Troubleshooting](#troubleshooting)
58
59
  - [Glossary](#glossary)
@@ -487,9 +488,9 @@ this sub-component when using `FormField`. This component also exists on `FormFi
487
488
 
488
489
  ### Menu Item
489
490
 
490
- **PR: ** [2969](https://github.com/Workday/canvas-kit/pull/2969)
491
+ **PR:** [2969](https://github.com/Workday/canvas-kit/pull/2969)
491
492
 
492
- `Menu.Item` was converted to use Stencils for styling and uses SystemIcon stencil variables to
493
+ `Menu.Item` was converted to use Stencils for styling and uses `SystemIcon` stencil variables to
493
494
  change icon color instead of deeply nested selectors. We also added `Menu.Option` component for
494
495
  menus that have a selected visual state. `Menu.Option` will need more accessibility affordances that
495
496
  depend on the nature of your use of the `Menu` component. For example, `<Combobox>` and `<Select>`
@@ -505,6 +506,20 @@ We've removed the `MenuItemProps` export from `@workday/canvas-kit-react/menu`.
505
506
  polymorphic components. The `never` means "don't add element props". The second parameter is used to
506
507
  pass the interface that the `as` prop is pointing to.
507
508
 
509
+ `Menu.Item` no longer sets `aria-selected` since that attribute is not valid on a `role=menuitem`.
510
+ The `Menu.Option` was added to support the role of a `role=option` for going inside a
511
+ `role=listbox`. The `Combobox` family of components uses a `role=listbox` for menu options. The
512
+ `Menu.Option` renders a checkmark for a visual indication of selected options.
513
+
514
+ ### MultiSelect
515
+
516
+ **PR:** [2911](https://github.com/Workday/canvas-kit/pull/2911)
517
+
518
+ Added the `MultiSelect` component to select more than one option from a list of options. The
519
+ `MultiSelect` is similar in API to the `Select` component, except the values are comma delimited
520
+ with a space. If the ids represented are `['1', '2']`, then the string value of the form field is
521
+ `'1, 2'`.
522
+
508
523
  ### Search Form (Labs)
509
524
 
510
525
  **PRs:** [#2934](https://github.com/Workday/canvas-kit/pull/2934),
@@ -0,0 +1,98 @@
1
+ import {
2
+ ExampleCodeBlock,
3
+ InformationHighlight,
4
+ SymbolDoc,
5
+ Specifications,
6
+ } from '@workday/canvas-kit-docs';
7
+
8
+ import Basic from './examples/Basic';
9
+ import Complex from './examples/Complex';
10
+ import Icons from './examples/Icons';
11
+ import Controlled from './examples/Controlled';
12
+ import Searching from './examples/Searching';
13
+
14
+
15
+ # Canvas Kit MultiSelect
16
+
17
+ MultiSelect inputs allow users to choose multiple options from a list of items.
18
+
19
+ ## Installation
20
+
21
+ ```sh
22
+ yarn add @workday/canvas-kit-preview-react
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Basic Example
28
+
29
+ `MultiSelect` supports a
30
+ [dynamic API](/getting-started/for-developers/resources/collection-api/#dynamic-items) where you
31
+ pass an array of items via the `items` prop and provide a render function to display the items. The
32
+ items may be provided as an
33
+ [array of strings](/getting-started/for-developers/resources/collection-api/#array-of-strings) or an
34
+ [array of objects](/getting-started/for-developers/resources/collection-api/#array-of-objects).
35
+
36
+ `MultiSelect` should be used in tandem with [Form Field](/components/inputs/form-field/) where the
37
+ `MultiSelect` wraps the `FormField` element and the `FormField` element wraps the children of
38
+ `MultiSelect` to meet accessibility standards. This ensures the `label` text from `FormField` is
39
+ attached to the `MultiSelect.Input` and read out as a group for voiceover.
40
+
41
+ ```tsx
42
+ <MultiSelect items={options}>
43
+ <FormField label="Your Label">
44
+ <MultiSelect.Input onChange={e => handleChange(e)} id="contact-multi-select" />
45
+ <MultiSelect.Popper>
46
+ <MultiSelect.Card>
47
+ <MultiSelect.List>
48
+ {item => <MultiSelect.Item>{item.id}</MultiSelect.Item>}
49
+ </MultiSelect.List>
50
+ </MultiSelect.Card>
51
+ </MultiSelect.Popper>
52
+ </FormField>
53
+ </MultiSelect>
54
+ ```
55
+
56
+ <ExampleCodeBlock code={Basic} />
57
+
58
+ ### Complex
59
+
60
+ When registering items in an array of objects, it's common to have the text that is displayed to the
61
+ user be different than an id. In this example, `serverId` and `label` properties need to be remapped
62
+ to `id` and `text` hence the usage of `getId` and `getTextValue`. If your object has the properties
63
+ `text` and `id`, there would be no need for this.
64
+
65
+ <ExampleCodeBlock code={Complex} />
66
+
67
+ ### With Icons
68
+
69
+ Use `MultiSelect.Item.Icon` to render an icon for a `MultiSelect.Item`. The `icon` prop for
70
+ `MultiSelect.Item.Icon` accepts [system icons](/assets/system-icons/) from
71
+ `@workday/canvas-system-icons-web`.
72
+
73
+ > **Note: `data-id` on `MultiSelect.Item` must match the `id` property in your array of objects.
74
+ > This ensures proper keyboard handling and type-ahead.**
75
+
76
+ <ExampleCodeBlock code={Icons} />
77
+
78
+ ### Controlled
79
+
80
+ The MultiSelect can be a controlled input component by passing the `value` and `onChange` to either
81
+ the `<MultiSelect>` component or the `<MultiSelect.Input>` component. Internally, the
82
+ `MultiSelect.Input` watches for changes on the `value` React prop as well as the `value` DOM
83
+ property and will update the model accordingly.
84
+
85
+ <ExampleCodeBlock code={Controlled} />
86
+
87
+ ### Searching
88
+
89
+ A MultiSelect input can be used as a filter for results. Most likely this also means there are many
90
+ items that may not be all be loaded from the server at once. The `useComboboxLoader` can be used to
91
+ dynamically load items as the user navigates the available options.
92
+
93
+ > **Note:** The behavior of search is experimental. The example should continue to work without
94
+ > modification, but how the searchable input is presented to the user may change with user testing.
95
+ > Don't rely too much on the exact behavior of the search input. For example, the search input may
96
+ > be cleared when the user blurs the field.
97
+
98
+ <ExampleCodeBlock code={Searching} />
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+
3
+ import {FormField} from '@workday/canvas-kit-react/form-field';
4
+ import {MultiSelect} from '@workday/canvas-kit-preview-react/multi-select';
5
+
6
+ const items = ['Cheese', 'Olives', 'Onions', 'Pepperoni', 'Peppers'];
7
+
8
+ export default () => {
9
+ return (
10
+ <>
11
+ <MultiSelect items={items}>
12
+ <FormField orientation="horizontal">
13
+ <FormField.Label>Toppings</FormField.Label>
14
+ <FormField.Input as={MultiSelect.Input} placeholder="Select Multiple" />
15
+ <MultiSelect.Popper>
16
+ <MultiSelect.Card>
17
+ <MultiSelect.List>
18
+ {item => (
19
+ <MultiSelect.Item data-id={item}>
20
+ <MultiSelect.Item.Text>{item}</MultiSelect.Item.Text>
21
+ </MultiSelect.Item>
22
+ )}
23
+ </MultiSelect.List>
24
+ </MultiSelect.Card>
25
+ </MultiSelect.Popper>
26
+ </FormField>
27
+ </MultiSelect>
28
+ </>
29
+ );
30
+ };
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+
3
+ import {CanvasProvider} from '@workday/canvas-kit-react/common';
4
+ import {createStyles} from '@workday/canvas-kit-styling';
5
+ import {FormField} from '@workday/canvas-kit-react/form-field';
6
+
7
+ import {system} from '@workday/canvas-tokens-web';
8
+
9
+ import {MultiSelect} from '@workday/canvas-kit-preview-react/multi-select';
10
+
11
+ const mainContentStyles = createStyles({
12
+ padding: system.space.x4,
13
+ });
14
+
15
+ const items = [
16
+ {id: '1', text: 'Cheese'},
17
+ {id: '2', text: 'Olives'},
18
+ {id: '3', text: 'Onions'},
19
+ {id: '4', text: 'Pepperoni'},
20
+ {id: '5', text: 'Peppers'},
21
+ ];
22
+
23
+ export default () => {
24
+ const [value, setValue] = React.useState('');
25
+ const [label, setLabel] = React.useState('');
26
+ return (
27
+ <CanvasProvider>
28
+ <>
29
+ <form
30
+ onSubmit={e => {
31
+ console.log('form submitted');
32
+ e.preventDefault();
33
+ }}
34
+ >
35
+ <main className={mainContentStyles}>
36
+ <MultiSelect items={items} getId={i => i.id} getTextValue={i => i.text}>
37
+ <FormField orientation="horizontal">
38
+ <FormField.Label>Toppings</FormField.Label>
39
+ <FormField.Input
40
+ as={MultiSelect.Input}
41
+ placeholder="Select Multiple"
42
+ name="toppings"
43
+ onChange={e => {
44
+ const value = e.currentTarget.value;
45
+ setValue(value);
46
+ setLabel(
47
+ value
48
+ .split(', ')
49
+ .map(item => items.find(i => i.id === item)?.text || 'Not Found')
50
+ .join(', ')
51
+ );
52
+ }}
53
+ value={value}
54
+ />
55
+ <MultiSelect.Popper>
56
+ <MultiSelect.Card>
57
+ <MultiSelect.List>
58
+ {item => (
59
+ <MultiSelect.Item data-id={item.id}>
60
+ <MultiSelect.Item.Text>{item.text}</MultiSelect.Item.Text>
61
+ </MultiSelect.Item>
62
+ )}
63
+ </MultiSelect.List>
64
+ </MultiSelect.Card>
65
+ </MultiSelect.Popper>
66
+ </FormField>
67
+ </MultiSelect>
68
+ </main>
69
+ </form>
70
+ <div>Selected IDs: {value}</div>
71
+ <div>Selected Labels: {label}</div>
72
+ </>
73
+ </CanvasProvider>
74
+ );
75
+ };
@@ -0,0 +1,92 @@
1
+ import React from 'react';
2
+
3
+ import {FormField} from '@workday/canvas-kit-react/form-field';
4
+ import {PrimaryButton, SecondaryButton} from '@workday/canvas-kit-react/button';
5
+ import {Flex} from '@workday/canvas-kit-react/layout';
6
+
7
+ import {MultiSelect} from '@workday/canvas-kit-preview-react/multi-select';
8
+
9
+ const items = [
10
+ {id: '1', text: 'Cheese'},
11
+ {id: '2', text: 'Olives'},
12
+ {id: '3', text: 'Onions'},
13
+ {id: '4', text: 'Pepperoni'},
14
+ {id: '5', text: 'Peppers'},
15
+ ];
16
+
17
+ export default () => {
18
+ const formRef = React.useRef<HTMLFormElement>(null);
19
+ const [value, setValue] = React.useState('1');
20
+ const [label, setLabel] = React.useState('Cheese');
21
+
22
+ function handleOnChange(event: React.ChangeEvent<HTMLInputElement>) {
23
+ const value = event.currentTarget.value;
24
+ setValue(value);
25
+ setLabel(
26
+ value
27
+ .split(', ')
28
+ .map(item => items.find(i => i.id === item)?.text || 'Not Found')
29
+ .join(', ')
30
+ );
31
+ }
32
+
33
+ return (
34
+ <>
35
+ <form
36
+ onSubmit={e => {
37
+ console.log('form submitted');
38
+ e.preventDefault();
39
+ }}
40
+ ref={formRef}
41
+ >
42
+ <Flex gap="s" flexDirection="column">
43
+ <MultiSelect items={items}>
44
+ <FormField orientation="horizontal">
45
+ <FormField.Label>Toppings</FormField.Label>
46
+ <FormField.Input
47
+ as={MultiSelect.Input}
48
+ placeholder="Select Multiple"
49
+ name="toppings"
50
+ onChange={handleOnChange}
51
+ value={value}
52
+ />
53
+ <MultiSelect.Popper>
54
+ <MultiSelect.Card>
55
+ <MultiSelect.List>
56
+ {item => (
57
+ <MultiSelect.Item data-id={item.id}>
58
+ <MultiSelect.Item.Text>{item.text}</MultiSelect.Item.Text>
59
+ </MultiSelect.Item>
60
+ )}
61
+ </MultiSelect.List>
62
+ </MultiSelect.Card>
63
+ </MultiSelect.Popper>
64
+ </FormField>
65
+ </MultiSelect>
66
+ <Flex gap="s">
67
+ <SecondaryButton
68
+ onClick={e => {
69
+ setValue('1, 2, 3');
70
+ }}
71
+ >
72
+ Set to "Cheese, Olives, Onions" via React `value`
73
+ </SecondaryButton>
74
+ <SecondaryButton
75
+ onClick={e => {
76
+ const input = formRef.current.querySelector('[name=toppings]') as HTMLInputElement;
77
+ input.value = '1, 2';
78
+ }}
79
+ >
80
+ Set to "Cheese, Olives" via DOM `value`
81
+ </SecondaryButton>
82
+ </Flex>
83
+ <div>
84
+ <PrimaryButton type="submit">Submit</PrimaryButton>
85
+ </div>
86
+ <div>Selected ID: {value}</div>
87
+ <div>Selected Label: {label}</div>
88
+ </Flex>
89
+ </form>
90
+ </>
91
+ );
92
+ };
@@ -0,0 +1,43 @@
1
+ import React from 'react';
2
+
3
+ import {FormField} from '@workday/canvas-kit-react/form-field';
4
+ import {
5
+ mediaPauseIcon,
6
+ mediaPlayIcon,
7
+ mediaTopicsIcon,
8
+ skipIcon,
9
+ previousIcon,
10
+ } from '@workday/canvas-system-icons-web';
11
+
12
+ import {MultiSelect} from '@workday/canvas-kit-preview-react/multi-select';
13
+
14
+ const items = [
15
+ {id: '1', text: 'Pause', icon: mediaPauseIcon},
16
+ {id: '2', text: 'Play', icon: mediaPlayIcon},
17
+ {id: '3', text: 'Skip', icon: skipIcon},
18
+ {id: '4', text: 'Previous', icon: previousIcon},
19
+ ];
20
+
21
+ export default () => {
22
+ return (
23
+ <MultiSelect items={items}>
24
+ <FormField orientation="horizontal">
25
+ <FormField.Label>Controls</FormField.Label>
26
+ <FormField.Input as={MultiSelect.Input} placeholder="Select Multiple" />
27
+ <MultiSelect.Popper>
28
+ <MultiSelect.Card>
29
+ <MultiSelect.List>
30
+ {item => (
31
+ <MultiSelect.Item data-id={item.id}>
32
+ <MultiSelect.Item.Icon icon={item.icon} />
33
+ <MultiSelect.Item.Text>{item.text}</MultiSelect.Item.Text>
34
+ <MultiSelect.Item.Icon icon={mediaTopicsIcon} />
35
+ </MultiSelect.Item>
36
+ )}
37
+ </MultiSelect.List>
38
+ </MultiSelect.Card>
39
+ </MultiSelect.Popper>
40
+ </FormField>
41
+ </MultiSelect>
42
+ );
43
+ };
@@ -0,0 +1,123 @@
1
+ import React from 'react';
2
+
3
+ import {system} from '@workday/canvas-tokens-web';
4
+
5
+ import {createStyles} from '@workday/canvas-kit-styling';
6
+ import {LoadReturn} from '@workday/canvas-kit-react/collection';
7
+ import {CanvasProvider} from '@workday/canvas-kit-react/common';
8
+ import {useComboboxLoader} from '@workday/canvas-kit-react/combobox';
9
+ import {FormField} from '@workday/canvas-kit-react/form-field';
10
+
11
+ import {MultiSelect, useMultiSelectModel} from '@workday/canvas-kit-preview-react/multi-select';
12
+ import {StyledMenuItem} from '@workday/canvas-kit-react/menu';
13
+
14
+ const mainContentStyles = createStyles({
15
+ padding: system.space.x4,
16
+ });
17
+
18
+ const colors = ['Red', 'Blue', 'Purple', 'Green', 'Pink'];
19
+ const fruits = ['Apple', 'Orange', 'Banana', 'Grape', 'Lemon', 'Lime'];
20
+ const options = Array(1000)
21
+ .fill('')
22
+ .map((_, index) => {
23
+ return {
24
+ id: `${index + 1}`,
25
+ text: `${colors[index % colors.length]} ${fruits[index % fruits.length]} ${index + 1}`,
26
+ };
27
+ });
28
+
29
+ export default () => {
30
+ const [value, setValue] = React.useState('');
31
+
32
+ const {model, loader} = useComboboxLoader(
33
+ {
34
+ // You can start with any number that makes sense.
35
+ total: 0,
36
+
37
+ // Pick whatever number makes sense for your API
38
+ pageSize: 20,
39
+
40
+ // A load function that will be called by the loader. You must return a promise that returns
41
+ // an object like `{items: [], total: 0}`. The `items` will be merged into the loader's cache
42
+ async load({pageNumber, pageSize, filter}) {
43
+ return new Promise<LoadReturn<(typeof options)[0]>>(resolve => {
44
+ // simulate a server response by resolving after a period of time
45
+ setTimeout(() => {
46
+ // simulate paging and filtering based on pre-computed items
47
+ const start = (pageNumber - 1) * pageSize;
48
+ const end = start + pageSize;
49
+ const filteredItems = options.filter(item => {
50
+ if (filter === '' || typeof filter !== 'string') {
51
+ return true;
52
+ }
53
+ return item.text.toLowerCase().includes(filter.toLowerCase());
54
+ });
55
+
56
+ const total = filteredItems.length;
57
+ const items = filteredItems.slice(start, end);
58
+
59
+ resolve({
60
+ items,
61
+ total,
62
+ });
63
+ }, 300);
64
+ });
65
+ },
66
+ onShow() {
67
+ // The `shouldLoad` cancels while the combobox menu is hidden, so let's load when it is
68
+ // visible
69
+ loader.load();
70
+ },
71
+ },
72
+ useMultiSelectModel
73
+ );
74
+
75
+ return (
76
+ <CanvasProvider>
77
+ <>
78
+ <form
79
+ onSubmit={e => {
80
+ console.log('form submitted');
81
+ e.preventDefault();
82
+ }}
83
+ >
84
+ <main className={mainContentStyles}>
85
+ <MultiSelect model={model}>
86
+ <FormField orientation="horizontal">
87
+ <FormField.Label>Fruits</FormField.Label>
88
+ <FormField.Input
89
+ as={MultiSelect.SearchInput}
90
+ placeholder="Search"
91
+ name="toppings"
92
+ onChange={e => {
93
+ setValue(e.currentTarget.value);
94
+ }}
95
+ value={value}
96
+ />
97
+ <MultiSelect.Popper>
98
+ <MultiSelect.Card>
99
+ {model.state.items.length === 0 && (
100
+ <StyledMenuItem as="span">No Results Found</StyledMenuItem>
101
+ )}
102
+ {model.state.items.length > 0 && (
103
+ <MultiSelect.List maxHeight={200}>
104
+ {item =>
105
+ item ? (
106
+ <MultiSelect.Item data-id={item.id}>
107
+ <MultiSelect.Item.Text>{item.text}</MultiSelect.Item.Text>
108
+ </MultiSelect.Item>
109
+ ) : undefined
110
+ }
111
+ </MultiSelect.List>
112
+ )}
113
+ </MultiSelect.Card>
114
+ </MultiSelect.Popper>
115
+ </FormField>
116
+ </MultiSelect>
117
+ </main>
118
+ </form>
119
+ <div>Selected: {value}</div>
120
+ </>
121
+ </CanvasProvider>
122
+ );
123
+ };
@@ -66,6 +66,7 @@ export default () => {
66
66
  </SegmentedControl.Item>
67
67
  </SegmentedControl.List>
68
68
  </SegmentedControl>
69
+ <p>Selected: {containerWidth}</p>
69
70
  </footer>
70
71
  </div>
71
72
  );
@@ -78,7 +78,8 @@ on a dark or colorful background such as `blueberry400`.
78
78
 
79
79
  ### Grow Prop
80
80
 
81
- The example below shows the use of the `grow` prop on different variants of buttons. This will set the width of the button to the width of its container.
81
+ The example below shows the use of the `grow` prop on different variants of buttons. This will set
82
+ the width of the button to the width of its container.
82
83
 
83
84
  <ExampleCodeBlock code={Grow} />
84
85
 
@@ -106,4 +107,4 @@ should be used for navigation.
106
107
 
107
108
  ## Specifications
108
109
 
109
- <Specifications file="Button.spec.ts" name="Button" />
110
+ <Specifications file="Button.spec.tsx" name="Button" />
@@ -36,13 +36,16 @@ two separate `input` elements.
36
36
  the same as the user input. Any prop related to the function of forms will be passed here. For
37
37
  example, the `name` attribute will be passed here. The `ref` will be pointed to this element.
38
38
 
39
+ `Select` and `MultiSelect` are examples of constrained comboboxes.
40
+
39
41
  ### Arbitrary
40
42
 
41
43
  An arbitrary combobox allows the user to enter any value. The list of options are presented as
42
44
  suggestions and selecting an option will prefill the combobox with the value of the option. The user
43
45
  is still allowed to modify the combobox even after an option is entered. With arbitrary comboboxes,
44
46
  there is only one `input` element. Arbitrary combobox inputs should use the
45
- [useComboboxInputArbitrary](#usecomboboxinputarbirary) hook.
47
+ [useComboboxInputArbitrary](#usecomboboxinputarbirary) hook. Typeahead or `Autocomplete` are
48
+ examples are arbitrary value comboboxes.
46
49
 
47
50
  ## Installation
48
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workday/canvas-kit-docs",
3
- "version": "12.0.0-alpha.918-next.0",
3
+ "version": "12.0.0-alpha.919-next.0",
4
4
  "description": "Documentation components of Canvas Kit components",
5
5
  "author": "Workday, Inc. (https://www.workday.com)",
6
6
  "license": "Apache-2.0",
@@ -44,10 +44,10 @@
44
44
  "dependencies": {
45
45
  "@emotion/styled": "^11.6.0",
46
46
  "@storybook/csf": "0.0.1",
47
- "@workday/canvas-kit-labs-react": "^12.0.0-alpha.918-next.0",
48
- "@workday/canvas-kit-preview-react": "^12.0.0-alpha.918-next.0",
49
- "@workday/canvas-kit-react": "^12.0.0-alpha.918-next.0",
50
- "@workday/canvas-kit-styling": "^12.0.0-alpha.918-next.0",
47
+ "@workday/canvas-kit-labs-react": "^12.0.0-alpha.919-next.0",
48
+ "@workday/canvas-kit-preview-react": "^12.0.0-alpha.919-next.0",
49
+ "@workday/canvas-kit-react": "^12.0.0-alpha.919-next.0",
50
+ "@workday/canvas-kit-styling": "^12.0.0-alpha.919-next.0",
51
51
  "@workday/canvas-system-icons-web": "^3.0.0",
52
52
  "@workday/canvas-tokens-web": "^2.0.1",
53
53
  "markdown-to-jsx": "^7.2.0",
@@ -60,5 +60,5 @@
60
60
  "mkdirp": "^1.0.3",
61
61
  "typescript": "5.0"
62
62
  },
63
- "gitHead": "3f21a8c474728598369595c8978854dab2355235"
63
+ "gitHead": "4fa21d7c07d138e03daf228f4a6a17fcbf379a3f"
64
64
  }