disco_app 0.8.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/disco_app/icons.svg +0 -0
  3. data/app/assets/javascripts/disco_app/components/{filterable_shop_list.js.jsx → custom/filterable_shop_list.js.jsx} +1 -1
  4. data/app/assets/javascripts/disco_app/components/custom/inline-radio-options.es6.jsx +59 -0
  5. data/app/assets/javascripts/disco_app/components/custom/rules-editor.es6.jsx +360 -0
  6. data/app/assets/javascripts/disco_app/components/{shop_list.js.jsx → custom/shop_list.js.jsx} +9 -11
  7. data/app/assets/javascripts/disco_app/components/custom/shop_row.js.jsx +43 -0
  8. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card-section.es6.jsx +30 -0
  9. data/app/assets/javascripts/disco_app/components/ui-kit/cards/card.es6.jsx +9 -0
  10. data/app/assets/javascripts/disco_app/components/ui-kit/cards/cart-section-title.es6.jsx +7 -0
  11. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_form.es6.jsx +72 -0
  12. data/app/assets/javascripts/disco_app/components/ui-kit/forms/base_input.es6.jsx +20 -0
  13. data/app/assets/javascripts/disco_app/components/ui-kit/forms/button.es6.jsx +13 -0
  14. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-radio.es6.jsx +30 -0
  15. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-select.es6.jsx +39 -0
  16. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-text.es6.jsx +59 -0
  17. data/app/assets/javascripts/disco_app/components/ui-kit/forms/input-textarea.es6.jsx +48 -0
  18. data/app/assets/javascripts/disco_app/components/ui-kit/icons/icon-chevron.es6.jsx +33 -0
  19. data/app/assets/javascripts/disco_app/components/ui-kit/icons/next-icon.es6.jsx +18 -0
  20. data/app/assets/javascripts/disco_app/components/ui-kit/input_select.es6.jsx +21 -0
  21. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-annotated-section.es6.jsx +29 -0
  22. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-empty-state.es6.jsx +35 -0
  23. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-footer-help.es6.jsx +13 -0
  24. data/app/assets/javascripts/disco_app/components/ui-kit/ui-layout/ui-page-actions.es6.jsx +39 -0
  25. data/app/assets/javascripts/disco_app/components.js +1 -4
  26. data/app/assets/javascripts/disco_app/disco_app.js +3 -0
  27. data/app/assets/javascripts/disco_app/ui-kit.js +1 -0
  28. data/app/assets/stylesheets/disco_app/admin/_header.scss +66 -0
  29. data/app/assets/stylesheets/disco_app/admin/_layout.scss +40 -0
  30. data/app/assets/stylesheets/disco_app/admin/_nav.scss +172 -0
  31. data/app/assets/stylesheets/disco_app/admin.scss +11 -0
  32. data/app/assets/stylesheets/disco_app/disco_app.scss +12 -12
  33. data/app/assets/stylesheets/disco_app/{disco/mixins → mixins}/_flexbox.scss +6 -0
  34. data/app/assets/stylesheets/disco_app/ui-kit/_ui-empty-state.scss +94 -0
  35. data/app/assets/stylesheets/disco_app/ui-kit/_ui-footer-help.scss +25 -0
  36. data/app/assets/stylesheets/disco_app/ui-kit/_ui-icons.scss +28 -0
  37. data/app/assets/stylesheets/disco_app/ui-kit/_ui-kit.scss +5086 -0
  38. data/app/assets/stylesheets/disco_app/ui-kit/_ui-layout.scss +10 -0
  39. data/app/assets/stylesheets/disco_app/ui-kit/_ui-page-actions.scss +21 -0
  40. data/app/assets/stylesheets/disco_app/{disco/_tabs.scss → ui-kit/_ui-tabs.scss} +3 -1
  41. data/app/assets/stylesheets/disco_app/ui-kit/_ui-type.scss +13 -0
  42. data/app/controllers/disco_app/admin/concerns/plans_controller.rb +8 -5
  43. data/app/controllers/disco_app/admin/concerns/subscriptions_controller.rb +32 -0
  44. data/app/controllers/disco_app/admin/subscriptions_controller.rb +3 -0
  45. data/app/helpers/disco_app/application_helper.rb +22 -0
  46. data/app/models/disco_app/concerns/plan.rb +2 -2
  47. data/app/models/disco_app/concerns/shop.rb +0 -4
  48. data/app/models/disco_app/concerns/subscription.rb +12 -0
  49. data/app/resources/disco_app/admin/resources/concerns/shop_resource.rb +40 -3
  50. data/app/views/disco_app/admin/plans/_form.html.erb +18 -21
  51. data/app/views/disco_app/admin/plans/_plan_code_fields.html.erb +15 -0
  52. data/app/views/disco_app/admin/plans/index.html.erb +2 -0
  53. data/app/views/disco_app/admin/shops/index.html.erb +2 -1
  54. data/app/views/disco_app/admin/subscriptions/edit.html.erb +33 -0
  55. data/app/views/disco_app/shared/_icons.html.erb +3 -0
  56. data/app/views/layouts/admin/_nav_items.erb +20 -0
  57. data/app/views/layouts/admin.html.erb +53 -9
  58. data/app/views/layouts/embedded_app.html.erb +2 -0
  59. data/app/views/layouts/embedded_app_modal.html.erb +11 -0
  60. data/config/routes.rb +4 -2
  61. data/lib/disco_app/engine.rb +2 -1
  62. data/lib/disco_app/version.rb +1 -1
  63. data/lib/generators/disco_app/disco_app_generator.rb +35 -2
  64. data/lib/generators/disco_app/templates/assets/javascripts/components.js +3 -0
  65. data/lib/generators/disco_app/templates/config/database.yml.tt +20 -0
  66. data/lib/tasks/database.rake +8 -0
  67. data/test/controllers/disco_app/charges_controller_test.rb +9 -2
  68. data/test/fixtures/api/widget_store/charges/create_recurring_application_charge_request.json +1 -1
  69. data/test/fixtures/api/widget_store/charges/create_second_recurring_application_charge_request.json +1 -1
  70. data/test/fixtures/disco_app/subscriptions.yml +1 -0
  71. data/test/models/disco_app/subscription_test.rb +19 -0
  72. data/test/services/disco_app/charges_service_test.rb +9 -2
  73. data/test/test_helper.rb +3 -0
  74. metadata +80 -21
  75. data/app/assets/javascripts/disco_app/components/shop_row.js.jsx +0 -27
  76. data/app/assets/stylesheets/disco_app/bootstrap/_custom.scss +0 -54
  77. data/app/assets/stylesheets/disco_app/bootstrap/_variables.scss +0 -872
  78. data/app/assets/stylesheets/disco_app/disco/_buttons.scss +0 -31
  79. data/app/assets/stylesheets/disco_app/disco/_cards.scss +0 -52
  80. data/app/assets/stylesheets/disco_app/disco/_forms.scss +0 -23
  81. data/app/assets/stylesheets/disco_app/disco/_grid.scss +0 -58
  82. data/app/assets/stylesheets/disco_app/disco/_sections.scss +0 -61
  83. data/app/assets/stylesheets/disco_app/disco/_tables.scss +0 -57
  84. data/app/assets/stylesheets/disco_app/disco/_type.scss +0 -39
  85. data/app/views/layouts/admin/_navbar.html.erb +0 -25
  86. data/lib/generators/disco_app/adminify/adminify_generator.rb +0 -35
  87. data/lib/generators/disco_app/reactify/reactify_generator.rb +0 -45
  88. /data/app/assets/javascripts/disco_app/components/{shop_filter_tab.js.jsx → custom/shop_filter_tab.js.jsx} +0 -0
  89. /data/app/assets/javascripts/disco_app/components/{shop_filter_tabs.js.jsx → custom/shop_filter_tabs.js.jsx} +0 -0
  90. /data/app/assets/javascripts/disco_app/components/{shopify_admin_link.js.jsx → custom/shopify_admin_link.js.jsx} +0 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Base class for form components that provides helper methods
3
+ * for rendering errors associated with the form
4
+ **/
5
+ class BaseForm extends React.Component {
6
+
7
+ /**
8
+ * check if the field parameter has errors associated.
9
+ * if no parameters are given, it checks if there's at least an error at all
10
+ **/
11
+ hasErrors(field) {
12
+ if ((arguments.length === 0 && this.errorKeys().length > 0) ||
13
+ (this.props.errors && this.props.errors.errors.indexOf(field) > -1)) {
14
+ return true;
15
+ }
16
+ return false;
17
+ }
18
+
19
+ /**
20
+ * returns a list of fields that contain errors
21
+ **/
22
+ errorKeys() {
23
+ return this.props.errors.errors;
24
+ }
25
+
26
+ /**
27
+ * returns the type of resource associate with this error
28
+ **/
29
+ errorType() {
30
+ return this.props.errors.type;
31
+ }
32
+
33
+ /**
34
+ * returns the error messages
35
+ **/
36
+ errorMessages() {
37
+ return this.props.errors.messages;
38
+ }
39
+
40
+ /**
41
+ * renders basic form errors
42
+ **/
43
+ renderErrors() {
44
+ if (!this.hasErrors()) {
45
+ return null;
46
+ }
47
+
48
+ var title = "There " + (this.errorMessages().length == 1 ? "is" : "are") + " " + this.errorMessages().length + " error" + (this.errorMessages().length > 1 ? "s" : "") + " for this " + this.errorType() + ":";
49
+ var errorMessages = this.errorMessages().map(function(message) {
50
+ return <li>{message}</li>;
51
+ });
52
+
53
+ return (
54
+ <div className="ui-banner ui-banner--status-error ui-banner--default-vertical-spacing ui-banner--default-horizontal-spacing">
55
+ <div className="ui-banner__ribbon">
56
+ <svg className="next-icon next-icon--24" viewBox="0 0 24 24">
57
+ <path d="M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.6 0 12 0zm0 4c1.4 0 2.7.4 3.9 1L12 8.8 8.8 12 5 15.9c-.6-1.1-1-2.5-1-3.9 0-4.4 3.6-8 8-8zm0 16c-1.4 0-2.7-.4-3.9-1l3.9-3.9 3.2-3.2L19 8.1c.6 1.1 1 2.5 1 3.9 0 4.4-3.6 8-8 8z"></path>
58
+ </svg>
59
+ </div>
60
+ <div className="ui-banner__content">
61
+ <h2 className="ui-banner__title">
62
+ {title}
63
+ </h2>
64
+ <ul>
65
+ {errorMessages}
66
+ </ul>
67
+ </div>
68
+ </div>
69
+ )
70
+ }
71
+
72
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Base class providing helper method for deciding if an input contains errors.
3
+ * Expects subclasses to have `name` and `errors` props.
4
+ **/
5
+ class BaseInput extends React.Component {
6
+
7
+ /**
8
+ * Checks if the component has associated errors or not
9
+ **/
10
+ hasError() {
11
+ if (!(this.props && this.props.name)) {
12
+ return false;
13
+ }
14
+ var fieldName = this.props.name.substring(this.props.name.lastIndexOf("[") + 1, this.props.name.lastIndexOf("]"));
15
+ return (fieldName &&
16
+ this.props.errors &&
17
+ this.props.errors.errors &&
18
+ this.props.errors.errors.indexOf(fieldName) > -1);
19
+ }
20
+ }
@@ -0,0 +1,13 @@
1
+ const Button = ({ children, disabled, onClick }) => {
2
+
3
+ const className = classNames({
4
+ 'btn': true
5
+ });
6
+
7
+ return(
8
+ <button type="button" disabled={disabled} className={className} onClick={onClick}>
9
+ {children}
10
+ </button>
11
+ )
12
+
13
+ };
@@ -0,0 +1,30 @@
1
+ const InputRadio = ({ label, name, value, checked, inline, isLast, onChange }) => {
2
+
3
+ const id = `${name}[${value}]`;
4
+
5
+ const wrapperClassName = classNames({
6
+ 'next-input-wrapper': true,
7
+ 'inline': inline,
8
+ 'sr': !isLast
9
+ });
10
+
11
+ const labelClassName = classNames({
12
+ 'next-label': true,
13
+ 'next-label--switch': true,
14
+ 'inline': inline,
15
+ 'fw-normal': inline
16
+ });
17
+
18
+ const handleChange = (e) => {
19
+ onChange(e.target.value);
20
+ };
21
+
22
+ return(
23
+ <div className={wrapperClassName}>
24
+ <label htmlFor={id} className={labelClassName}>{label}</label>
25
+ <input id={id} className="next-radio" type="radio" value={value} name={name} checked={checked} onChange={handleChange} />
26
+ <span className="next-radio--styled" />
27
+ </div>
28
+ )
29
+
30
+ };
@@ -0,0 +1,39 @@
1
+ const InputSelect = ({ id, label, labelHidden, name, options, value, onChange }) => {
2
+
3
+ const optionElements = options.map((option) => {
4
+ return <option key={option.value} value={option.value}>{option.label}</option>;
5
+ });
6
+
7
+ const labelClassName = classNames({
8
+ 'next-label': true,
9
+ 'helper--visually-hidden': labelHidden
10
+ });
11
+
12
+ const handleChange = (e) => {
13
+ onChange(e.target.value);
14
+ };
15
+
16
+ return(
17
+ <div className="next-input-wrapper">
18
+ <label className={labelClassName} htmlFor={id}>{label}</label>
19
+ <div className="next-select__wrapper next-input--has-content">
20
+ <select className="next-select rule-field" id={id} name={name} value={value} onChange={handleChange}>
21
+ {optionElements}
22
+ </select>
23
+ <NextIcon name="next-chevron-down" size={12} />
24
+ </div>
25
+ </div>
26
+ )
27
+
28
+ };
29
+
30
+ InputSelect.propTypes = {
31
+ label: React.PropTypes.string.isRequired,
32
+ name: React.PropTypes.string,
33
+ options: React.PropTypes.arrayOf(
34
+ React.PropTypes.shape({
35
+ label: React.PropTypes.string.isRequired,
36
+ value: React.PropTypes.string.isRequired
37
+ })
38
+ ).isRequired
39
+ };
@@ -0,0 +1,59 @@
1
+ class InputText extends BaseInput {
2
+
3
+ render() {
4
+ const { errors, name, value, defaultValue, disabled, helpMessage, label, labelHidden, onChange, placeholder } = this.props;
5
+
6
+ const wrapperClassName = classNames({
7
+ 'next-input-wrapper': true,
8
+ 'next-input-wrapper--is-error': this.hasError()
9
+ });
10
+
11
+ const labelClassName = classNames({
12
+ 'next-label': true,
13
+ 'helper--visually-hidden': labelHidden
14
+ });
15
+
16
+ const handleChange = (e) => {
17
+ onChange(e.target.value);
18
+ };
19
+
20
+ let helpElement = null;
21
+ if(helpMessage) {
22
+ helpElement = <p className="next-input__help-text">{helpMessage}</p>;
23
+ }
24
+
25
+ return (
26
+ <div className={wrapperClassName}>
27
+ <label className={labelClassName} htmlFor={name}>{label}</label>
28
+ <input
29
+ id={name}
30
+ className="next-input"
31
+ disabled={disabled}
32
+ value={value}
33
+ defaultValue={defaultValue}
34
+ name={name}
35
+ onChange={handleChange}
36
+ placeholder={placeholder}
37
+ type="text"
38
+ />
39
+ {helpElement}
40
+ </div>
41
+ );
42
+ }
43
+ }
44
+
45
+ InputText.propTypes = {
46
+ errors: React.PropTypes.object,
47
+ label: React.PropTypes.string.isRequired,
48
+ name: React.PropTypes.string,
49
+ onChange: React.PropTypes.func,
50
+ placeholder: React.PropTypes.string,
51
+ helpMessage: React.PropTypes.string,
52
+ error: React.PropTypes.bool,
53
+ disabled: React.PropTypes.bool
54
+ };
55
+
56
+ InputText.defaultProps = {
57
+ errors: {},
58
+ disabled: false
59
+ };
@@ -0,0 +1,48 @@
1
+ class InputTextArea extends BaseInput {
2
+
3
+ render() {
4
+ const { errors, defaultValue, label, name, onChange, placeholder, helpMessage } = this.props;
5
+
6
+ const wrapperClassName = classNames({
7
+ 'next-input-wrapper': true,
8
+ 'next-input-wrapper--is-error': this.hasError()
9
+ });
10
+
11
+ const handleChange = (e) => {
12
+ onChange(e.target.value);
13
+ };
14
+
15
+ let helpElement = null;
16
+ if(helpMessage) {
17
+ helpElement = <p className="next-input__help-text">{helpMessage}</p>;
18
+ }
19
+
20
+ return (
21
+ <div className={wrapperClassName}>
22
+ <label className="next-label" htmlFor={name}>{label}</label>
23
+ <textarea
24
+ className="next-textarea"
25
+ defaultValue={defaultValue}
26
+ name={name}
27
+ onChange={handleChange}
28
+ placeholder={placeholder}
29
+ />
30
+ {helpElement}
31
+ </div>
32
+ );
33
+ }
34
+ };
35
+
36
+ InputTextArea.propTypes = {
37
+ errors: React.PropTypes.object,
38
+ defaultValue: React.PropTypes.string,
39
+ label: React.PropTypes.string.isRequired,
40
+ name: React.PropTypes.string.isRequired,
41
+ onChange: React.PropTypes.func.isRequired,
42
+ placeholder: React.PropTypes.string,
43
+ helpMessage: React.PropTypes.string
44
+ };
45
+
46
+ InputTextArea.defaultProps = {
47
+ errors: {},
48
+ };
@@ -0,0 +1,33 @@
1
+ const IconChevron = ({ size, direction, disabled }) => {
2
+
3
+ const iconClasses = classNames({
4
+ "next-icon": true,
5
+ "next-icon--blue": disabled === false,
6
+ "next-icon--sky-darker": disabled,
7
+ "next-icon--rotate-180": direction === 'previous',
8
+ ['next-icon--' + size]: true
9
+ });
10
+
11
+ return (
12
+ <svg
13
+ aria-labelledby="next-chevron"
14
+ role="img"
15
+ className={iconClasses}
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ viewBox="0 0 28 28">
18
+
19
+ <path d="M7,3.8l10.5,10.5L7,24.7l2.7,2.7L23,14.3L9.7,1C9.7,1,7,3.8,7,3.8z" />
20
+ </svg>
21
+ );
22
+ };
23
+
24
+ IconChevron.propTypes = {
25
+ direction: React.PropTypes.oneOf(['next', 'previous']).isRequired,
26
+ disabled: React.PropTypes.bool,
27
+ size: React.PropTypes.number
28
+ };
29
+
30
+ IconChevron.defaultProps = {
31
+ disabled: false,
32
+ size: 10
33
+ };
@@ -0,0 +1,18 @@
1
+ const NextIcon = ({ size, name }) => {
2
+
3
+ const className = classNames({
4
+ "next-icon": true,
5
+ ['next-icon--size-' + size]: true
6
+ });
7
+
8
+ return (
9
+ <svg className={className}>
10
+ <use xlinkHref={'#' + name} />
11
+ </svg>
12
+ );
13
+ };
14
+
15
+ NextIcon.propTypes = {
16
+ size: React.PropTypes.number,
17
+ name: React.PropTypes.string
18
+ };
@@ -0,0 +1,21 @@
1
+ const InputSelectOld = ({ label, name, options, value, onChange }) => {
2
+
3
+ const id = name;
4
+
5
+ const optionElements = options.map((option) => {
6
+ return <option key={option.value} value={option.value}>{option.label}</option>;
7
+ });
8
+
9
+ const handleChange = (e) => {
10
+ onChange(e.target.value);
11
+ };
12
+
13
+ return(
14
+ <div className="next-input-wrapper next-input-wrapper--halved">
15
+ <select id={id} name={name} value={value} onChange={handleChange}>
16
+ {optionElements}
17
+ </select>
18
+ </div>
19
+ )
20
+
21
+ };
@@ -0,0 +1,29 @@
1
+ const UIAnnotatedSection = ({ title, description, children }) => {
2
+
3
+ return (
4
+ <section className="ui-annotated-section">
5
+
6
+ <div className="ui-annotated-section__annotation">
7
+ <div className="ui-annotated-section__title">
8
+ <h2 className="next-heading next-heading--no-margin">{title}</h2>
9
+ </div>
10
+
11
+ <div className="ui-annotated-section__description">
12
+ <p>{description}</p>
13
+ </div>
14
+ </div>
15
+
16
+ <div className="ui-annotated-section__content">
17
+ {children}
18
+ </div>
19
+
20
+ </section>
21
+ );
22
+
23
+ };
24
+
25
+ UIAnnotatedSection.propTypes = {
26
+ title: React.PropTypes.string.isRequired,
27
+ description: React.PropTypes.string,
28
+ children: React.PropTypes.node
29
+ };
@@ -0,0 +1,35 @@
1
+ const UIEmptyState = ({ title, subtitle, image, href, label }) => {
2
+
3
+ let imageSubsection = null;
4
+ if(image) {
5
+ imageSubsection = (
6
+ <div className="ui-empty-state__subsection">
7
+ <div className="ui-empty-state__items ui-empty-state__items--odd-queries ui-empty-state__items--quantity-queries">
8
+ <div className="ui-empty-state__item">
9
+ <div className="ui-empty-state__subitems">
10
+ <div className="ui-empty-state__subitem">
11
+ <img src={image} alt={title} />
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </div>
17
+ );
18
+ }
19
+
20
+ return(
21
+ <div className="ui-empty-state">
22
+ <div className="ui-empty-state__section">
23
+ <div className="ui-empty-state__subsection">
24
+ <h1 className="next-heading next-heading--xl">{title}</h1>
25
+ <h2 className="next-heading next-heading--subdued">{subtitle}</h2>
26
+ </div>
27
+ {imageSubsection}
28
+ <div className="ui-empty-state__subsection">
29
+ <a className="btn btn-large btn-primary" href={href}>{label}</a>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ )
34
+
35
+ };
@@ -0,0 +1,13 @@
1
+ const UIFooterHelp = ({ __html }) => {
2
+
3
+ return(
4
+ <div className="ui-footer-help">
5
+ <div className="ui-footer-help__content">
6
+ <div>
7
+ <p dangerouslySetInnerHTML={{__html: __html}} />
8
+ </div>
9
+ </div>
10
+ </div>
11
+ )
12
+
13
+ };
@@ -0,0 +1,39 @@
1
+ const UIPageActions = ({ label, disabled, secondaryHref, secondaryLabel }) => {
2
+
3
+ const buttonClassName = classNames({
4
+ 'btn': true,
5
+ 'btn-primary': !disabled,
6
+ 'disabled': disabled
7
+ });
8
+
9
+ let secondaryElement = null;
10
+ if(secondaryHref) {
11
+ secondaryElement = (
12
+ <div className="ui-page-actions__secondary">
13
+ <div className="button-group">
14
+ <a className="btn" href={secondaryHref}>{secondaryLabel}</a>
15
+ </div>
16
+ </div>
17
+ );
18
+ }
19
+
20
+ return (
21
+ <div className="ui-page-actions">
22
+ {secondaryElement}
23
+ <div className="ui-page-actions__primary">
24
+ <div className="button-group button-group--right-aligned">
25
+ <button name="button" type="submit" className={buttonClassName} disabled={disabled}>
26
+ {label}
27
+ </button>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ );
32
+ };
33
+
34
+ UIPageActions.propTypes = {
35
+ label: React.PropTypes.string.isRequired,
36
+ disabled: React.PropTypes.bool,
37
+ secondaryHref: React.PropTypes.string,
38
+ secondaryLabel: React.PropTypes.string
39
+ };
@@ -1,5 +1,2 @@
1
- /**
2
- * disco_app/components.js
3
- * React components common to Disco applications.
4
- */
1
+ //= require classnames
5
2
  //= require_tree ./components
@@ -3,5 +3,8 @@
3
3
  * Base Javascript for Disco applications.
4
4
  * Assumes that jQuery will be loaded in the parent application.js.
5
5
  */
6
+ //= require react
7
+ //= require react_ujs
6
8
  //= require bootstrap-sprockets
7
9
  //= require disco_app/shopify-turbolinks
10
+ //= require ./ui-kit
@@ -0,0 +1 @@
1
+ (function(){var t,e,n,s,a,i=function(t,e){return function(){return t.apply(e,arguments)}};t={STYLIZED:"next-input--stylized",SELECT_WRAPPER:"next-select__wrapper",TEXTAREA:"next-textarea",INPUT:"next-input",SELECT:"next-select",LABEL:"next-label"},n={LABEL:{FOCUSED:t.LABEL+"--is-focused"},INPUT:{FOCUSED:t.INPUT+"--is-focused"}},e=[t.INPUT,t.TEXTAREA,t.SELECT],s=function(){function s(){this.handleFocusChange=i(this.handleFocusChange,this),document.addEventListener("focus",this.handleFocusChange,!0),document.addEventListener("blur",this.handleFocusChange,!0)}return s.prototype.handleFocusChange=function(t){var s,a,i,o,r,c,l,u,d;for(u=t.target,a=0,o=e.length;o>a;a++)l=e[a],d=u.classList.contains(l);return d?(c=this.findLabelAndParent(u),i=c[0],r=c[1],s="focus"===t.type,i.classList.toggle(n.LABEL.FOCUSED,s),r.classList.toggle(n.INPUT.FOCUSED,s)):void 0},s.prototype.findLabelAndParent=function(e){var n,s;return n=document.querySelectorAll("label[for='"+e.getAttribute("id")+"']")[0],s=this._findParentNodeByClass(t.SELECT_WRAPPER,e),s||(s=this._findParentNodeByClass(t.STYLIZED,e)),[n,s]},s.prototype._findParentNodeByClass=function(t,e){var n;for(n=e.parentNode;!n.classList.contains(t);)n=n.parentNode;return n.classList.contains(t)?n:void 0},s}(),a=new s}).call(this),function(){}.call(this);
@@ -0,0 +1,66 @@
1
+ //
2
+ // header.scss
3
+ // Styles for admin header.
4
+ // --------------------------------------------------
5
+
6
+ .header-row {
7
+ display: block;
8
+ box-sizing: border-box;
9
+ position: fixed;
10
+ top: 0;
11
+ z-index: 198;
12
+ left: 230px;
13
+ right: 0;
14
+ height: 56px;
15
+ max-width: 100vw;
16
+ box-shadow: 0 1px 0 rgba(0,0,0,0.07);
17
+ background: #ffffff;
18
+ }
19
+
20
+ .header {
21
+ position: relative;
22
+ height: 56px;
23
+ padding: 0 20px;
24
+ display: -webkit-box;
25
+ display: -webkit-flex;
26
+ display: -ms-flexbox;
27
+ display: flex;
28
+ -webkit-box-orient: horizontal;
29
+ -webkit-box-direction: normal;
30
+ -webkit-flex-direction: row;
31
+ -ms-flex-direction: row;
32
+ flex-direction: row;
33
+ -webkit-box-align: center;
34
+ -webkit-align-items: center;
35
+ -ms-flex-align: center;
36
+ align-items: center;
37
+ }
38
+
39
+ .header__main {
40
+ -webkit-box-flex: 1;
41
+ -webkit-flex: 1 0 0%;
42
+ -ms-flex: 1 0 0%;
43
+ flex: 1 0 0%;
44
+ -webkit-box-ordinal-group: 2;
45
+ -webkit-order: 1;
46
+ -ms-flex-order: 1;
47
+ order: 1;
48
+ font-size: 24px;
49
+ text-align: left;
50
+ margin: 0;
51
+ padding: 10px 0;
52
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
53
+ font-weight: 300;
54
+ white-space: nowrap;
55
+ text-overflow: ellipsis;
56
+ overflow: hidden;
57
+ z-index: 2;
58
+ }
59
+
60
+ .breadcrumb {
61
+ color: #798c9c;
62
+ }
63
+
64
+ .breadcrumb a, .breadcrumb a:hover {
65
+ color: #798c9c;
66
+ }
@@ -0,0 +1,40 @@
1
+ //
2
+ // layout.scss
3
+ // Styles for admin layout.
4
+ // --------------------------------------------------
5
+
6
+ body {
7
+ line-height: 18px;
8
+ font-size: 13px;
9
+ color: #31373d;
10
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
11
+ text-rendering: optimizeLegibility;
12
+ }
13
+
14
+ html, body {
15
+ height: 100%;
16
+ margin: 0;
17
+ padding: 0;
18
+ border: 0;
19
+ background: #ebeef0;
20
+ }
21
+
22
+ #wrapper {
23
+ min-height: 100%;
24
+ background: #ebeef0;
25
+ }
26
+
27
+ #content {
28
+ padding: 56px 0 0 230px;
29
+ width: 100%;
30
+ box-sizing: border-box;
31
+ outline: 0;
32
+ display: block;
33
+ max-width: 3000px;
34
+ margin: 0 auto;
35
+ position: relative;
36
+ -webkit-transition: -webkit-transform 200ms ease;
37
+ transition: -webkit-transform 200ms ease;
38
+ transition: transform 200ms ease;
39
+ transition: transform 200ms ease, -webkit-transform 200ms ease;
40
+ }