From 0ed46572ad04dc81dd9f528216b410030293bd64 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Tue, 12 Mar 2019 09:54:52 +0900 Subject: [PATCH 01/35] Add schema to properties passed to callbacks --- packages/vulcan-lib/lib/server/mutators.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index fb20a00dc..2d348efd3 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -77,7 +77,7 @@ export const createMutator = async ({ Note: keep newDocument for backwards compatibility */ - const properties = { data, currentUser, collection, context, document, newDocument: document }; + const properties = { data, currentUser, collection, context, document, newDocument: document, schema }; /* @@ -267,7 +267,7 @@ export const updateMutator = async ({ Properties */ - const properties = { data, oldDocument, document, currentUser, collection, context }; + const properties = { data, oldDocument, document, currentUser, collection, context, schema }; /* @@ -467,7 +467,7 @@ export const deleteMutator = async ({ Properties */ - const properties = { document, currentUser, collection, context }; + const properties = { document, currentUser, collection, context, schema }; /* From ab9bc3b73aa8016fc6976fd14274573bc07c53cb Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Tue, 12 Mar 2019 21:54:27 +0900 Subject: [PATCH 02/35] Fix delete error --- packages/vulcan-lib/lib/server/mutators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index 2d348efd3..a64441583 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -578,7 +578,7 @@ const startDebugMutator = (name, action, properties) => { }); }; -const endDebugMutator = (name, action, properties) => { +const endDebugMutator = (name, action, properties = {}) => { Object.keys(properties).forEach(p => { debug(`// ${p}: `, properties[p]); }); From 26899c7ff6a3d63612afb1b193ad77daf9283cfc Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Tue, 12 Mar 2019 21:54:50 +0900 Subject: [PATCH 03/35] Do not call mergeSchema unless necessary --- packages/vulcan-lib/lib/server/apollo-server/initGraphQL.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vulcan-lib/lib/server/apollo-server/initGraphQL.js b/packages/vulcan-lib/lib/server/apollo-server/initGraphQL.js index 789ccbe62..e0e73f481 100644 --- a/packages/vulcan-lib/lib/server/apollo-server/initGraphQL.js +++ b/packages/vulcan-lib/lib/server/apollo-server/initGraphQL.js @@ -75,7 +75,8 @@ const initGraphQL = () => { resolvers: GraphQLSchema.resolvers, schemaDirectives: GraphQLSchema.directives, }); - const mergedSchema = mergeSchemas({ schemas: [executableSchema, ...GraphQLSchema.stitchedSchemas] }); + // only call mergeSchemas if we actually have stitchedSchemas + const mergedSchema = GraphQLSchema.stitchedSchemas.length > 0 ? mergeSchemas({ schemas: [executableSchema, ...GraphQLSchema.stitchedSchemas] }) : executableSchema; GraphQLSchema.finalSchema = typeDefs; GraphQLSchema.executableSchema = mergedSchema; From 448f9c16b8b540c46865d2c4f59f17070c36596f Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Tue, 12 Mar 2019 21:57:11 +0900 Subject: [PATCH 04/35] Simplify error handling --- packages/vulcan-lib/lib/modules/errors.js | 46 +++++++++-------------- packages/vulcan-lib/lib/server/errors.js | 4 +- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/packages/vulcan-lib/lib/modules/errors.js b/packages/vulcan-lib/lib/modules/errors.js index 3f350abea..24a74f20b 100644 --- a/packages/vulcan-lib/lib/modules/errors.js +++ b/packages/vulcan-lib/lib/modules/errors.js @@ -18,6 +18,8 @@ const getFirstWord = input => { Parse a GraphQL error message +TODO: check if still useful? + Sample message: "GraphQL error: Variable "$data" got invalid value {"meetingDate":"2018-08-07T06:05:51.704Z"}. @@ -28,6 +30,11 @@ In field "addresses": Expected "[JSON]!", found null." */ const parseErrorMessage = message => { + + if (!message) { + return null; + } + // note: optionally add .slice(1) at the end to get rid of the first error, which is not that helpful let fieldErrors = message.split('\n'); @@ -52,6 +59,7 @@ const parseErrorMessage = message => { }); return fieldErrors; }; + /* Errors can have the following properties stored on their `data` property: @@ -60,37 +68,17 @@ Errors can have the following properties stored on their `data` property: - properties: additional data. Will be passed to vulcan-i18n as values - message: if id cannot be used as i81n key, message will be used -Scenario 1: normal error thrown with new Error(), put it in array and return it - -Scenario 2: multiple GraphQL errors stored on data.errors - -Scenario 3: single GraphQL error with data property - -Scenario 4: single GraphQL error with no data property - */ export const getErrors = error => { - // 1. by default, return raw error wrapped in array - let errors = [error]; + const graphQLErrors = error.graphQLErrors; + + // error thrown using new ApolloError + const apolloErrors = get(graphQLErrors, '0.extensions.exception.data.errors'); - // if this is one or more GraphQL errors, extract and convert them - if (error.graphQLErrors && error.graphQLErrors.length > 0) { - // get first graphQL error (see https://github.com/thebigredgeek/apollo-errors/issues/12) - const graphQLError = error.graphQLErrors[0]; - const data = get(graphQLError, 'extensions.exception.data') - if (data && !isEmpty(data)) { - if (data.errors) { - // 2. there are multiple errors on the data.errors object - errors = data.errors; - } else { - // 3. there is only one error - errors = [data]; - } - } else { - // 4. there is no data object, try to parse raw error message - errors = parseErrorMessage(graphQLError.message); - } - } - return errors; + // regular server error (with schema stitching) + const regularErrors = get(graphQLErrors, '0.extensions.exception.errors'); + + return apolloErrors || regularErrors || graphQLErrors; + }; diff --git a/packages/vulcan-lib/lib/server/errors.js b/packages/vulcan-lib/lib/server/errors.js index cd7d70e8b..000b3870d 100644 --- a/packages/vulcan-lib/lib/server/errors.js +++ b/packages/vulcan-lib/lib/server/errors.js @@ -1,4 +1,4 @@ -import { ApolloError } from 'apollo-server'; +import { UserInputError } from 'apollo-server'; /* @@ -11,5 +11,5 @@ An error should have: */ export const throwError = error => { const { id, } = error; - throw new ApolloError(id, 'VALIDATION_ERROR', error); + throw new UserInputError(id, error); }; From a7430225458a18a3061496de760b93ebcd2a2c4b Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 09:34:30 +0100 Subject: [PATCH 05/35] material-ui --- .eslintrc | 2 +- package-lock.json | 64 +- packages/vulcan-ui-material/.editorconfig | 15 + packages/vulcan-ui-material/.eslintrc | 86 +++ packages/vulcan-ui-material/.gitignore | 3 + packages/vulcan-ui-material/.versions | 89 +++ packages/vulcan-ui-material/accounts.css | 18 + packages/vulcan-ui-material/client/main.js | 3 + .../client/wrapWithMuiTheme.jsx | 29 + .../components/accounts/AccountsButton.jsx | 46 ++ .../components/accounts/AccountsButtons.jsx | 48 ++ .../components/accounts/AccountsField.jsx | 114 ++++ .../components/accounts/AccountsFields.jsx | 27 + .../components/accounts/AccountsForm.jsx | 68 ++ .../accounts/AccountsPasswordOrService.jsx | 57 ++ .../accounts/AccountsSocialButtons.jsx | 28 + .../components/bonus/LoadMore.jsx | 133 ++++ .../components/bonus/ScrollTrigger.jsx | 125 ++++ .../components/bonus/SearchInput.jsx | 240 +++++++ .../components/bonus/TooltipIconButton.jsx | 108 +++ .../components/bonus/TooltipIntl.jsx | 168 +++++ .../components/core/Card.jsx | 212 ++++++ .../components/core/Datatable.jsx | 644 ++++++++++++++++++ .../components/core/EditButton.jsx | 86 +++ .../components/core/Loading.jsx | 9 + .../components/core/ModalTrigger.jsx | 150 ++++ .../components/core/NewButton.jsx | 44 ++ .../components/forms/FormComponentInner.jsx | 129 ++++ .../components/forms/FormErrors.jsx | 51 ++ .../components/forms/FormGroup.jsx | 205 ++++++ .../components/forms/FormGroupNone.jsx | 95 +++ .../components/forms/FormGroupWithLine.jsx | 198 ++++++ .../components/forms/FormNested.jsx | 13 + .../components/forms/FormNestedDivider.jsx | 29 + .../components/forms/FormNestedFoot.jsx | 20 + .../components/forms/FormNestedHead.jsx | 13 + .../components/forms/FormSubmit.jsx | 136 ++++ .../forms/base-controls/EndAdornment.jsx | 84 +++ .../forms/base-controls/MuiCheckboxGroup.jsx | 150 ++++ .../forms/base-controls/MuiFormControl.jsx | 91 +++ .../forms/base-controls/MuiFormHelper.jsx | 75 ++ .../forms/base-controls/MuiInput.jsx | 138 ++++ .../forms/base-controls/MuiRadioGroup.jsx | 162 +++++ .../forms/base-controls/MuiSelect.jsx | 210 ++++++ .../forms/base-controls/MuiSuggest.jsx | 437 ++++++++++++ .../forms/base-controls/MuiSwitch.jsx | 71 ++ .../forms/base-controls/StartAdornment.jsx | 54 ++ .../forms/base-controls/mixins/component.jsx | 139 ++++ .../components/forms/controls/Checkbox.jsx | 10 + .../forms/controls/CheckboxGroup.jsx | 10 + .../forms/controls/CountrySelect.jsx | 11 + .../components/forms/controls/Date.jsx | 37 + .../components/forms/controls/DateRdt.jsx | 60 ++ .../components/forms/controls/DateTime.jsx | 37 + .../components/forms/controls/DateTimeRdt.jsx | 61 ++ .../components/forms/controls/Default.jsx | 10 + .../components/forms/controls/Email.jsx | 10 + .../components/forms/controls/Number.jsx | 10 + .../components/forms/controls/PostalCode.jsx | 15 + .../components/forms/controls/RadioGroup.jsx | 10 + .../forms/controls/RegionSelect.jsx | 31 + .../components/forms/controls/Select.jsx | 14 + .../forms/controls/SelectMultiple.jsx | 13 + .../components/forms/controls/Textarea.jsx | 15 + .../components/forms/controls/Time.jsx | 37 + .../components/forms/controls/TimeRdt.jsx | 73 ++ .../components/forms/controls/Url.jsx | 10 + .../components/forms/controls/countries.js | 402 +++++++++++ .../vulcan-ui-material/components/index.js | 58 ++ .../components/theme/JssCleanup.jsx | 23 + .../components/theme/ThemeStyles.jsx | 232 +++++++ .../components/ui/Alert.jsx | 31 + .../components/ui/Button.jsx | 51 ++ .../components/upload/UploadImage.jsx | 117 ++++ .../components/upload/UploadInner.jsx | 181 +++++ packages/vulcan-ui-material/en_US.js | 12 + .../vulcan-ui-material/example/Header.jsx | 98 +++ .../vulcan-ui-material/example/Layout.jsx | 136 ++++ .../example/SideNavigation.jsx | 105 +++ packages/vulcan-ui-material/forms.css | 16 + packages/vulcan-ui-material/fr_FR.js | 8 + packages/vulcan-ui-material/history.md | 109 +++ packages/vulcan-ui-material/modules/index.js | 4 + packages/vulcan-ui-material/modules/routes.js | 8 + .../vulcan-ui-material/modules/sampleTheme.js | 76 +++ packages/vulcan-ui-material/modules/themes.js | 67 ++ packages/vulcan-ui-material/package.js | 26 + packages/vulcan-ui-material/readme.md | 208 ++++++ packages/vulcan-ui-material/server/main.js | 3 + .../server/wrapWithMuiTheme.jsx | 43 ++ 90 files changed, 7582 insertions(+), 22 deletions(-) create mode 100644 packages/vulcan-ui-material/.editorconfig create mode 100755 packages/vulcan-ui-material/.eslintrc create mode 100644 packages/vulcan-ui-material/.gitignore create mode 100644 packages/vulcan-ui-material/.versions create mode 100644 packages/vulcan-ui-material/accounts.css create mode 100644 packages/vulcan-ui-material/client/main.js create mode 100644 packages/vulcan-ui-material/client/wrapWithMuiTheme.jsx create mode 100755 packages/vulcan-ui-material/components/accounts/AccountsButton.jsx create mode 100755 packages/vulcan-ui-material/components/accounts/AccountsButtons.jsx create mode 100755 packages/vulcan-ui-material/components/accounts/AccountsField.jsx create mode 100755 packages/vulcan-ui-material/components/accounts/AccountsFields.jsx create mode 100755 packages/vulcan-ui-material/components/accounts/AccountsForm.jsx create mode 100644 packages/vulcan-ui-material/components/accounts/AccountsPasswordOrService.jsx create mode 100644 packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx create mode 100644 packages/vulcan-ui-material/components/bonus/LoadMore.jsx create mode 100644 packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx create mode 100644 packages/vulcan-ui-material/components/bonus/SearchInput.jsx create mode 100644 packages/vulcan-ui-material/components/bonus/TooltipIconButton.jsx create mode 100644 packages/vulcan-ui-material/components/bonus/TooltipIntl.jsx create mode 100644 packages/vulcan-ui-material/components/core/Card.jsx create mode 100644 packages/vulcan-ui-material/components/core/Datatable.jsx create mode 100644 packages/vulcan-ui-material/components/core/EditButton.jsx create mode 100644 packages/vulcan-ui-material/components/core/Loading.jsx create mode 100644 packages/vulcan-ui-material/components/core/ModalTrigger.jsx create mode 100644 packages/vulcan-ui-material/components/core/NewButton.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormComponentInner.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormErrors.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormGroup.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormGroupNone.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormGroupWithLine.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormNested.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormNestedHead.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormSubmit.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/EndAdornment.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiCheckboxGroup.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiFormControl.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiFormHelper.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiSelect.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiSuggest.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/MuiSwitch.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/StartAdornment.jsx create mode 100644 packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Checkbox.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/CheckboxGroup.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/CountrySelect.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Date.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/DateRdt.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/DateTime.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Default.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Email.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Number.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/PostalCode.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/RadioGroup.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/RegionSelect.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Select.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/SelectMultiple.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Textarea.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Time.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/TimeRdt.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/Url.jsx create mode 100644 packages/vulcan-ui-material/components/forms/controls/countries.js create mode 100644 packages/vulcan-ui-material/components/index.js create mode 100644 packages/vulcan-ui-material/components/theme/JssCleanup.jsx create mode 100644 packages/vulcan-ui-material/components/theme/ThemeStyles.jsx create mode 100644 packages/vulcan-ui-material/components/ui/Alert.jsx create mode 100644 packages/vulcan-ui-material/components/ui/Button.jsx create mode 100755 packages/vulcan-ui-material/components/upload/UploadImage.jsx create mode 100755 packages/vulcan-ui-material/components/upload/UploadInner.jsx create mode 100644 packages/vulcan-ui-material/en_US.js create mode 100644 packages/vulcan-ui-material/example/Header.jsx create mode 100644 packages/vulcan-ui-material/example/Layout.jsx create mode 100644 packages/vulcan-ui-material/example/SideNavigation.jsx create mode 100644 packages/vulcan-ui-material/forms.css create mode 100644 packages/vulcan-ui-material/fr_FR.js create mode 100644 packages/vulcan-ui-material/history.md create mode 100644 packages/vulcan-ui-material/modules/index.js create mode 100755 packages/vulcan-ui-material/modules/routes.js create mode 100644 packages/vulcan-ui-material/modules/sampleTheme.js create mode 100644 packages/vulcan-ui-material/modules/themes.js create mode 100644 packages/vulcan-ui-material/package.js create mode 100644 packages/vulcan-ui-material/readme.md create mode 100644 packages/vulcan-ui-material/server/main.js create mode 100644 packages/vulcan-ui-material/server/wrapWithMuiTheme.jsx diff --git a/.eslintrc b/.eslintrc index e175ad797..845d06457 100644 --- a/.eslintrc +++ b/.eslintrc @@ -55,7 +55,7 @@ "avoid-escape" ], "react/prop-types": 0, - "semi": [1, "always"] + "semi": [2, "always"] }, "env": { "browser": true, diff --git a/package-lock.json b/package-lock.json index adbaeba4d..279f2043d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "Vulcan", - "version": "1.12.16", + "version": "1.12.17", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -702,7 +702,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -898,11 +897,12 @@ } }, "apollo-errors": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/apollo-errors/-/apollo-errors-1.5.1.tgz", - "integrity": "sha512-gYAceMzNJfF+mUHH2/4UcZTkZtDY54arCTKGbKa7WU5IXnTJ4V+P94wHodcDkLLHWpHL8SW1hEgjN5ZINcPb1w==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/apollo-errors/-/apollo-errors-1.9.0.tgz", + "integrity": "sha512-XVukHd0KLvgY6tNjsPS3/Re3U6RQlTKrTbIpqqeTMo2N34uQMr+H1UheV21o8hOZBAFosvBORVricJiP5vfmrw==", "requires": { - "es6-error": "^4.0.0" + "assert": "^1.4.1", + "extendable-error": "^0.1.5" } }, "apollo-link": { @@ -1289,6 +1289,14 @@ "safer-buffer": "~2.1.0" } }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + } + }, "assert-err": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assert-err/-/assert-err-1.1.0.tgz", @@ -2384,7 +2392,7 @@ }, "cheerio": { "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "resolved": "http://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", "requires": { "css-select": "~1.2.0", @@ -2831,7 +2839,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "requires": { "boolbase": "~1.0.0", @@ -3370,11 +3378,6 @@ "es6-symbol": "~3.1.1" } }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" - }, "es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", @@ -3977,6 +3980,11 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, + "extendable-error": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.5.tgz", + "integrity": "sha1-EiMIpwl7yJomOyxPvwiceBQOO20=" + }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -6168,8 +6176,7 @@ "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "optional": true + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=" }, "loose-envify": { "version": "1.3.1", @@ -6261,7 +6268,7 @@ "process": "^0.11.9", "punycode": "^1.4.1", "querystring-es3": "^0.2.1", - "readable-stream": "git+https://github.com/meteor/readable-stream.git", + "readable-stream": "git+https://github.com/meteor/readable-stream.git#c688cdd193549919b840e8d72a86682d91961e12", "stream-browserify": "^2.0.1", "string_decoder": "^1.0.1", "timers-browserify": "^1.4.2", @@ -6711,11 +6718,11 @@ "version": "git+https://github.com/meteor/readable-stream.git#c688cdd193549919b840e8d72a86682d91961e12", "from": "git+https://github.com/meteor/readable-stream.git", "requires": { - "inherits": "~2.0.3", + "inherits": "~2.0.1", "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.0", + "process-nextick-args": "~1.0.6", + "safe-buffer": "^5.0.1", + "string_decoder": "~1.0.0", "util-deprecate": "~1.0.1" } }, @@ -9717,6 +9724,21 @@ "os-homedir": "^1.0.0" } }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9852,7 +9874,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", diff --git a/packages/vulcan-ui-material/.editorconfig b/packages/vulcan-ui-material/.editorconfig new file mode 100644 index 000000000..68b3a5241 --- /dev/null +++ b/packages/vulcan-ui-material/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.js] +trim_trailing_whitespace = false + +[*.md] +trim_trailing_whitespace = false diff --git a/packages/vulcan-ui-material/.eslintrc b/packages/vulcan-ui-material/.eslintrc new file mode 100755 index 000000000..39277e6db --- /dev/null +++ b/packages/vulcan-ui-material/.eslintrc @@ -0,0 +1,86 @@ +{ + "extends": [ + "eslint:recommended", + //"airbnb", + "plugin:meteor/recommended", + "plugin:react/recommended" + ], + "parser": "babel-eslint", + "parserOptions": { + "allowImportExportEverywhere": true, + "ecmaVersion": 6, + "sourceType": "module" + }, + "rules": { + "babel/generator-star-spacing": 0, + "babel/new-cap": [1, { + "capIsNewExceptions": [ + "Optional", + "OneOf", + "Maybe", + "MailChimpAPI", + "Juice", + "Run", + "AppComposer", + "Query", + "InArray" + ] + }], + "babel/array-bracket-spacing": 0, + "babel/object-curly-spacing": 0, + "babel/object-shorthand": 0, + "babel/arrow-parens": 0, + "babel/no-await-in-loop": 1, + "comma-dangle": 0, + "key-spacing": 0, + "no-extra-boolean-cast": 0, + "no-undef": 1, + "no-unused-vars": [1, { + "vars": "all", + "args": "none", + "varsIgnorePattern": "React|PropTypes|Component" + }], + "react/prop-types": 0, + "react/display-name": 0, + "meteor/audit-argument-checks": 0, + "meteor/no-session": 0, + "no-case-declarations": 0, + "no-console": 0, + "semi": "error", + "quotes": [ + 1, + "single" + ] + }, + "env": { + "browser": true, + "commonjs": true, + "es6": true, + "meteor": true, + "node": true + }, + "plugins": [ + "babel", + "meteor", + "react", + "jsx-a11y" + ], + "settings": { + "import/resolver": { + "meteor": { + "paths": [ + "/usr/local/share/global_modules" + ], + "moduleDirectory": [ + "node_modules", + "packages" + ] + } + } + }, + "root": true, + "globals": { + "param": true, + "returns": true + } +} diff --git a/packages/vulcan-ui-material/.gitignore b/packages/vulcan-ui-material/.gitignore new file mode 100644 index 000000000..e0ac94aec --- /dev/null +++ b/packages/vulcan-ui-material/.gitignore @@ -0,0 +1,3 @@ +npm-debug.log +node_modules +.idea/workspace.xml diff --git a/packages/vulcan-ui-material/.versions b/packages/vulcan-ui-material/.versions new file mode 100644 index 000000000..5e93359dd --- /dev/null +++ b/packages/vulcan-ui-material/.versions @@ -0,0 +1,89 @@ +accounts-base@1.4.3 +allow-deny@1.1.0 +autoupdate@1.5.0 +babel-compiler@7.2.4 +babel-runtime@1.3.0 +base64@1.0.11 +binary-heap@1.0.11 +blaze-tools@1.0.10 +boilerplate-generator@1.6.0 +buffer@0.0.0 +caching-compiler@1.2.1 +caching-html-compiler@1.1.3 +callback-hook@1.1.0 +check@1.3.1 +ddp@1.4.0 +ddp-client@2.3.3 +ddp-common@1.4.0 +ddp-rate-limiter@1.0.7 +ddp-server@2.2.0 +deps@1.0.12 +diff-sequence@1.1.1 +dynamic-import@0.5.0 +ecmascript@0.12.4 +ecmascript-runtime@0.7.0 +ecmascript-runtime-client@0.8.0 +ecmascript-runtime-server@0.7.1 +ejson@1.1.0 +email@1.2.3 +erikdakoda:vulcan-material-ui@1.12.8_17 +es5-shim@4.8.0 +fetch@0.1.0 +fourseven:scss@4.10.0 +geojson-utils@1.0.10 +hot-code-push@1.0.4 +html-tools@1.0.11 +htmljs@1.0.11 +http@1.4.1 +id-map@1.1.0 +inter-process-messaging@0.1.0 +localstorage@1.2.0 +logging@1.1.20 +meteor@1.9.2 +meteorhacks:inject-initial@1.0.4 +meteorhacks:picker@1.0.3 +minifier-css@1.4.1 +minifier-js@2.4.0 +minimongo@1.4.5 +modern-browsers@0.1.3 +modules@0.13.0 +modules-runtime@0.10.3 +mongo@1.6.0 +mongo-decimal@0.1.0 +mongo-dev-server@1.1.0 +mongo-id@1.0.7 +npm-mongo@3.1.1 +ordered-dict@1.1.0 +percolatestudio:synced-cron@1.1.0 +promise@0.11.2 +random@1.1.0 +rate-limit@1.0.9 +reactive-dict@1.2.1 +reactive-var@1.0.11 +reload@1.2.0 +retry@1.1.0 +routepolicy@1.1.0 +server-render@0.3.1 +service-configuration@1.0.11 +session@1.2.0 +shell-server@0.4.0 +socket-stream-client@0.2.2 +spacebars-compiler@1.1.3 +standard-minifier-css@1.5.2 +standard-minifier-js@2.4.0 +static-html@1.2.2 +templating-tools@1.1.2 +tracker@1.2.0 +underscore@1.0.10 +url@1.2.0 +vulcan:accounts@1.12.8 +vulcan:core@1.12.8 +vulcan:debug@1.12.8 +vulcan:email@1.12.8 +vulcan:forms@1.12.8 +vulcan:i18n@1.12.8 +vulcan:lib@1.12.8 +vulcan:routing@1.12.8 +vulcan:users@1.12.8 +webapp@1.7.1 +webapp-hashing@1.0.9 diff --git a/packages/vulcan-ui-material/accounts.css b/packages/vulcan-ui-material/accounts.css new file mode 100644 index 000000000..73485abcf --- /dev/null +++ b/packages/vulcan-ui-material/accounts.css @@ -0,0 +1,18 @@ +.accounts-ui .form-control { + color: rgba(0, 0, 0, 0.87); + padding: 8px 0; + font-size: 1rem; + font-family: "Roboto", "Helvetica", "Arial", sans-serif; + border: none; + box-shadow: none; + border-bottom: 1px solid rgba(0, 0, 0, 0.87); + margin-bottom: 1px; + border-radius: 0; + width: 100%; +} + +.accounts-ui .form-control:focus { + border-bottom-width: 2px; + margin-bottom: 0; +} + diff --git a/packages/vulcan-ui-material/client/main.js b/packages/vulcan-ui-material/client/main.js new file mode 100644 index 000000000..1ed6f2fb0 --- /dev/null +++ b/packages/vulcan-ui-material/client/main.js @@ -0,0 +1,3 @@ +export * from '../components/index'; +export * from '../modules/index'; +import './wrapWithMuiTheme'; diff --git a/packages/vulcan-ui-material/client/wrapWithMuiTheme.jsx b/packages/vulcan-ui-material/client/wrapWithMuiTheme.jsx new file mode 100644 index 000000000..8b0c3baa7 --- /dev/null +++ b/packages/vulcan-ui-material/client/wrapWithMuiTheme.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { addCallback, registerComponent, Components } from 'meteor/vulcan:core'; +import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider'; +import { getCurrentTheme } from '../modules/themes'; +import JssCleanup from '../components/theme/JssCleanup'; + +class ThemeProvider extends React.Component { + render() { + const theme = getCurrentTheme(); + return ( + + {this.props.children} + + ); + } +} + +registerComponent('ThemeProvider', ThemeProvider); + +function wrapWithMuiTheme(app) { + return ( + + {app} + + ); +} + + +addCallback('router.client.wrapper', wrapWithMuiTheme); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsButton.jsx b/packages/vulcan-ui-material/components/accounts/AccountsButton.jsx new file mode 100755 index 000000000..d80dfe629 --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsButton.jsx @@ -0,0 +1,46 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import Button from '@material-ui/core/Button'; +import { replaceComponent, Utils } from 'meteor/vulcan:core'; +import classNames from 'classnames'; + + +export class AccountsButton extends Component { + render () { + + const { + label, + type, + disabled = false, + className, + onClick + } = this.props; + + return ( + + ); + } +} + + +AccountsButton.propTypes = { + label: PropTypes.string.isRequired, + type: PropTypes.oneOf(['link', 'submit', 'button']), + disabled: PropTypes.bool, + className: PropTypes.string, + onClick: PropTypes.func.isRequired, +}; + + +replaceComponent('AccountsButton', AccountsButton); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsButtons.jsx b/packages/vulcan-ui-material/components/accounts/AccountsButtons.jsx new file mode 100755 index 000000000..dc55093d9 --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsButtons.jsx @@ -0,0 +1,48 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import CardActions from '@material-ui/core/CardActions'; +import withStyles from '@material-ui/core/styles/withStyles'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + flexDirection: 'row-reverse', + padding: theme.spacing.unit * 2, + height: 'auto', + }, +}); + + +export class AccountsButtons extends Component { + render () { + + const { + classes, + buttons = {}, + className = 'buttons', + } = this.props; + + return ( + + {Object.keys(buttons).map((id, i) => + + )} + + ); + } +} + + +AccountsButtons.propTypes = { + classes: PropTypes.object.isRequired, + buttons: PropTypes.object, + className: PropTypes.string, +}; + + +AccountsButtons.displayName = 'AccountsButtons'; + + +replaceComponent('AccountsButtons', AccountsButtons, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsField.jsx b/packages/vulcan-ui-material/components/accounts/AccountsField.jsx new file mode 100755 index 000000000..4ac940a51 --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsField.jsx @@ -0,0 +1,114 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { replaceComponent } from 'meteor/vulcan:core'; +import TextField from '@material-ui/core/TextField'; + + +const autocompleteValues = { + 'username': 'username', + 'usernameOrEmail': 'email', + 'email': 'email', + 'password': 'current-password' +}; + + +export class AccountsField extends PureComponent { + + + constructor (props) { + super(props); + this.state = { + mount: true + }; + } + + + triggerUpdate () { + // Trigger an onChange on initial load, to support browser pre-filled values. + const { onChange } = this.props; + if (this.input && onChange) { + onChange({ target: { value: this.input.value } }); + } + } + + + componentDidMount () { + this.triggerUpdate(); + } + + + componentDidUpdate (prevProps) { + // Re-mount component so that we don't expose browser pre-filled passwords if the component was + // a password before and now something else. + if (prevProps.id !== this.props.id) { + this.setState({ mount: false }); + } else if (!this.state.mount) { + this.setState({ mount: true }); + this.triggerUpdate(); + } + } + + + render () { + const { + id, + hint, + label, + type = 'text', + onChange, + required = false, + className = 'field', + defaultValue = '', + autoFocus, + messages, + } = this.props; + let { message } = this.props; + const { mount = true } = this.state; + + if (type === 'notice') { + return
{label}
; + } + + const autoComplete = autocompleteValues[id]; + + if (messages && messages.find && typeof id === 'string') { + const foundMessage = messages.find(element => { + if (typeof element.field !== 'string') return false; + return id.toLowerCase().indexOf(element.field.toLowerCase()) > -1; + }); + if (foundMessage) { + message = foundMessage; + } + } + + return ( + mount && + +
+ { this.input = ref; }} + onChange={onChange} + placeholder={hint} + defaultValue={defaultValue} + autoComplete={autoComplete } + label={label} + autoFocus={autoFocus} + required={required} + error={!!message} + helperText={message && message.message} + fullWidth + /> +
+ ); + } +} + + +AccountsField.propTypes = { + onChange: PropTypes.func, +}; + + +replaceComponent('AccountsField', AccountsField); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsFields.jsx b/packages/vulcan-ui-material/components/accounts/AccountsFields.jsx new file mode 100755 index 000000000..59309c9a3 --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsFields.jsx @@ -0,0 +1,27 @@ +import React, { Component } from 'react'; +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import CardContent from '@material-ui/core/CardContent'; + + +export class AccountsFields extends Component { + render () { + const { + fields = {}, + className = 'fields', + messages, + } = this.props; + + return ( + + { + Object.keys(fields).map((id, i) => + + ) + } + + ); + } +} + + +replaceComponent('AccountsFields', AccountsFields); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsForm.jsx b/packages/vulcan-ui-material/components/accounts/AccountsForm.jsx new file mode 100755 index 000000000..af28fb37a --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsForm.jsx @@ -0,0 +1,68 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; + + +const styles = theme => ({ + messages: theme.utils.errorMessage, +}); + + +export class AccountsForm extends Component { + + + componentDidMount () { + let form = this.form; + if (form) { + form.addEventListener('submit', (e) => { + e.preventDefault(); + }); + } + } + + + render () { + const { + oauthServices, + fields, + buttons, + messages, + ready = true, + className, + classes, + } = this.props; + + return ( +
this.form = ref} + className={classNames(className, 'accounts-ui', { 'ready': ready, })} + noValidate + > + + + + + + + ); + } + + +} + + +AccountsForm.propTypes = { + oauthServices: PropTypes.object, + fields: PropTypes.object.isRequired, + buttons: PropTypes.object.isRequired, + error: PropTypes.string, + ready: PropTypes.bool, + classes: PropTypes.object.isRequired, +}; + + +AccountsForm.displayName = 'AccountsForm'; + + +registerComponent('AccountsForm', AccountsForm, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsPasswordOrService.jsx b/packages/vulcan-ui-material/components/accounts/AccountsPasswordOrService.jsx new file mode 100644 index 000000000..8f3a40667 --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsPasswordOrService.jsx @@ -0,0 +1,57 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { replaceComponent } from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import Typography from '@material-ui/core/Typography'; +import CardActions from '@material-ui/core/CardActions'; +import withStyles from '@material-ui/core/styles/withStyles'; +import classNames from 'classnames'; + +const styles = theme => ({ + root: { + flexDirection: 'row-reverse', + paddingRight: theme.spacing.unit * 2, + paddingLeft: theme.spacing.unit * 2, + height: 'auto', + }, + typography: { + marginRight: theme.spacing.unit, + } +}); + +export function hasPasswordService() { + // First look for OAuth services. + return !!Package['accounts-password']; +} + +export class AccountsPasswordOrService extends PureComponent { + render () { + let { className = 'password-or-service', style = {}, classes } = this.props; + const services = Object.keys(this.props.oauthServices).map(service => { + return this.props.oauthServices[service].label; + }); + let labels = services; + if (services.length > 2) { + labels = []; + } + + if (hasPasswordService() && services.length > 0) { + return ( + + { `${this.context.intl.formatMessage({id: 'accounts.or_use'})} ${ labels.join(' / ') }` } + + ); + } + return null; + } +} + +AccountsPasswordOrService.propTypes = { + oauthServices: PropTypes.object +}; + +AccountsPasswordOrService.contextTypes = { + intl: intlShape +}; + +replaceComponent('AccountsPasswordOrService', AccountsPasswordOrService, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx b/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx new file mode 100644 index 000000000..2ce19f254 --- /dev/null +++ b/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import CardActions from '@material-ui/core/CardActions' +import withStyles from '@material-ui/core/styles/withStyles' +import classNames from 'classnames' + +const styles = theme => ({ + root: { + justifyContent: 'flex-end', + padding: theme.spacing.unit * 2, + height: 'auto', + }, +}); + +export class AccountsSocialButtons extends React.Component { + render() { + let { oauthServices = {}, className = 'social-buttons', classes } = this.props; + return( + + {Object.keys(oauthServices).map((id, i) => { + return ; + })} + + ); + } +} + +replaceComponent('AccountsSocialButtons', AccountsSocialButtons, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/bonus/LoadMore.jsx b/packages/vulcan-ui-material/components/bonus/LoadMore.jsx new file mode 100644 index 000000000..934da0c7d --- /dev/null +++ b/packages/vulcan-ui-material/components/bonus/LoadMore.jsx @@ -0,0 +1,133 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage, intlShape } from 'react-intl'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import ArrowDownIcon from 'mdi-material-ui/ArrowDown'; +import ScrollTrigger from './ScrollTrigger'; +import classNames from 'classnames'; + + +const styles = theme => ({ + + root: { + textAlign: 'center', + flexBasis: '100%', + }, + + textButton: { + marginTop: theme.spacing.unit * 2, + }, + + iconButton: {}, + + caption: { + marginTop: theme.spacing.unit * 3, + paddingTop: theme.spacing.unit, + paddingBottom: theme.spacing.unit, + }, + +}); + + +const LoadMore = ({ + classes, + count, + totalCount, + loadMore, + networkStatus, + showCount, + useTextButton, + className, + infiniteScroll, + }, { intl }) => { + + const isLoadingMore = networkStatus === 2; + const loadMoreText = intl.formatMessage({ id: 'load_more.load_more' }); + const title = `${loadMoreText} (${count}/${totalCount})`; + const hasMore = totalCount > count; + const countValues = { count, totalCount }; + + const loadMoreButton = useTextButton + ? + + : + loadMore()}> + + ; + + return ( +
+ { + showCount && + + + + + } + { + isLoadingMore + + ? + + + + : + + hasMore + + ? + + infiniteScroll + + ? + + loadMore()}> + {loadMoreButton} + + + : + + loadMoreButton + : + + null + } +
+ ); +}; + + +LoadMore.propTypes = { + classes: PropTypes.object.isRequired, + count: PropTypes.number, + totalCount: PropTypes.number, + loadMore: PropTypes.func, + networkStatus: PropTypes.number, + showCount: PropTypes.bool, + useTextButton: PropTypes.bool, + className: PropTypes.string, + infiniteScroll: PropTypes.bool, +}; + + +LoadMore.defaultProps = { + showCount: true, +}; + + +LoadMore.contextTypes = { + intl: intlShape.isRequired, +}; + + +LoadMore.displayName = 'LoadMore'; + + +registerComponent('LoadMore', LoadMore, [withStyles, styles]); + diff --git a/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx b/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx new file mode 100644 index 000000000..0340f2536 --- /dev/null +++ b/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx @@ -0,0 +1,125 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; +import _throttle from 'lodash/throttle'; + + +class ScrollTrigger extends Component { + + constructor (props) { + super(props); + + this.onScroll = _throttle(this.onScroll.bind(this), 100, { + leading: true, + trailing: true, + }); + + this.onResize = _throttle(this.onResize.bind(this), 100, { + leading: true, + trailing: true, + }); + + this.inViewport = false; + this.passive = this.supportsPassive ? { passive: true } : false; + } + + supportsPassive () { + let supportsPassive = false; + try { + const opts = Object.defineProperty({}, 'passive', { + get: function() { + supportsPassive = true; + } + }); + window.addEventListener("testPassive", null, opts); + window.removeEventListener("testPassive", null, opts); + //eslint-disable-next-line no-empty + } catch (e) {} + return supportsPassive; + } + + componentDidMount () { + this.scroller = document.getElementById('main'); + this.scroller.addEventListener('resize', this.onResize, this.passive); + this.scroller.addEventListener('scroll', this.onScroll, this.passive); + + this.inViewport = false; + + if (this.props.triggerOnLoad) { + this.checkStatus(); + } + } + + componentWillUnmount () { + if (!this.scroller) return; + this.scroller.removeEventListener('resize', this.onResize); + this.scroller.removeEventListener('scroll', this.onScroll); + this.scroller = null; + } + + onResize () { + this.checkStatus(); + } + + onScroll () { + this.checkStatus(); + } + + checkStatus () { + if (!this.scroller) return; + + const { + onEnter, + } = this.props; + + //eslint-disable-next-line + const element = ReactDOM.findDOMNode(this.element); + const elementRect = element.getBoundingClientRect(); + const viewportEnd = this.scroller.clientHeight + this.props.preload; + const inViewport = elementRect.top < viewportEnd; + + if (inViewport) { + if (!this.inViewport) { + this.inViewport = true; + + onEnter(this); + } + + } else { + if (this.inViewport) { + this.inViewport = false; + } + } + } + + render () { + const { + children, + } = this.props; + + return ( +
{this.element = element;}}> + {children} +
+ ); + } +} + + +ScrollTrigger.propTypes = { + scrollerId: PropTypes.string, + triggerOnLoad: PropTypes.bool, + preload: PropTypes.number, + onEnter: PropTypes.func, +}; + + +ScrollTrigger.defaultProps = { + scrollerId: 'main', + preload: 1000, + triggerOnLoad: true, + onEnter: () => {}, +}; + + +export default ScrollTrigger; diff --git a/packages/vulcan-ui-material/components/bonus/SearchInput.jsx b/packages/vulcan-ui-material/components/bonus/SearchInput.jsx new file mode 100644 index 000000000..fa611fdd0 --- /dev/null +++ b/packages/vulcan-ui-material/components/bonus/SearchInput.jsx @@ -0,0 +1,240 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import SearchIcon from 'mdi-material-ui/Magnify'; +import ClearIcon from 'mdi-material-ui/CloseCircle'; +import Input from '@material-ui/core/Input'; +import NoSsr from '@material-ui/core/NoSsr'; +import classNames from 'classnames'; +import _debounce from 'lodash/debounce'; +import KeyboardEventHandler from 'react-keyboard-event-handler'; +import autosizeInput from 'autosize-input'; + +const styles = theme => ({ + + '@global': { + 'input[type=text]::-ms-clear, input[type=text]::-ms-reveal': + { + display: 'none', + width: 0, + height: 0, + }, + 'input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button': + { display: 'none' }, + 'input[type="search"]::-webkit-search-results-button, input[type="search"]::-webkit-search-results-decoration': + { display: 'none' }, + }, + + root: { + display: 'inline-flex', + backgroundColor: theme.palette.common.faintBlack, + borderRadius: 20, + padding: 6, + }, + + clear: { + transition: theme.transitions.create('opacity,transform', { + duration: theme.transitions.duration.short, + }), + opacity: 0.65, + width: 36, + height: 36, + margin: -6, + marginLeft: 0, + '& svg': { + width: 16, + height: 16, + }, + flexDirection: 'column', + }, + + clearDense: { + width: 32, + height: 32, + margin: -4, + marginLeft: 0, + }, + + clearDisabled: { + opacity: 0, + pointerEvents: 'none', + }, + + dense: { + padding: 4, + }, + + icon: { + color: theme.palette.common.lightBlack, + marginLeft: theme.spacing.unit, + marginRight: theme.spacing.unit, + }, + + input: { + lineHeight: 1, + paddingTop: 2, + paddingBottom: 2, + marginBottom: 1, + /*transition: theme.transitions.create('width', { + duration: theme.transitions.duration.shortest, + }),*/ + minWidth: 130, + }, + +}); + + +class SearchInput extends PureComponent { + + constructor (props) { + super(props); + + this.state = { + value: props.defaultValue || '', + }; + + this.input = null; + this.removeAutosize = null; + this.triggerResize = null; + + this.updateQuery = _debounce(this.updateQuery, 500); + } + + componentDidMount () { + if (!document) return; + const element = document.querySelector(`.search-input-${this.props.name} input[type=search]`); + + // We have to patch into addEventListener because autosizeInput provides no way to trigger resize + element._addEventListener = element.addEventListener; + element.addEventListener = function(type, listener, useCapture) { + if(useCapture === undefined) + useCapture = false; + this._addEventListener(type, listener, useCapture); + this.triggerResize = listener; + }; + + this.removeAutosize = autosizeInput(element); + + this.triggerResize = element.triggerResize; + element.addEventListener = element._addEventListener; + } + + componentWillUnmount () { + if (this.removeAutosize) { + this.removeAutosize(); + } + } + + handleShortcutKeys = (key, event) => { + switch (key) { + case 's': + this.focusInput(); + event.preventDefault(); + break; + case 'c': + case 'esc': + this.clearSearch(event, true); + event.preventDefault(); + break; + } + }; + + handleFocus = () => { + this.input.select(); + }; + + focusInput = (event) => { + this.input.focus(); + }; + + clearSearch = (event, dontFocus) => { + this.setState({ value: '' }, this.triggerResize); + this.updateQuery(''); + + if (!dontFocus) { + this.focusInput(); + } + }; + + updateSearch = (event) => { + const value = event.target.value; + this.setState({ value: value }); + this.updateQuery(value); + }; + + updateQuery = (value) => { + this.props.updateQuery(value); + }; + + render () { + const { + classes, + className, + dense, + noShortcuts, + name, + } = this.props; + + const searchIcon = ; + + const clearButton = } + onClick={this.clearSearch} + classes={{ + root: classNames(!this.state.value && classes.clearDisabled), + button: classNames('clear-button', classes.clear, dense && classes.clearDense), + }} + disabled={!this.state.value} + />; + + return ( + + this.input = input} + value={this.state.value} + type="search" + onChange={this.updateSearch} + onFocus={this.handleFocus} + disableUnderline={true} + startAdornment={searchIcon} + endAdornment={clearButton} + /> + + { + // KeyboardEventHandler is not valid on the server, where its name is undefined + typeof window !== 'undefined' && KeyboardEventHandler.name && !noShortcuts && + + + } + + + ); + } + +} + + +SearchInput.propTypes = { + classes: PropTypes.object.isRequired, + updateQuery: PropTypes.func.isRequired, + className: PropTypes.string, + dense: PropTypes.bool, + noShortcuts: PropTypes.bool, + name: PropTypes.string.isRequired, +}; + + +SearchInput.defaultProps = { + name: 'search', +}; + + +SearchInput.displayName = 'SearchInput'; + + +registerComponent('SearchInput', SearchInput, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/bonus/TooltipIconButton.jsx b/packages/vulcan-ui-material/components/bonus/TooltipIconButton.jsx new file mode 100644 index 000000000..45e3ba288 --- /dev/null +++ b/packages/vulcan-ui-material/components/bonus/TooltipIconButton.jsx @@ -0,0 +1,108 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent, Utils } from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import withStyles from '@material-ui/core/styles/withStyles'; +import withTheme from '@material-ui/core/styles/withTheme'; +import Tooltip from '@material-ui/core/Tooltip'; +import IconButton from '@material-ui/core/IconButton'; +import Button from '@material-ui/core/Button'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: {}, + tooltip: { + margin: '4px !important', + }, + buttonWrap: { + display: 'inline-block', + }, + button: {}, +}); + + +const TooltipIconButton = (props, { intl }) => { + + const { + title, + titleId, + placement, + icon, + className, + classes, + theme, + buttonRef, + variant, + ...properties + } = props; + + const titleText = props.title || intl.formatMessage({ id: titleId }); + const slug = Utils.slugify(titleId); + + return ( + +
+ { + variant === 'fab' + + ? + + + + : + + + {icon} + + } +
+
+ ); + +}; + + +TooltipIconButton.propTypes = { + title: PropTypes.node, + titleId: PropTypes.string, + placement: PropTypes.string, + icon: PropTypes.node.isRequired, + className: PropTypes.string, + classes: PropTypes.object, + buttonRef: PropTypes.func, + variant: PropTypes.string, + theme: PropTypes.object, +}; + + +TooltipIconButton.defaultProps = { + placement: 'bottom', +}; + + +TooltipIconButton.contextTypes = { + intl: intlShape.isRequired, +}; + + +TooltipIconButton.displayName = 'TooltipIconButton'; + + +registerComponent('TooltipIconButton', TooltipIconButton, [withStyles, styles], [withTheme]); diff --git a/packages/vulcan-ui-material/components/bonus/TooltipIntl.jsx b/packages/vulcan-ui-material/components/bonus/TooltipIntl.jsx new file mode 100644 index 000000000..5e21e277d --- /dev/null +++ b/packages/vulcan-ui-material/components/bonus/TooltipIntl.jsx @@ -0,0 +1,168 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent, Utils } from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import withStyles from '@material-ui/core/styles/withStyles'; +import withTheme from '@material-ui/core/styles/withTheme'; +import Tooltip from '@material-ui/core/Tooltip'; +import IconButton from '@material-ui/core/IconButton'; +import Button from '@material-ui/core/Button'; +import classNames from 'classnames'; + + +const styles = theme => ({ + + root: { + display: 'inherit', + }, + + tooltip: { + margin: '4px !important', + }, + + buttonWrap: { + display: 'inherit', + }, + + button: {}, + + icon: {}, + + popoverPopper: { + zIndex: 1700, + }, + + popoverTooltip: { + zIndex: 1701, + }, + +}); + + +const TooltipIntl = (props, { intl }) => { + + const { + title, + titleId, + titleValues, + placement, + icon, + className, + classes, + theme, + enterDelay, + leaveDelay, + buttonRef, + variant, + parent, + children, + ...properties + } = props; + + const iconWithClass = icon && React.cloneElement(icon, { className: classes.icon }); + const popperClass = parent === 'popover' && classes.popoverPopper; + const tooltipClass = parent === 'popover' && classes.popoverTooltip; + const tooltipEnterDelay = typeof enterDelay === 'number' ? enterDelay : theme.utils.tooltipEnterDelay; + const tooltipLeaveDelay = typeof leaveDelay === 'number' ? leaveDelay : theme.utils.tooltipLeaveDelay; + const titleText = props.title || intl.formatMessage({ id: titleId }, titleValues); + const slug = Utils.slugify(titleId); + + return ( + + + + { + variant === 'fab' && !!icon + + ? + + + + : + + !!icon + + ? + + + {iconWithClass} + + + : + + variant === 'button' + + ? + + + : + + children + } + + + + ); + +}; + + +TooltipIntl.propTypes = { + title: PropTypes.node, + titleId: PropTypes.string, + titleValues: PropTypes.object, + placement: PropTypes.string, + icon: PropTypes.node, + className: PropTypes.string, + classes: PropTypes.object, + buttonRef: PropTypes.func, + variant: PropTypes.string, + theme: PropTypes.object, + enterDelay: PropTypes.number, + leaveDelay: PropTypes.number, + parent: PropTypes.oneOf(['default', 'popover']), + children: PropTypes.node, +}; + + +TooltipIntl.defaultProps = { + placement: 'bottom', + parent: 'default', +}; + + +TooltipIntl.contextTypes = { + intl: intlShape.isRequired, +}; + + +TooltipIntl.displayName = 'TooltipIntl'; + + +registerComponent('TooltipIntl', TooltipIntl, [withStyles, styles], [withTheme]); diff --git a/packages/vulcan-ui-material/components/core/Card.jsx b/packages/vulcan-ui-material/components/core/Card.jsx new file mode 100644 index 000000000..a311c4841 --- /dev/null +++ b/packages/vulcan-ui-material/components/core/Card.jsx @@ -0,0 +1,212 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { intlShape } from 'meteor/vulcan:i18n'; +import { replaceComponent, Components } from 'meteor/vulcan:core'; +import moment from 'moment'; +import withStyles from '@material-ui/core/styles/withStyles'; +import IconButton from '@material-ui/core/IconButton'; +import Checkbox from '@material-ui/core/Checkbox'; +import EditIcon from 'mdi-material-ui/Pencil'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableRow from '@material-ui/core/TableRow'; +import TableCell from '@material-ui/core/TableCell'; +import classNames from 'classnames'; + + +const getLabel = (field, fieldName, collection, intl) => { + const schema = collection.simpleSchema()._schema; + const fieldSchema = schema[fieldName]; + if (fieldSchema) { + return intl.formatMessage( + { id: `${collection._name}.${fieldName}`, defaultMessage: fieldSchema.label }); + } else { + return fieldName; + } +}; + + +const getTypeName = (field, fieldName, collection) => { + const schema = collection.simpleSchema()._schema; + const fieldSchema = schema[fieldName]; + if (fieldSchema) { + const type = fieldSchema.type.singleType; + const typeName = typeof type === 'function' ? type.name : type; + return typeName; + } else { + return typeof field; + } +}; + + +const parseImageUrl = value => { + const isImage = ['.png', '.jpg', '.gif'].indexOf(value.substr(-4)) !== -1 || + ['.webp', '.jpeg'].indexOf(value.substr(-5)) !== -1; + return isImage ? + {value}/ : + ; +}; + + +const LimitedString = ({ string }) => +
+ {string.indexOf(' ') === -1 && string.length > 30 ? + {string.substr(0, 30)}… : + {string} + } +
; + + +export const getFieldValue = (value, typeName, classes={}) => { + + if (typeof value === 'undefined' || value === null) { + return ''; + } + + if (Array.isArray(value)) { + typeName = 'Array'; + } + + if (typeof typeName === 'undefined') { + typeName = typeof value; + } + + switch (typeName) { + + case 'Boolean': + case 'boolean': + return ; + + case 'Number': + case 'number': + case 'SimpleSchema.Integer': + return {value.toString()}; + + case 'Array': + return
    {value.map( + (item, index) =>
  1. {getFieldValue(item, typeof item, classes)}
  2. )}
; + + case 'Object': + case 'object': + return ( + + + {_.map(value, (value, key) => + + {key} + {getFieldValue(value, typeof value, classes)} + + )} + +
+ ); + + case 'Date': + return moment(new Date(value)).format('dddd, MMMM Do YYYY, h:mm:ss'); + + default: + return parseImageUrl(value); + } +}; + + +const CardItem = ({ label, value, typeName, classes }) => + + + {label} + + + {getFieldValue(value, typeName, classes)} + + ; + + +const CardEdit = (props, context) => { + const classes = props.classes; + const editTitle = context.intl.formatMessage({ id: 'cards.edit' }); + return ( + + + + + } + > + + + + + ); +}; + + +CardEdit.contextTypes = { intl: intlShape }; + + +const CardEditForm = ({ collection, document, closeModal }) => + { + closeModal(); + }} + />; + + +const styles = theme => ({ + root: {}, + table: { + maxWidth: '100%' + }, + tableBody: {}, + tableRow: {}, + tableCell: {}, + tableHeadCell: {}, +}); + + +const Card = ({ className, collection, document, currentUser, fields, classes }, { intl }) => { + + const fieldNames = fields ? fields : _.without(_.keys(document), '__typename'); + const canUpdate = currentUser && collection.options.mutations.update.check(currentUser, document); + + return ( +
+ + + {canUpdate ? : null} + {fieldNames.map((fieldName, index) => + + )} + +
+
+ ); +}; + + +Card.displayName = 'Card'; + + +Card.propTypes = { + className: PropTypes.string, + collection: PropTypes.object, + document: PropTypes.object, + currentUser: PropTypes.object, + fields: PropTypes.array, + classes: PropTypes.object.isRequired, +}; + + +Card.contextTypes = { + intl: intlShape +}; + + +replaceComponent('Card', Card, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/core/Datatable.jsx b/packages/vulcan-ui-material/components/core/Datatable.jsx new file mode 100644 index 000000000..982a404d6 --- /dev/null +++ b/packages/vulcan-ui-material/components/core/Datatable.jsx @@ -0,0 +1,644 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { + Components, + registerComponent, + replaceComponent, + withCurrentUser, + withMulti, + Utils +} from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import TableCell from '@material-ui/core/TableCell'; +import TableFooter from '@material-ui/core/TableFooter'; +import Tooltip from '@material-ui/core/Tooltip'; +import TableSortLabel from '@material-ui/core/TableSortLabel'; +import TablePagination from '@material-ui/core/TablePagination'; +import Toolbar from '@material-ui/core/Toolbar'; +import Typography from '@material-ui/core/Typography'; +import { getFieldValue } from './Card'; +import _assign from 'lodash/assign'; +import classNames from 'classnames'; + + +/* + +Datatable Component + +*/ +const baseStyles = theme => ({ + root: { + position: 'relative', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + }, + addButton: { + position: 'absolute', + top: '-8px', + right: 0, + }, + search: { + marginBottom: theme.spacing.unit * 8, + }, + table: {}, + denseTable: {}, + denserTable: {}, + flatTable: {}, + tableHead: {}, + tableBody: {}, + tableFooter: {}, + tableRow: {}, + tableHeadCell: {}, + tableCell: {}, + clickRow: {}, + editCell: {}, + editButton: {} +}); + +const delay = (function () { + var timer = 0; + return function (callback, ms) { + clearTimeout(timer); + timer = setTimeout(callback, ms); + }; +})(); + +class Datatable extends PureComponent { + + constructor (props) { + super(props); + + this.updateQuery = this.updateQuery.bind(this); + + this.state = { + value: '', + query: '', + currentSort: {}, + }; + } + + toggleSort = column => { + let currentSort; + if (!this.state.currentSort[column]) { + currentSort = { [column]: 1 }; + } else if (this.state.currentSort[column] === 1) { + currentSort = { [column]: -1 }; + } else { + currentSort = {}; + } + this.setState({ currentSort }); + }; + + updateQuery (value) { + this.setState({ + value: value + }); + delay(() => { + this.setState({ + query: value + }); + }, 700); + } + + render () { + if (this.props.data) { + + return ; + + } else { + + const { + className, + collection, + options, + showSearch, + showNew, + currentUser, + classes, + } = this.props; + + const listOptions = { + collection: collection, + ...options, + }; + + const DatatableWithMulti = withMulti(listOptions)(Components.DatatableContents); + + // add _id to orderBy when we want to sort a column, to avoid breaking the graphql() hoc; + // see https://github.com/VulcanJS/Vulcan/issues/2090#issuecomment-433860782 + // this.state.currentSort !== {} is always false, even when console.log(this.state.currentSort) displays + // {}. So we test on the length of keys for this object. + const orderBy = Object.keys(this.state.currentSort).length == 0 ? {} : + { ...this.state.currentSort, _id: -1 }; + + return ( +
+ {/* DatatableAbove Component part*/} + { + showSearch && + + + } + { + showNew && + + + } + + +
+ ); + } + } +} + + +Datatable.propTypes = { + title: PropTypes.string, + className: PropTypes.string, + collection: PropTypes.object, + options: PropTypes.object, + columns: PropTypes.array, + showEdit: PropTypes.bool, + editComponent: PropTypes.func, + showNew: PropTypes.bool, + showSearch: PropTypes.bool, + emptyState: PropTypes.node, + currentUser: PropTypes.object, + classes: PropTypes.object, + data: PropTypes.array, + footerData: PropTypes.array, + dense: PropTypes.string, + queryDataRef: PropTypes.func, + rowClass: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), + handleRowClick: PropTypes.func, + intlNamespace: PropTypes.string, + toggleSort: PropTypes.func, + currentSort: PropTypes.object, + paginate: PropTypes.bool, +}; + + +Datatable.defaultProps = { + showNew: true, + showEdit: true, + showSearch: true, + paginate: false, +}; + + +replaceComponent('Datatable', Datatable, withCurrentUser, [withStyles, baseStyles]); + + +/* + +DatatableContents Component + +*/ +const datatableContentsStyles = theme => (_assign({}, baseStyles(theme), { + table: { + marginTop: theme.spacing.unit * 3, + marginBottom: theme.spacing.unit * 3, + }, + denseTable: theme.utils.denseTable, + flatTable: theme.utils.flatTable, + denserTable: theme.utils.denserTable, +})); + + +const DatatableContents = ({ + collection, + columns, + results, + loading, + loadMore, + count, + totalCount, + networkStatus, + refetch, + showEdit, + editComponent, + emptyState, + currentUser, + classes, + footerData, + dense, + queryDataRef, + rowClass, + handleRowClick, + intlNamespace, + title, + toggleSort, + currentSort, + paginate, + paginationTerms, + setPaginationTerms + }) => { + + if (loading) { + return ; + } else if (!results || !results.length) { + return emptyState || null; + } + + if (queryDataRef) queryDataRef(this.props); + + const denseClass = dense && classes[dense + 'Table']; + + // Pagination functions + const getPage = (paginationTerms) => (parseInt((paginationTerms.limit - 1) / paginationTerms.itemsPerPage)); + + const onChangePage = (event, page) => { + setPaginationTerms({ + itemsPerPage: paginationTerms.itemsPerPage, + limit: (page + 1) * paginationTerms.itemsPerPage, + offset: page * paginationTerms.itemsPerPage + }); + }; + + const onChangeRowsPerPage = (event) => { + let value = event.target.value; + let offset = Math.max(0, parseInt((paginationTerms.limit - paginationTerms.itemsPerPage) / value) * value); + let limit = Math.min(offset + value, totalCount); + setPaginationTerms({ + itemsPerPage: value, + limit: limit, + offset: offset + }); + }; + + return ( + + { + title && + + + title + + + } + + + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + + ) + } + { + (showEdit || editComponent) && + + + } + + + + { + results && + + + { + results.map( + (document, index) => + ) + } + + } + + { + footerData && + + + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + + {footerData[index]} + + ) + } + { + (showEdit || editComponent) && + + + } + + + + } + +
+ { + paginate && + + + } + { + !paginate && loadMore && + + + } +
+ ); +}; + + +replaceComponent('DatatableContents', DatatableContents, [withStyles, datatableContentsStyles]); + + +/* + +DatatableHeader Component + +*/ +const DatatableHeader = ({ collection, intlNamespace, column, classes, toggleSort, currentSort }, { intl }) => { + const columnName = typeof column === 'string' ? column : column.name || column.label; + let formattedLabel = ''; + + if (collection) { + const schema = collection.simpleSchema()._schema; + + /* + use either: + + 1. the column name translation + 2. the column name label in the schema (if the column name matches a schema field) + 3. the raw column name. + */ + const defaultMessage = schema[columnName] ? schema[columnName].label : Utils.camelToSpaces(columnName); + formattedLabel = typeof columnName === 'string' ? + intl.formatMessage({ + id: `${collection._name}.${columnName}`, + defaultMessage: defaultMessage + }) : + ''; + + // if sortable is a string, use it as the name of the property to sort by. If it's just `true`, use + // column.name + const sortPropertyName = typeof column.sortable === 'string' ? column.sortable : column.name; + + if (column.sortable) { + return ; + } + } else if (intlNamespace) { + formattedLabel = typeof columnName === 'string' ? + intl.formatMessage({ + id: `${intlNamespace}.${columnName}`, + defaultMessage: columnName + }) : + ''; + } else { + formattedLabel = intl.formatMessage({ id: columnName, defaultMessage: columnName }); + } + + return {formattedLabel}; +}; + + +DatatableHeader.contextTypes = { + intl: intlShape, +}; + + +replaceComponent('DatatableHeader', DatatableHeader); + + +/* + +DatatableSorter Component + +*/ + +const DatatableSorter = ({ name, label, toggleSort, currentSort, sortable }) => + + + toggleSort(name)} + > + {label} + + + ; + +replaceComponent('DatatableSorter', DatatableSorter); + + +/* + +DatatableRow Component + +*/ +const datatableRowStyles = theme => (_assign({}, baseStyles(theme), { + clickRow: { + cursor: 'pointer', + }, + editCell: { + paddingTop: '0 !important', + paddingBottom: '0 !important', + textAlign: 'right', + }, +})); + + +const DatatableRow = ({ + collection, + columns, + document, + refetch, + showEdit, + editComponent, + currentUser, + rowClass, + handleRowClick, + classes, + }, { intl }) => { + + const EditComponent = editComponent; + + if (typeof rowClass === 'function') { + rowClass = rowClass(document); + } + + return ( + handleRowClick(event, document))} + hover + > + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + ) + } + + { + (showEdit || editComponent) && + + + { + EditComponent && + + + } + { + showEdit && + + + } + + } + + + ); +}; + + +replaceComponent('DatatableRow', DatatableRow, [withStyles, datatableRowStyles]); + + +DatatableRow.contextTypes = { + intl: intlShape +}; + + +/* + +DatatableCell Component + +*/ +const DatatableCell = ({ column, document, currentUser, classes }) => { + const Component = column.component || + Components[column.componentName] || + Components.DatatableDefaultCell; + + const columnName = typeof column === 'string' ? column : column.name; + const className = typeof columnName === 'string' ? + `datatable-item-${columnName.toLowerCase()}` : + ''; + const cellClass = typeof column.cellClass === 'function' ? + column.cellClass({ column, document, currentUser }) : + typeof column.cellClass === 'string' ? + column.cellClass : + null; + + return ( + + + + ); +}; + + +replaceComponent('DatatableCell', DatatableCell); + + +/* + +DatatableDefaultCell Component + +*/ +const DatatableDefaultCell = ({ column, document }) => +
+ { + typeof column === 'string' + ? + getFieldValue(document[column]) + : + getFieldValue(document[column.name]) + } +
; + + +replaceComponent('DatatableDefaultCell', DatatableDefaultCell); diff --git a/packages/vulcan-ui-material/components/core/EditButton.jsx b/packages/vulcan-ui-material/components/core/EditButton.jsx new file mode 100644 index 000000000..7e391a648 --- /dev/null +++ b/packages/vulcan-ui-material/components/core/EditButton.jsx @@ -0,0 +1,86 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import EditIcon from 'mdi-material-ui/Pencil'; + + +const EditButton = ({ + collection, + document, + color = 'default', + variant, + triggerClasses, + buttonClasses, + ...props + }, { intl }) => ( + + } + color={color} + variant={variant} + classes={buttonClasses} + />} + > + + +); + + +EditButton.propTypes = { + collection: PropTypes.object.isRequired, + document: PropTypes.object.isRequired, + color: PropTypes.oneOf(['default', 'inherit', 'primary', 'secondary']), + variant: PropTypes.string, + triggerClasses: PropTypes.object, + buttonClasses: PropTypes.object, +}; + + +EditButton.contextTypes = { + intl: intlShape +}; + + +EditButton.displayName = 'EditButton'; + + +registerComponent('EditButton', EditButton); + + +/* + +EditForm Component + +*/ +const EditForm = ({ collection, document, closeModal, options, successCallback, removeSuccessCallback,...props }) => { + + const success = successCallback + ? () => { + successCallback(); + closeModal(); + } + : closeModal; + + const remove = removeSuccessCallback + ? () => { + removeSuccessCallback(); + closeModal(); + } + : closeModal; + + return ( + +); +} + +registerComponent('EditForm', EditForm); diff --git a/packages/vulcan-ui-material/components/core/Loading.jsx b/packages/vulcan-ui-material/components/core/Loading.jsx new file mode 100644 index 000000000..dc821a032 --- /dev/null +++ b/packages/vulcan-ui-material/components/core/Loading.jsx @@ -0,0 +1,9 @@ +import React from 'react'; +import { replaceComponent } from 'meteor/vulcan:core'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +function Loading(props) { + return ; +} + +replaceComponent('Loading', Loading); diff --git a/packages/vulcan-ui-material/components/core/ModalTrigger.jsx b/packages/vulcan-ui-material/components/core/ModalTrigger.jsx new file mode 100644 index 000000000..8a3dbd159 --- /dev/null +++ b/packages/vulcan-ui-material/components/core/ModalTrigger.jsx @@ -0,0 +1,150 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { intlShape } from 'meteor/vulcan:i18n'; +import { registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Dialog from '@material-ui/core/Dialog'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Button from '@material-ui/core/Button'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + display: 'inline-block', + }, + button: {}, + anchor: {}, + dialog: {}, + dialogPaper: {}, + dialogTitle: {}, + dialogContent: { + paddingTop: '4px', + }, + dialogOverflow: { + overflowY: 'visible', + }, +}); + + +class ModalTrigger extends PureComponent { + + constructor (props) { + super(props); + + this.state = { modalIsOpen: false }; + + + } + + componentDidMount() { + if (this.props.action) { + this.props.action({ + openModal: this.openModal, + closeModal: this.closeModal, + }); + } + } + + openModal = () => { + this.setState({ modalIsOpen: true }); + }; + + closeModal = () => { + this.setState({ modalIsOpen: false }); + }; + + render () { + const { + className, + dialogClassName, + dialogOverflow, + labelId, + component, + titleId, + type, + children, + classes, + } = this.props; + + const intl = this.context.intl; + + const label = labelId ? intl.formatMessage({ id: labelId }) : this.props.label; + const title = titleId ? intl.formatMessage({ id: titleId }) : this.props.title; + const overflowClass = dialogOverflow && classes.dialogOverflow; + + const triggerComponent = component + ? + React.cloneElement(component, { onClick: this.openModal }) + : + type === 'button' + ? + + : + {label}; + + const childrenComponent = typeof children.type === 'function' ? + React.cloneElement(children, { closeModal: this.closeModal }) : + children; + + return ( + + + {triggerComponent} + + + + { + title && + + {title} + } + + + {childrenComponent} + + + + + + ); + } +} + + +ModalTrigger.propTypes = { + /** + * Callback fired when the component mounts. + * This is useful when you want to trigger an action programmatically. + * It supports `openModal()` and `closeModal()`. + * + * @param {object} actions This object contains all possible actions + * that can be triggered programmatically. + */ + action: PropTypes.func, + className: PropTypes.string, + dialogClassName: PropTypes.string, + dialogOverflow: PropTypes.bool, + label: PropTypes.string, + labelId: PropTypes.string, + component: PropTypes.object, + title: PropTypes.node, + titleId: PropTypes.string, + type: PropTypes.oneOf(['link', 'button']), + children: PropTypes.node, + classes: PropTypes.object, +}; + + +ModalTrigger.contextTypes = { + intl: intlShape, +}; + + +registerComponent('ModalTrigger', ModalTrigger, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/core/NewButton.jsx b/packages/vulcan-ui-material/components/core/NewButton.jsx new file mode 100644 index 000000000..1ce2cbf93 --- /dev/null +++ b/packages/vulcan-ui-material/components/core/NewButton.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import AddIcon from 'mdi-material-ui/Plus'; + + +const NewButton = ({ + className, + collection, + color = 'default', + variant, + }, { intl }) => ( + + } + color={color} + variant={variant} + />} + > + + +); + + +NewButton.propTypes = { + className: PropTypes.string, + collection: PropTypes.object.isRequired, + color: PropTypes.oneOf(['default', 'inherit', 'primary', 'secondary']), + variant: PropTypes.string, +}; + + +NewButton.contextTypes = { + intl: intlShape +}; + + +NewButton.displayName = 'NewButton'; + + +replaceComponent('NewButton', NewButton); diff --git a/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx b/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx new file mode 100644 index 000000000..473f39e6d --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx @@ -0,0 +1,129 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { intlShape } from 'meteor/vulcan:i18n'; +import { Components, registerComponent, instantiateComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import _omit from 'lodash/omit'; +import classNames from 'classnames'; + + +const styles = theme => ({ + + formInput: { + position: 'relative', + marginBottom: theme.spacing.unit * 3, + }, + + halfWidthLeft: { + display: 'inline-block', + width: '48%', + verticalAlign: 'top', + marginRight: '4%', + }, + + halfWidthRight: { + display: 'inline-block', + width: '48%', + verticalAlign: 'top', + }, + + thirdWidthLeft: { + display: 'inline-block', + width: '31%', + verticalAlign: 'top', + marginRight: '3.5%', + }, + + thirdWidthRight: { + display: 'inline-block', + width: '31%', + verticalAlign: 'top', + }, + + hidden: { + display: 'none', + }, + +}); + + +class FormComponentInner extends PureComponent { + + getProperties = () => { + return _omit(this.props, 'classes'); + }; + + render () { + const { + classes, + inputClassName, + name, + input, + hidden, + beforeComponent, + afterComponent, + formInput, + intlInput, + nestedInput, + formComponents, + } = this.props; + + const FormComponents = formComponents; + + const inputClass = classNames( + classes.formInput, + hidden && classes.hidden, + inputClassName && classes[inputClassName], + `input-${name}`, + `form-component-${input || 'default'}` + ); + + const properties = this.getProperties(); + + const FormInput = formInput; + + if (intlInput) { + return ; + } else if (nestedInput){ + return ; + } else { + return ( +
+ {instantiateComponent(beforeComponent, properties)} + + {instantiateComponent(afterComponent, properties)} +
+ ); + } + + } +} + + +FormComponentInner.contextTypes = { + intl: intlShape, +}; + + +FormComponentInner.propTypes = { + classes: PropTypes.object.isRequired, + inputClassName: PropTypes.string, + name: PropTypes.string.isRequired, + input: PropTypes.any, + beforeComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + afterComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + errors: PropTypes.array.isRequired, + help: PropTypes.node, + onChange: PropTypes.func.isRequired, + showCharsRemaining: PropTypes.bool.isRequired, + charsRemaining: PropTypes.number, + charsCount: PropTypes.number, + max: PropTypes.number, + formInput: PropTypes.func.isRequired, +}; + + +FormComponentInner.displayName = 'FormComponentInner'; + + +registerComponent('FormComponentInner', FormComponentInner, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormErrors.jsx b/packages/vulcan-ui-material/components/forms/FormErrors.jsx new file mode 100644 index 000000000..049d13ce1 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormErrors.jsx @@ -0,0 +1,51 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { replaceComponent, Components } from 'meteor/vulcan:core'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; + +import Snackbar from '@material-ui/core/Snackbar'; +import withStyles from '@material-ui/core/styles/withStyles'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + position: 'relative', + boxShadow: 'none', + marginBottom: theme.spacing.unit * 2 + }, + list: { + marginBottom: 0 + }, + error: { '& > div': { backgroundColor: theme.palette.error[500] } }, + danger: { '& > div': { backgroundColor: theme.palette.error[500] } }, + warning: { '& > div': { backgroundColor: theme.palette.error[500] } } +}); + + +const FormErrors = ({ errors, classes }) => { + const messageNode = ( +
    + {errors.map((error, index) => ( +
  • + +
  • + ))} +
+ ); + + return ( +
+ {!!errors.length && ( + + )} +
+ ); +}; + + +replaceComponent('FormErrors', FormErrors, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormGroup.jsx b/packages/vulcan-ui-material/components/forms/FormGroup.jsx new file mode 100644 index 000000000..3ad7781db --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormGroup.jsx @@ -0,0 +1,205 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Components, replaceComponent, instantiateComponent, } from 'meteor/vulcan:core'; +import Users from 'meteor/vulcan:users'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Collapse from '@material-ui/core/Collapse'; +import Typography from '@material-ui/core/Typography'; +import Paper from '@material-ui/core/Paper'; +import ExpandLessIcon from 'mdi-material-ui/ChevronUp'; +import ExpandMoreIcon from 'mdi-material-ui/ChevronDown'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + minWidth: '320px' + }, + paper: { + padding: theme.spacing.unit * 3 + }, + subtitle1: { + display: 'flex', + alignItems: 'center', + paddingLeft: theme.spacing.unit / 2, + marginTop: theme.spacing.unit * 5, + marginBottom: theme.spacing.unit, + color: theme.palette.primary[500], + }, + collapsible: { + cursor: 'pointer', + }, + label: {}, + toggle: { + '& svg': { + width: 21, + height: 21, + display: 'block', + } + }, + container: { + paddingLeft: 4, + paddingRight: 4, + marginLeft: -4, + marginRight: -4, + }, + entered: { + overflow: 'visible', + }, +}); + + +class FormGroup extends PureComponent { + + + constructor (props) { + super(props); + + this.isAdmin = props.name === 'admin'; + + this.state = { + collapsed: props.startCollapsed || this.isAdmin, + }; + } + + + toggle = () => { + const collapsible = this.props.collapsible || this.isAdmin; + if (!collapsible) return; + + this.setState({ + collapsed: !this.state.collapsed + }); + }; + + + renderHeading = () => { + const { classes, label } = this.props; + const collapsible = this.props.collapsible || this.isAdmin; + + return ( + + +
+ {label} +
+ + { + collapsible && + +
+ { + this.state.collapsed + ? + + : + + } +
+ } + +
+ ); + }; + + + // if at least one of the fields in the group has an error, the group as a whole has an error + hasErrors = () => _.some(this.props.fields, field => { + return !!this.props.errors.filter(error => error.path === field.path).length; + }); + + + render () { + const { + name, + hidden, + classes, + currentUser, + } = this.props; + + if (this.isAdmin && !Users.isAdmin(currentUser)) { + return null; + } + + if (typeof hidden === 'function' ? hidden({ ...this.props }) : hidden) { + return null; + } + + //do not display if no fields, no startComponent and no endComponent + if (!this.props.startComponent && !this.props.endComponent && !this.props.fields.length) { + return null; + } + + const anchorName = name.split('.').length > 1 ? name.split('.')[1] : name; + const collapseIn = !this.state.collapsed || this.hasErrors(); + + const FormComponents = this.props.formComponents; + + return ( + + ); + } + + +} + + +FormGroup.propTypes = { + name: PropTypes.string, + label: PropTypes.string, + order: PropTypes.number, + hidden: PropTypes.bool, + fields: PropTypes.array, + collapsible: PropTypes.bool, + startCollapsed: PropTypes.bool, + updateCurrentValues: PropTypes.func, + startComponent: PropTypes.node, + endComponent: PropTypes.node, + currentUser: PropTypes.object, +}; + + +replaceComponent('FormGroup', FormGroup, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormGroupNone.jsx b/packages/vulcan-ui-material/components/forms/FormGroupNone.jsx new file mode 100644 index 000000000..773b11eba --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormGroupNone.jsx @@ -0,0 +1,95 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { + Components, + registerComponent, + instantiateComponent, +} from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Users from 'meteor/vulcan:users'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + minWidth: '320px' + }, +}); + + +class FormGroupNone extends PureComponent { + + + render () { + const { + name, + hidden, + classes, + currentUser, + } = this.props; + + if (this.isAdmin && !Users.isAdmin(currentUser)) { + return null; + } + + if (typeof hidden === 'function' ? hidden({ ...this.props }) : hidden) { + return null; + } + + //do not display if no fields, no startComponent and no endComponent + if (!this.props.startComponent && !this.props.endComponent && !this.props.fields.length) { + return null; + } + + const anchorName = name.split('.').length > 1 ? name.split('.')[1] : name; + + const FormComponents = this.props.formComponents; + + return ( + + ); + } + + +} + + +FormGroupNone.propTypes = { + name: PropTypes.string, + order: PropTypes.number, + hidden: PropTypes.bool, + fields: PropTypes.array, + updateCurrentValues: PropTypes.func, + startComponent: PropTypes.node, + endComponent: PropTypes.node, + currentUser: PropTypes.object, +}; + + +registerComponent('FormGroupNone', FormGroupNone, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormGroupWithLine.jsx b/packages/vulcan-ui-material/components/forms/FormGroupWithLine.jsx new file mode 100644 index 000000000..7ea0c0965 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormGroupWithLine.jsx @@ -0,0 +1,198 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent, instantiateComponent } from 'meteor/vulcan:core'; +import Users from 'meteor/vulcan:users'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Collapse from '@material-ui/core/Collapse'; +import Divider from '@material-ui/core/Divider'; +import Typography from '@material-ui/core/Typography'; +import ExpandLessIcon from 'mdi-material-ui/ChevronUp'; +import ExpandMoreIcon from 'mdi-material-ui/ChevronDown'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + minWidth: '320px', + }, + divider: { + marginLeft: theme.spacing.unit * -3, + marginRight: theme.spacing.unit * -3, + }, + subtitle1: { + marginTop: theme.spacing.unit * 5, + position: 'relative', + }, + collapsible: { + cursor: 'pointer', + }, + typography: { + display: 'flex', + alignItems: 'center', + '& > div': { + display: 'flex', + alignItems: 'center', + }, + '& > div:first-child': { + ...theme.typography.subtitle1, + }, + paddingTop: theme.spacing.unit, + paddingBottom: theme.spacing.unit, + }, + toggle: { + color: theme.palette.action.active, + }, + entered: { + overflow: 'visible', + }, +}); + + +class FormGroupWithLine extends PureComponent { + + constructor (props) { + super(props); + + this.isAdmin = props.name === 'admin'; + + this.state = { + collapsed: props.startCollapsed || this.isAdmin, + }; + } + + + toggle = () => { + const collapsible = this.props.collapsible || this.isAdmin; + if (!collapsible) return; + + this.setState({ + collapsed: !this.state.collapsed + }); + }; + + + renderHeading = () => { + const { classes } = this.props; + const collapsible = this.props.collapsible || this.isAdmin; + + return ( +
+ + + + +
+ {this.props.label} +
+ { + collapsible && + +
+ { + this.state.collapsed + ? + + : + + } +
+ } +
+ +
+ ); + }; + + + // if at least one of the fields in the group has an error, the group as a whole has an error + hasErrors = () => _.some(this.props.fields, field => { + return !!this.props.errors.filter(error => error.path === field.path).length; + }); + + + render () { + const { + name, + hidden, + classes, + currentUser, + } = this.props; + + if (this.isAdmin && !Users.isAdmin(currentUser)) { + return null; + } + + if (typeof hidden === 'function' ? hidden({ ...this.props }) : hidden) { + return null; + } + + //do not display if no fields, no startComponent and no endComponent + if (!this.props.startComponent && !this.props.endComponent && !this.props.fields.length) { + return null; + } + + const anchorName = name.split('.').length > 1 ? name.split('.')[1] : name; + const collapseIn = !this.state.collapsed || this.hasErrors(); + + const FormComponents = this.props.formComponents; + + return ( +
+ ); + } +} + + +FormGroupWithLine.propTypes = { + name: PropTypes.string, + label: PropTypes.string, + order: PropTypes.number, + fields: PropTypes.array, + collapsible: PropTypes.bool, + startCollapsed: PropTypes.bool, + updateCurrentValues: PropTypes.func, + startComponent: PropTypes.node, + endComponent: PropTypes.node, + currentUser: PropTypes.object, +}; + + +registerComponent('FormGroupWithLine', FormGroupWithLine, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormNested.jsx b/packages/vulcan-ui-material/components/forms/FormNested.jsx new file mode 100644 index 000000000..2b3c7e5db --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormNested.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { replaceComponent } from 'meteor/vulcan:core'; +import Delete from 'mdi-material-ui/Delete'; +import Plus from 'mdi-material-ui/Plus'; + + +const IconRemove = () => ; + +replaceComponent('IconRemove', IconRemove); + +const IconAdd = () => ; + +replaceComponent('IconAdd', IconAdd); diff --git a/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx b/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx new file mode 100644 index 000000000..344ba4fdb --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { registerComponent } from 'meteor/vulcan:core'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Divider from '@material-ui/core/Divider'; + + +const styles = theme => ({ + + divider: { + marginLeft: -24, + marginRight: -24, + marginTop: 16, + marginBottom: 23, + }, + +}); + + +const FormNestedDivider = ({ classes, label, addItem }) => ; + +FormNestedDivider.propTypes = { + classes: PropTypes.object.isRequired, + label: PropTypes.string, + addItem: PropTypes.func, +}; + +registerComponent('FormNestedDivider', FormNestedDivider, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx b/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx new file mode 100644 index 000000000..7cb6b98eb --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import Grid from '@material-ui/core/Grid'; + +const FormNestedFoot = ({ label, addItem }) => ( + + + + + +); + +FormNestedFoot.propTypes = { + label: PropTypes.string, + addItem: PropTypes.func, +}; + +registerComponent('FormNestedFoot', FormNestedFoot); diff --git a/packages/vulcan-ui-material/components/forms/FormNestedHead.jsx b/packages/vulcan-ui-material/components/forms/FormNestedHead.jsx new file mode 100644 index 000000000..bd24a88d5 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormNestedHead.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { replaceComponent } from 'meteor/vulcan:core'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; + +const FormNestedHead = ({ label, addItem }) => ; + +FormNestedHead.propTypes = { + label: PropTypes.string, + addItem: PropTypes.func, +}; + +replaceComponent('FormNestedHead', FormNestedHead); diff --git a/packages/vulcan-ui-material/components/forms/FormSubmit.jsx b/packages/vulcan-ui-material/components/forms/FormSubmit.jsx new file mode 100644 index 000000000..b8b92a301 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormSubmit.jsx @@ -0,0 +1,136 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, replaceComponent } from 'meteor/vulcan:core'; +import { intlShape } from 'meteor/vulcan:i18n'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import DeleteIcon from 'mdi-material-ui/Delete'; +import Tooltip from '@material-ui/core/Tooltip'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import classNames from 'classnames'; + + +const styles = theme => ({ + root: { + textAlign: 'center', + marginTop: theme.spacing.unit * 4, + }, + button: { + margin: theme.spacing.unit, + }, + delete: { + float: 'left', + }, + tooltip: { + margin: 3, + } +}); + + +const FormSubmit = ({ + submitLabel, + cancelLabel, + cancelCallback, + revertLabel, + revertCallback, + document, + deleteDocument, + collectionName, + classes + }, { + intl, + isChanged, + clearForm, + }) => { + + if (typeof isChanged !== 'function') { + isChanged = () => true; + } + + return ( +
+ + { + deleteDocument + ? + + + + + + : + null + } + + { + cancelCallback + ? + + : + null + } + + { + revertCallback + ? + + : + null + } + + + +
+ ); +}; + + +FormSubmit.propTypes = { + submitLabel: PropTypes.node, + cancelLabel: PropTypes.node, + revertLabel: PropTypes.node, + cancelCallback: PropTypes.func, + revertCallback: PropTypes.func, + document: PropTypes.object, + deleteDocument: PropTypes.func, + collectionName: PropTypes.string, + classes: PropTypes.object, +}; + + +FormSubmit.contextTypes = { + intl: intlShape, + isChanged: PropTypes.func, + clearForm: PropTypes.func, +}; + + +replaceComponent('FormSubmit', FormSubmit, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/EndAdornment.jsx b/packages/vulcan-ui-material/components/forms/base-controls/EndAdornment.jsx new file mode 100644 index 000000000..4f136be08 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/EndAdornment.jsx @@ -0,0 +1,84 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { instantiateComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import IconButton from '@material-ui/core/IconButton'; +import CloseIcon from 'mdi-material-ui/CloseCircle'; +import classNames from 'classnames'; + + +export const styles = theme => ({ + inputAdornment: { + whiteSpace: 'nowrap', + marginTop: '0 !important', + '& > *': { + verticalAlign: 'bottom', + }, + '& > svg': { + color: theme.palette.common.darkBlack, + }, + '& > * + *': { + marginLeft: 8, + } + }, + clearButton: { + opacity: 0, + '& svg': { + width: 20, + height: 20, + }, + marginRight: -12, + marginLeft: -4, + '&:first-child': { + marginLeft: -12, + }, + transition: theme.transitions.create('opacity', { + duration: theme.transitions.duration.short, + }), + }, + urlButton: { + verticalAlign: 'bottom', + width: 24, + height: 24, + fontSize: 20, + } +}); + + +const EndAdornment = (props) => { + const { classes, value, addonAfter, changeValue, hideClear, disabled } = props; + + if (!addonAfter && (!changeValue || hideClear || disabled)) return null; + const hasValue = !!value || value === 0; + + const clearButton = changeValue && !hideClear && !disabled && + { + event.preventDefault(); + changeValue(null); + }} + tabIndex="-1" + > + + ; + + return ( + + {instantiateComponent(addonAfter)} + {clearButton} + + ); +}; + + +EndAdornment.propTypes = { + classes: PropTypes.object.isRequired, + value: PropTypes.any, + changeValue: PropTypes.func, + hideClear: PropTypes.bool, + addonAfter: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.func]), +}; + + +export default withStyles(styles)(EndAdornment); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiCheckboxGroup.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiCheckboxGroup.jsx new file mode 100644 index 000000000..4d80d8492 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiCheckboxGroup.jsx @@ -0,0 +1,150 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createReactClass from 'create-react-class'; +import ComponentMixin from './mixins/component'; +import withStyles from '@material-ui/core/styles/withStyles'; +import FormGroup from '@material-ui/core/FormGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; +import Checkbox from '@material-ui/core/Checkbox'; +import Switch from '@material-ui/core/Switch'; +import classNames from 'classnames'; + + +const styles = theme => ({ + group: { + marginTop: '8px', + }, + twoColumn: { + display: 'block', + [theme.breakpoints.down('md')]: { + '& > label': { + marginRight: theme.spacing.unit * 5, + }, + }, + [theme.breakpoints.up('md')]: { + '& > label': { + width: '49%', + }, + }, + }, + threeColumn: { + display: 'block', + [theme.breakpoints.down('xs')]: { + '& > label': { + marginRight: theme.spacing.unit * 5, + }, + }, + [theme.breakpoints.up('xs')]: { + '& > label': { + width: '49%', + }, + }, + [theme.breakpoints.up('md')]: { + '& > label': { + width: '32%', + }, + }, + }, +}); + + +const MuiCheckboxGroup = createReactClass({ + + mixins: [ComponentMixin], + + propTypes: { + name: PropTypes.string.isRequired, + options: PropTypes.array.isRequired, + classes: PropTypes.object.isRequired, + variant: PropTypes.oneOf(['checkbox', 'switch']), + }, + + componentDidMount: function () { + if (this.props.refFunction) { + this.props.refFunction(this); + } + }, + + getDefaultProps: function () { + return { + label: '', + help: null, + variant: 'checkbox', + }; + }, + + changeCheckbox: function () { + const value = []; + this.props.options.forEach(function (option, key) { + if (this[this.props.name + '-' + option.value].checked) { + value.push(option.value); + } + }.bind(this)); + //this.setValue(value); + this.props.onChange(this.props.name, value); + }, + + validate: function () { + if (this.props.onBlur) { + this.props.onBlur(); + } + return true; + }, + + renderElement: function () { + const controls = this.props.options.map((checkbox, key) => { + let value = checkbox.value; + let checked = (this.props.value.indexOf(value) !== -1); + let disabled = checkbox.disabled || this.props.disabled; + const Component = this.props.variant === 'switch' ? Switch : Checkbox; + + return ( + this[this.props.name + '-' + value] = c} + checked={checked} + onChange={this.changeCheckbox} + value={value} + disabled={disabled} + /> + } + label={checkbox.label} + /> + ); + }); + + const maxLength = this.props.options.reduce((max, option) => + option.label.length > max ? option.label.length : max, 0); + + const columnClass = maxLength < 20 ? 'threeColumn' : maxLength < 30 ? 'twoColumn' : ''; + + return ( + + {controls} + + ); + }, + + render: function () { + + if (this.props.layout === 'elementOnly') { + return ( +
{this.renderElement()}
+ ); + } + + return ( + + {this.renderElement()} + + + ); + } +}); + + +export default withStyles(styles)(MuiCheckboxGroup); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiFormControl.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiFormControl.jsx new file mode 100644 index 000000000..7b426355d --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiFormControl.jsx @@ -0,0 +1,91 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createReactClass from 'create-react-class'; +import InputLabel from '@material-ui/core/InputLabel'; +import FormControl from '@material-ui/core/FormControl'; +import FormLabel from '@material-ui/core/FormLabel'; + + +//noinspection JSUnusedGlobalSymbols +const MuiFormControl = createReactClass({ + + propTypes: { + label: PropTypes.node, + children: PropTypes.node, + required: PropTypes.bool, + hasErrors: PropTypes.bool, + fakeLabel: PropTypes.bool, + hideLabel: PropTypes.bool, + layout: PropTypes.oneOf(['horizontal', 'vertical', 'elementOnly']), + htmlFor: PropTypes.string + }, + + getDefaultProps: function () { + return { + label: '', + required: false, + hasErrors: false, + fakeLabel: false, + hideLabel: false, + }; + }, + + renderRequiredSymbol: function () { + if (this.props.required === false) { + return null; + } + return ( + * + ); + }, + + renderLabel: function () { + if (this.props.layout === 'elementOnly' || this.props.hideLabel) { + return null; + } + + if (this.props.fakeLabel) { + return ( + + {this.props.label} + {this.renderRequiredSymbol()} + + ); + } + + const shrink = ['date', 'time', 'datetime'].includes(this.props.inputType) ? true : undefined; + + return ( + + {this.props.label} + {this.renderRequiredSymbol()} + + ); + }, + + render: function () { + const { layout, className, children, hasErrors } = this.props; + + if (layout === 'elementOnly') { + return {children}; + } + + return ( + + {this.renderLabel()} + {children} + + ); + } + +}); + + +export default MuiFormControl; diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiFormHelper.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiFormHelper.jsx new file mode 100644 index 000000000..58a5f4e5d --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiFormHelper.jsx @@ -0,0 +1,75 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { instantiateComponent, Components } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import FormHelperText from '@material-ui/core/FormHelperText'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import classNames from 'classnames'; + + +export const styles = theme => ({ + + error: { + color: theme.palette.error.main, + }, + + formHelperText: { + display: 'flex', + '& :first-child': { + flexGrow: 1, + } + }, + +}); + + +const MuiFormHelper = (props) => { + const { + classes, + help, + errors, + hasErrors, + showCharsRemaining, + charsRemaining, + charsCount, + max, + } = props; + + if (!help && !hasErrors && !showCharsRemaining) { + return null; + } + + const errorMessage = hasErrors && + ; + + return ( + + + + { + hasErrors ? errorMessage : help + } + + + { + showCharsRemaining && + + + {charsCount} / {max} + + } + + + ); +}; + + +MuiFormHelper.propTypes = { + classes: PropTypes.object.isRequired, + value: PropTypes.any, + changeValue: PropTypes.func, + addonAfter: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.func]), +}; + + +export default withStyles(styles)(MuiFormHelper); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx new file mode 100644 index 000000000..0f5775c4f --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx @@ -0,0 +1,138 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createReactClass from 'create-react-class'; +import withStyles from '@material-ui/core/styles/withStyles'; +import ComponentMixin from './mixins/component'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; +import Input from '@material-ui/core/Input'; +import StartAdornment, { hideStartAdornment, fixUrl } from './StartAdornment'; +import EndAdornment from './EndAdornment'; + + +export const styles = theme => ({ + inputRoot: { + '& .clear-enabled': { opacity: 0 }, + '&:hover .clear-enabled': { opacity: 0.54 }, + }, + inputFocused: { + '& .clear-enabled': { opacity: 0.54 } + }, +}); + + +//noinspection JSUnusedGlobalSymbols +const MuiInput = createReactClass({ + element: null, + + mixins: [ComponentMixin], + + displayName: 'MuiInput', + + propTypes: { + type: PropTypes.oneOf([ + 'color', + 'date', + 'datetime', + 'datetime-local', + 'email', + 'hidden', + 'month', + 'number', + 'password', + 'range', + 'search', + 'tel', + 'text', + 'time', + 'url', + 'week' + ]), + errors: PropTypes.array, + placeholder: PropTypes.string, + formatValue: PropTypes.func, + scrubValue: PropTypes.func, + hideClear: PropTypes.bool, + }, + + getDefaultProps: function () { + return { + type: 'text', + }; + }, + + handleChange: function (event) { + let value = event.target.value; + if (this.props.scrubValue) { + value = this.props.scrubValue(value); + } + this.changeValue(value); + }, + + changeValue: function (value) { + this.props.onChange(this.props.name, value); + }, + + handleBlur: function (event) { + const { type, value } = this.props; + + if (type === 'url' && !!value && value !== fixUrl(value)) { + this.changeValue(fixUrl(value)) + } + }, + + render: function () { + const startAdornment = hideStartAdornment(this.props) ? null : + ; + const endAdornment = + ; + + let element = this.renderElement(startAdornment, endAdornment); + + if (this.props.layout === 'elementOnly' || this.props.type === 'hidden') { + return element; + } + + return ( + + {element} + + + ); + }, + + renderElement: function (startAdornment, endAdornment) { + const { classes, disabled, autoFocus, formatValue } = this.props; + const value = formatValue ? formatValue(this.props.value) : this.props.value; + const options = this.props.options || {}; + + return ( + (this.element = c)} + {...this.cleanProps(this.props)} + id={this.getId()} + value={value} + onChange={this.handleChange} + onBlur={this.handleBlur} + disabled={disabled} + rows={options.rows || this.props.rows} + autoFocus={options.autoFocus || autoFocus} + startAdornment={startAdornment} + endAdornment={endAdornment} + placeholder={this.props.placeholder} + classes={{ root: classes.inputRoot, focused: classes.inputFocused }} + /> + ); + }, + + +}); + + +export default withStyles(styles)(MuiInput); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx new file mode 100644 index 000000000..d111dd111 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx @@ -0,0 +1,162 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createReactClass from 'create-react-class'; +import ComponentMixin from './mixins/component'; +import withStyles from '@material-ui/core/styles/withStyles'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import classNames from 'classnames'; + + +const styles = theme => ({ + group: { + marginTop: '8px', + }, + inline: { + flexDirection: 'row', + '& > label': { + marginRight: theme.spacing.unit * 5, + }, + }, + twoColumn: { + display: 'block', + [theme.breakpoints.down('md')]: { + '& > label': { + marginRight: theme.spacing.unit * 5, + }, + }, + [theme.breakpoints.up('md')]: { + '& > label': { + width: '49%', + }, + }, + }, + threeColumn: { + display: 'block', + [theme.breakpoints.down('xs')]: { + '& > label': { + marginRight: theme.spacing.unit * 5, + }, + }, + [theme.breakpoints.up('xs')]: { + '& > label': { + width: '49%', + }, + }, + [theme.breakpoints.up('md')]: { + '& > label': { + width: '32%', + }, + }, + }, + radio: { + width: '32px', + height: '32px', + marginLeft: '8px', + }, + line: { + marginBottom: '12px', + }, +}); + + +const MuiRadioGroup = createReactClass({ + + mixins: [ComponentMixin], + + propTypes: { + name: PropTypes.string.isRequired, + type: PropTypes.oneOf(['inline', 'stacked']), + options: PropTypes.array.isRequired + }, + + getInitialState: function () { + if (this.props.refFunction) { + this.props.refFunction(this); + } + }, + + getDefaultProps: function () { + return { + type: 'stacked', + label: '', + help: null, + classes: PropTypes.object.isRequired, + }; + }, + + changeRadio: function (event) { + const value = event.target.value; + //this.setValue(value); + this.props.onChange(this.props.name, value); + }, + + validate: function () { + if (this.props.onBlur) { + this.props.onBlur(); + } + return true; + }, + + renderElement: function () { + const controls = this.props.options.map((radio, key) => { + let checked = (this.props.value === radio.value); + let disabled = radio.disabled || this.props.disabled; + + return ( + this['element-' + key] = c} + checked={checked} + disabled={disabled} + />} + className={this.props.classes.line} + label={radio.label} + /> + ); + }); + + const maxLength = this.props.options.reduce((max, option) => + option.label.length > max ? option.label.length : max, 0); + + let columnClass = maxLength < 18 ? 'threeColumn' : maxLength < 30 ? 'twoColumn' : ''; + if (this.props.type === 'inline') columnClass = 'inline'; + + return ( + + {controls} + + ); + }, + + render: function () { + + if (this.props.layout === 'elementOnly') { + return ( +
{this.renderElement()}
+ ); + } + + return ( + + {this.renderElement()} + + + ); + } +}); + + +export default withStyles(styles)(MuiRadioGroup); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiSelect.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiSelect.jsx new file mode 100644 index 000000000..6c50f1bbd --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiSelect.jsx @@ -0,0 +1,210 @@ +import withStyles from '@material-ui/core/styles/withStyles'; +import React from 'react'; +import createReactClass from 'create-react-class'; +import ComponentMixin from './mixins/component'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; +import Select from '@material-ui/core/Select'; +import Input from '@material-ui/core/Input'; +import MenuItem from '@material-ui/core/MenuItem'; +import MenuList from '@material-ui/core/MenuList'; +import ListSubheader from '@material-ui/core/ListSubheader'; +import StartAdornment, { hideStartAdornment } from './StartAdornment'; +import EndAdornment from './EndAdornment'; +import _isArray from 'lodash/isArray'; + + +export const styles = theme => ({ + + inputRoot: { + '& .clear-enabled': { opacity: 0 }, + '&:hover .clear-enabled': { opacity: 0.54 }, + }, + + inputFocused: { + '& .clear-enabled': { opacity: 0.54 } + }, + + menuItem: { + paddingTop: 4, + paddingBottom: 4, + paddingLeft: 9, + fontFamily: theme.typography.fontFamily, + color: theme.palette.type === 'light' ? 'rgba(0, 0, 0, 0.87)' : theme.palette.common.white, + fontSize: theme.typography.pxToRem(16), + lineHeight: '1.1875em', + }, + + input: { + paddingLeft: 8, + }, + +}); + + +const MuiSelect = createReactClass({ + + element: null, + + mixins: [ComponentMixin], + + getInitialState: function () { + return { + isOpen: false, + }; + }, + + handleOpen: function () { + // this doesn't work + this.setState({ + isOpen: true, + }); + }, + + handleClose: function () { + // this doesn't work + this.setState({ + isOpen: false, + }); + }, + + handleChange: function (event) { + const target = event.target; + let value; + if (this.props.multiple) { + value = []; + for (let i = 0; i < target.length; i++) { + const option = target.options[i]; + if (option.selected) { + value.push(option.value); + } + } + } else { + value = target.value; + } + this.changeValue(value); + }, + + changeValue: function (value) { + this.props.onChange(this.props.name, value); + }, + + render: function () { + if (this.props.layout === 'elementOnly') { + return this.renderElement(); + } + + return ( + + {this.renderElement()} + + + ); + }, + + renderElement: function () { + const renderOption = (item, key) => { + //eslint-disable-next-line no-unused-vars + const { group, label, ...rest } = item; + return this.props.native + ? + + : + {label}; + }; + + const renderGroup = (label, key, nodes) => { + return this.props.native + ? + + {nodes} + + : + {label}} key={key}> + {nodes} + ; + }; + + const { options, classes } = this.props; + + let groups = options.filter(function (item) { + return item.group; + }).map(function (item) { + return item.group; + }); + // Get the unique items in group. + groups = [...new Set(groups)]; + + let optionNodes = []; + + if (groups.length === 0) { + optionNodes = options.map(function (item, index) { + return renderOption(item, index); + }); + } else { + // For items without groups. + const itemsWithoutGroup = options.filter(function (item) { + return !item.group; + }); + + itemsWithoutGroup.forEach(function (item, index) { + optionNodes.push(renderOption(item, 'no-group-' + index)); + }); + + groups.forEach(function (group, groupIndex) { + + const groupItems = options.filter(function (item) { + return item.group === group; + }); + + const groupOptionNodes = groupItems.map(function (item, index) { + return renderOption(item, groupIndex + '-' + index); + }); + + optionNodes.push(renderGroup(group, groupIndex, groupOptionNodes)); + }); + } + + let value = this.props.value; + if (!this.props.multiple && _isArray(value)) { + value = value.length ? value[0] : ''; + } + + const startAdornment = hideStartAdornment(this.props) ? null : + ; + const endAdornment = + ; + + return ( + } + > + {optionNodes} + + ); + } +}); + + +export default withStyles(styles)(MuiSelect); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiSuggest.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiSuggest.jsx new file mode 100644 index 000000000..287491b60 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiSuggest.jsx @@ -0,0 +1,437 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createReactClass from 'create-react-class'; +import ComponentMixin from './mixins/component'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Input from '@material-ui/core/Input'; +import Autosuggest from 'react-autosuggest'; +import Paper from '@material-ui/core/Paper'; +import MenuItem from '@material-ui/core/MenuItem'; +import match from 'autosuggest-highlight/match'; +import parse from 'autosuggest-highlight/parse'; +import { registerComponent } from 'meteor/vulcan:core'; +import StartAdornment, { hideStartAdornment } from './StartAdornment'; +import EndAdornment from './EndAdornment'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; +import _isEqual from 'lodash/isEqual'; +import classNames from 'classnames'; +import IsolatedScroll from 'react-isolated-scroll'; + + +const maxSuggestions = 100; + + +/*{ + container: 'react-autosuggest__container', + containerOpen: 'react-autosuggest__container--open', + input: 'react-autosuggest__input', + inputOpen: 'react-autosuggest__input--open', + inputFocused: 'react-autosuggest__input--focused', + suggestionsContainer: 'react-autosuggest__suggestions-container', + suggestionsContainerOpen: 'react-autosuggest__suggestions-container--open', + suggestionsList: 'react-autosuggest__suggestions-list', + suggestion: 'react-autosuggest__suggestion', + suggestionFirst: 'react-autosuggest__suggestion--first', + suggestionHighlighted: 'react-autosuggest__suggestion--highlighted', + sectionContainer: 'react-autosuggest__section-container', + sectionContainerFirst: 'react-autosuggest__section-container--first', + sectionTitle: 'react-autosuggest__section-title' +}*/ +const styles = theme => ({ + container: { + flexGrow: 1, + position: 'relative', + }, + textField: { + width: '100%', + 'label + div > &': { + marginTop: theme.spacing.unit * 2, + }, + }, + input: { + outline: 0, + font: 'inherit', + color: 'currentColor', + width: '100%', + border: '0', + margin: '0', + padding: '7px 0', + display: 'block', + boxSizing: 'content-box', + background: 'none', + verticalAlign: 'middle', + '&::-webkit-search-decoration, &::-webkit-search-cancel-button, &::after, &:after': + { display: 'none' }, + '&::-webkit-search-results, &::-webkit-search-results-decoration': + { display: 'none' }, + }, + readOnly: { + cursor: 'pointer', + }, + suggestionsContainer: { + display: 'none', + position: 'absolute', + left: 0, + right: 0, + zIndex: theme.zIndex.modal, + marginBottom: theme.spacing.unit * 3, + maxHeight: 48 * 8, + }, + suggestionsContainerOpen: { + display: 'flex', + }, + scroller: { + flexGrow: 1, + overflowY: 'auto', + }, + suggestion: { + display: 'block', + }, + suggestionIcon: { + marginRight: theme.spacing.unit * 2, + }, + selected: { + backgroundColor: theme.palette.secondary.light, + }, + suggestionsList: { + margin: 0, + padding: 0, + listStyleType: 'none', + }, + inputRoot: { + '& .clear-enabled': { opacity: 0 }, + '&:hover .clear-enabled': { opacity: 0.54 }, + }, + inputFocused: { + '& .clear-enabled': { opacity: 0.54 } + }, +}); + + +const MuiSuggest = createReactClass({ + + inputElement: null, + + mixins: [ComponentMixin], + + propTypes: { + options: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + formatted: PropTypes.node, + iconComponent: PropTypes.node, + onClick: PropTypes.func, + })), + classes: PropTypes.object.isRequired, + limitToList: PropTypes.bool, + disableText: PropTypes.bool, + showAllOptions: PropTypes.bool, + className: PropTypes.string, + autoComplete: PropTypes.string, + autoFocus: PropTypes.bool, + }, + + getDefaultProps: function () { + return { + autoComplete: 'off', + autoFocus: false, + }; + }, + + getOptionFormatted: function (option) { + return option.formatted || option.label || option.value || ''; + }, + + getOptionLabel: function (option) { + return option.label || option.value || ''; + }, + + getInitialState: function () { + if (this.props.refFunction) { + this.props.refFunction(this); + } + + const selectedOption = this.getSelectedOption(); + return { + inputValue: this.getOptionLabel(selectedOption), + selectedOption: selectedOption, + suggestions: [], + }; + }, + + componentWillReceiveProps: function (nextProps) { + if (nextProps.value !== this.state.value || + nextProps.options !== this.props.options) { + const selectedOption = this.getSelectedOption(nextProps); + this.setState({ + inputValue: this.getOptionLabel(selectedOption), + selectedOption: selectedOption, + }); + } + }, + + shouldComponentUpdate: function (nextProps, nextState) { + return !_isEqual(nextState, this.state) || + nextProps.help !== this.props.help || + nextProps.charsCount !== this.props.charsCount || + !_isEqual(nextProps.errors, this.props.errors) || + nextProps.options !== this.props.options; + }, + + getSelectedOption: function (props) { + props = props || this.props; + const selectedOption = props.options.find((opt) => opt.value === props.value); + return selectedOption || { label: '', value: null }; + }, + + handleFocus: function (event) { + if (!this.inputElement) return; + + this.inputElement.select(); + }, + + handleBlur: function (event, { highlightedSuggestion: suggestion }) { + if (suggestion) { + this.changeValue(suggestion); + } else if (this.props.limitToList) { + const selectedOption = this.getSelectedOption(); + this.setState({ + inputValue: this.getOptionLabel(selectedOption), + }); + } + }, + + suggestionSelected: function (event, { suggestion }) { + this.changeValue(suggestion); + }, + + changeValue: function (suggestion) { + if (!suggestion) { + suggestion = { label: '', value: null }; + } + if (suggestion.onClick) { + return; + } + this.setState({ + selectedOption: suggestion, + inputValue: this.getOptionLabel(suggestion), + }); + this.props.onChange(this.props.name, suggestion.value, this.getOptionLabel(suggestion)); + }, + + handleInputChange: function (event) { + const value = event.target.value; + this.setState({ + inputValue: value, + }); + }, + + handleSuggestionsFetchRequested: function ({ value, reason }) { + this.setState({ + suggestions: this.getSuggestions(value), + }); + }, + + handleSuggestionsClearRequested: function () { + this.setState({ + suggestions: [], + }); + }, + + shouldRenderSuggestions: function (value) { + return true; + }, + + render: function () { + const value = this.props.value; + + const startAdornment = hideStartAdornment(this.props) ? null : + ; + const endAdornment = + ; + + const element = this.renderElement(startAdornment, endAdornment); + + if (this.props.layout === 'elementOnly') { + return element; + } + + return ( + + {element} + + + ); + }, + + renderElement: function (startAdornment, endAdornment) { + const { classes, autoFocus, disableText, showAllOptions } = this.props; + + return ( + + ); + }, + + renderInputComponent: function (inputProps) { + const { classes, autoFocus, autoComplete, value, ref, startAdornment, endAdornment, disabled, ...rest } = inputProps; + + return ( + { ref(c); this.inputElement = c; }} + type="text" + startAdornment={startAdornment} + endAdornment={endAdornment} + disabled={disabled} + inputProps={{ + ...rest, + }} + /> + ); + }, + + renderSuggestion: function (suggestion, { query, isHighlighted }) { + const label = this.getOptionFormatted(suggestion); + const matches = match(label, query); + const parts = parse(label, matches); + const isSelected = suggestion.value === this.props.value; + const className = isSelected ? this.props.classes.selected : null; + + return ( + + { + suggestion.iconComponent && +
+ {suggestion.iconComponent} +
+ } +
+ {parts.map((part, index) => { + return part.highlight ? ( + + {part.text} + + ) : ( + + {part.text} + + ); + })} +
+
+ ); + }, + + renderSuggestionsContainer: function ({ containerProps, children }) { + const { classes } = this.props; + + return ( + + + {children} + + + ); + }, + + getSuggestionValue: function (suggestion) { + return suggestion.value; + }, + + getSuggestions: function (value) { + const inputValue = value.trim().toLowerCase(); + const inputLength = inputValue.length; + let count = 0; + const inputMatchesSelection = value === this.getOptionLabel(this.state.selectedOption); + + return (this.props.disableText || this.props.showAllOptions) && inputMatchesSelection ? + + this.props.options.filter(suggestion => { + return true; + }) + + : + + inputLength === 0 + + ? + + this.props.options.filter(suggestion => { + count += 1; + return count <= maxSuggestions; + }) + + : + + this.props.options.filter(suggestion => { + const label = this.getOptionLabel(suggestion); + const keep = + count < maxSuggestions && label.toLowerCase().slice(0, inputLength) === + inputValue; + + if (keep) { + count += 1; + } + + return keep; + }); + }, + +}); + + +export default withStyles(styles)(MuiSuggest); +registerComponent('MuiSuggest', MuiSuggest, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiSwitch.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiSwitch.jsx new file mode 100644 index 000000000..52181f865 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiSwitch.jsx @@ -0,0 +1,71 @@ +import React from 'react'; +import createReactClass from 'create-react-class'; +import ComponentMixin from './mixins/component'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import Switch from '@material-ui/core/Switch'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; + + +const MuiSwitch = createReactClass({ + + mixins: [ComponentMixin], + + getDefaultProps: function () { + return { + label: '', + rowLabel: '', + value: false + }; + }, + + changeValue: function (event) { + const target = event.target; + const value = target.checked; + + //this.setValue(value); + this.props.onChange(this.props.name, value); + + setTimeout(() => {document.activeElement.blur();}); + }, + + render: function () { + + const element = this.renderElement(); + + if (this.props.layout === 'elementOnly') { + return element; + } + + return ( + + {element} + + + ); + }, + + renderElement: function () { + return ( + this.element = c} + {...this.cleanSwitchProps(this.cleanProps(this.props))} + id={this.getId()} + checked={this.props.value === true} + onChange={this.changeValue} + disabled={this.props.disabled} + /> + } + label={this.props.label} + /> + ); + }, + +}); + + +export default MuiSwitch; diff --git a/packages/vulcan-ui-material/components/forms/base-controls/StartAdornment.jsx b/packages/vulcan-ui-material/components/forms/base-controls/StartAdornment.jsx new file mode 100644 index 000000000..58d6a7434 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/StartAdornment.jsx @@ -0,0 +1,54 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { instantiateComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import IconButton from '@material-ui/core/IconButton'; +import OpenInNewIcon from 'mdi-material-ui/OpenInNew'; +import { styles } from './EndAdornment'; + + +export const hideStartAdornment = (props) => { + return !props.addonBefore && !props.isUrl; +}; + + +export const fixUrl = (url) => { + return url.indexOf('http://') === -1 && url.indexOf('https://') ? 'http://' + url : url; +}; + + +const StartAdornment = (props) => { + const { classes, value, type, addonBefore } = props; + + if (hideStartAdornment(props)) return null; + + const urlButton = type === 'url' && + + + ; + + + return ( + + {instantiateComponent(addonBefore)} + {urlButton} + + ); +}; + + +StartAdornment.propTypes = { + classes: PropTypes.object.isRequired, + value: PropTypes.any, + type: PropTypes.string, + addonBefore: PropTypes.oneOfType([PropTypes.string, PropTypes.node, PropTypes.func]), +}; + + +export default withStyles(styles)(StartAdornment); diff --git a/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx b/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx new file mode 100644 index 000000000..7913cbfc5 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx @@ -0,0 +1,139 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import _omit from 'lodash/omit'; + + +export default { + + propTypes: { + label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + hideLabel: PropTypes.bool, + layout: PropTypes.string, + required: PropTypes.bool, + errors: PropTypes.arrayOf(PropTypes.object), + }, + + getFormControlProperties: function () { + return { + label: this.props.label, + hideLabel: this.props.hideLabel, + layout: this.props.layout, + required: this.props.required, + hasErrors: this.hasErrors(), + className: this.props.className, + inputType: this.props.inputType, + }; + }, + + getFormHelperProperties: function () { + return { + help: this.props.help, + errors: this.props.errors, + hasErrors: this.hasErrors(), + showCharsRemaining: this.props.showCharsRemaining, + charsRemaining: this.props.charsRemaining, + charsCount: this.props.charsCount, + max: this.props.max, + }; + }, + + hashString: function (string) { + let hash = 0; + for (let i = 0; i < string.length; i++) { + hash = (((hash << 5) - hash) + string.charCodeAt(i)) & 0xFFFFFFFF; + } + return hash; + }, + + /** + * The ID is used as an attribute on the form control, and is used to allow + * associating the label element with the form control. + * + * If we don't explicitly pass an `id` prop, we generate one based on the + * `name`, `label` and `itemIndex` (for nested forms) properties. + */ + getId: function () { + if (this.props.id) { + return this.props.id; + } + const label = (typeof this.props.label === 'undefined' ? '' : this.props.label); + const itemIndex = (typeof this.props.itemIndex=== 'undefined' ? '' : this.props.itemIndex); + return [ + 'frc', + this.props.name.split('[').join('_').replace(']', ''), + itemIndex, + this.hashString(JSON.stringify(label)) + ].join('-'); + }, + + hasErrors: function () { + return !!(this.props.errors && this.props.errors.length); + }, + + cleanProps: function (props) { + const removedFields = [ + 'beforeComponent', + 'afterComponent', + 'addonAfter', + 'addonBefore', + 'help', + 'label', + 'hideLabel', + 'options', + 'layout', + 'rowLabel', + 'validatePristine', + 'validateOnSubmit', + 'inputClassName', + 'optional', + 'throwError', + 'currentValues', + 'addToDeletedValues', + 'deletedValues', + 'clearFieldErrors', + 'formType', + 'inputType', + 'showCharsRemaining', + 'charsCount', + 'charsRemaining', + 'handleChange', + 'document', + 'updateCurrentValues', + 'classes', + 'errors', + 'description', + 'clearField', + 'regEx', + 'mustComplete', + 'renderComponent', + 'formInput', + 'className', + 'formatValue', + 'scrubValue', + 'custom', + 'hideClear', + 'inputProperties', + 'currentUser', + 'nestedSchema', + 'parentFieldName', + 'itemIndex', + 'formComponents', + 'autoValue', + 'minCount', + 'maxCount' + ]; + + return _omit(props, removedFields); + }, + + cleanSwitchProps: function (props) { + const removedFields = [ + 'value', + 'error', + ]; + + return _omit(props, removedFields); + }, + +}; diff --git a/packages/vulcan-ui-material/components/forms/controls/Checkbox.jsx b/packages/vulcan-ui-material/components/forms/controls/Checkbox.jsx new file mode 100644 index 000000000..4f016d1ad --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Checkbox.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiSwitch from '../base-controls/MuiSwitch'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const CheckboxComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentCheckbox', CheckboxComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/CheckboxGroup.jsx b/packages/vulcan-ui-material/components/forms/controls/CheckboxGroup.jsx new file mode 100644 index 000000000..226693354 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/CheckboxGroup.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiCheckboxGroup from '../base-controls/MuiCheckboxGroup'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const CheckboxGroupComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentCheckboxGroup', CheckboxGroupComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/CountrySelect.jsx b/packages/vulcan-ui-material/components/forms/controls/CountrySelect.jsx new file mode 100644 index 000000000..1c4cf14be --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/CountrySelect.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import MuiSuggest from '../base-controls/MuiSuggest'; +import { registerComponent } from 'meteor/vulcan:core'; +import { countries } from './countries'; + + +const CountrySelect = ({ refFunction, ...properties }) => + ; + + +registerComponent('CountrySelect', CountrySelect); diff --git a/packages/vulcan-ui-material/components/forms/controls/Date.jsx b/packages/vulcan-ui-material/components/forms/controls/Date.jsx new file mode 100644 index 000000000..190e08ced --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Date.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; + + +export const styles = theme => ({ + + '@global': { + 'input[type=date]::-ms-clear, input[type=date]::-ms-reveal': { + display: 'none', + width: 0, + height: 0, + }, + 'input[type=date]::-webkit-search-cancel-button': { + display: 'none', + '-webkit-appearance': 'none', + }, + 'input[type="date"]::-webkit-clear-button': { + display: 'none', + '-webkit-appearance': 'none', + }, + + 'input[type="date"]::-webkit-inner-spin-button,input[type="date"]::-webkit-outer-spin-button': { + '-webkit-appearance': 'none', + margin: 0, + }, + }, + +}); + + +const DateComponent = ({ refFunction, classes, ...properties }) => + ; + + +registerComponent('FormComponentDate', DateComponent, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/controls/DateRdt.jsx b/packages/vulcan-ui-material/components/forms/controls/DateRdt.jsx new file mode 100644 index 000000000..f012d6717 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/DateRdt.jsx @@ -0,0 +1,60 @@ +// Deprecated react-datetime version + +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import DateTimePicker from 'react-datetime'; +import { registerComponent } from 'meteor/vulcan:core'; + +class DateComponent extends PureComponent { + + constructor(props) { + super(props); + this.updateDate = this.updateDate.bind(this); + } + + // when the datetime picker has mounted, SmartForm will catch the date value (no formsy mixin in this component) + // componentDidMount() { + // if (this.props.value) { + // this.updateDate(this.props.value); + // } + // } + + updateDate(date) { + this.context.updateCurrentValues({[this.props.path]: date}); + } + + render() { + + const date = this.props.value ? (typeof this.props.value === 'string' ? new Date(this.props.value) : this.props.value) : null; + + return ( +
+ +
+ this.updateDate(newDate)} + inputProps={{name: this.props.name}} + /> +
+
+ ); + } +} + +DateComponent.propTypes = { + control: PropTypes.any, + datatype: PropTypes.any, + group: PropTypes.any, + label: PropTypes.string, + name: PropTypes.string, + value: PropTypes.any, +}; + +DateComponent.contextTypes = { + updateCurrentValues: PropTypes.func, +}; + +export default DateComponent; diff --git a/packages/vulcan-ui-material/components/forms/controls/DateTime.jsx b/packages/vulcan-ui-material/components/forms/controls/DateTime.jsx new file mode 100644 index 000000000..5fc870a0a --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/DateTime.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; + + +export const styles = theme => ({ + + '@global': { + 'input[type=datetime]::-ms-clear, input[type=datetime]::-ms-reveal': { + display: 'none', + width: 0, + height: 0, + }, + 'input[type=datetime]::-webkit-search-cancel-button': { + display: 'none', + '-webkit-appearance': 'none', + }, + 'input[type="datetime"]::-webkit-clear-button': { + display: 'none', + '-webkit-appearance': 'none', + }, + + 'input[type="datetime"]::-webkit-inner-spin-button,input[type="datetime"]::-webkit-outer-spin-button': { + '-webkit-appearance': 'none', + margin: 0, + }, + }, + +}); + + +const DateTimeComponent = ({ refFunction, classes, ...properties }) => + ; + + +registerComponent('FormComponentDateTime', DateTimeComponent, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx b/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx new file mode 100644 index 000000000..21c308d9e --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx @@ -0,0 +1,61 @@ +// Deprecated react-datetime version + +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import DateTimePicker from 'react-datetime'; +import { registerComponent } from 'meteor/vulcan:core'; + + +class DateTimeRdt extends PureComponent { + + constructor(props) { + super(props); + this.updateDate = this.updateDate.bind(this); + } + + // when the datetime picker has mounted, SmartForm will catch the date value (no formsy mixin in this component) + componentDidMount() { + if (this.props.value) { + this.updateDate(this.props.value); + } + } + + updateDate(date) { + this.context.updateCurrentValues({[this.props.name]: date}); + } + + render() { + + const date = this.props.value ? (typeof this.props.value === 'string' ? new Date(this.props.value) : this.props.value) : null; + + return ( +
+ +
+ this.updateDate(newDate._d)} + format={"x"} + inputProps={{name: this.props.name}} + /> +
+
+ ); + } +} + +DateTimeRdt.propTypes = { + control: PropTypes.any, + datatype: PropTypes.any, + group: PropTypes.any, + label: PropTypes.string, + name: PropTypes.string, + value: PropTypes.any, +}; + +DateTimeRdt.contextTypes = { + updateCurrentValues: PropTypes.func, +}; + +export default DateTimeRdt; diff --git a/packages/vulcan-ui-material/components/forms/controls/Default.jsx b/packages/vulcan-ui-material/components/forms/controls/Default.jsx new file mode 100644 index 000000000..e35f64abd --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Default.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const Default = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentDefault', Default); diff --git a/packages/vulcan-ui-material/components/forms/controls/Email.jsx b/packages/vulcan-ui-material/components/forms/controls/Email.jsx new file mode 100644 index 000000000..f4688361f --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Email.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const EmailComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentEmail', EmailComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/Number.jsx b/packages/vulcan-ui-material/components/forms/controls/Number.jsx new file mode 100644 index 000000000..03136d0c3 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Number.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const NumberComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentNumber', NumberComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/PostalCode.jsx b/packages/vulcan-ui-material/components/forms/controls/PostalCode.jsx new file mode 100644 index 000000000..810433e6c --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/PostalCode.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; +import { getCountryInfo } from './RegionSelect'; + + +const PostalCode = ({ classes, refFunction, ...properties }) => { + const currentCountryInfo = getCountryInfo(properties); + const postalLabel = currentCountryInfo ? currentCountryInfo.postalLabel : 'Postal code'; + + return ; +}; + + +registerComponent('PostalCode', PostalCode); diff --git a/packages/vulcan-ui-material/components/forms/controls/RadioGroup.jsx b/packages/vulcan-ui-material/components/forms/controls/RadioGroup.jsx new file mode 100644 index 000000000..63d0052e9 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/RadioGroup.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiRadioGroup from '../base-controls/MuiRadioGroup'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const RadioGroupComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentRadioGroup', RadioGroupComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/RegionSelect.jsx b/packages/vulcan-ui-material/components/forms/controls/RegionSelect.jsx new file mode 100644 index 000000000..a790bb351 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/RegionSelect.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import MuiSuggest from '../base-controls/MuiSuggest'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; +import { countryInfo } from './countries'; +import _get from 'lodash/get'; + + +export const getCountryInfo = function (formComponentProps) { + const addressPath = formComponentProps.path; + const countryParts = addressPath.split('.'); + countryParts[countryParts.length-1] = 'country'; + const country = _get(formComponentProps.document, countryParts); + return country && countryInfo[country]; +}; + + +const RegionSelect = ({ classes, refFunction, ...properties }) => { + const currentCountryInfo = getCountryInfo(properties); + const options = currentCountryInfo ? currentCountryInfo.regions : null; + const regionLabel = currentCountryInfo ? currentCountryInfo.regionLabel : 'Region'; + + if (options) { + return ; + } else { + return ; + } +}; + + +registerComponent('RegionSelect', RegionSelect); diff --git a/packages/vulcan-ui-material/components/forms/controls/Select.jsx b/packages/vulcan-ui-material/components/forms/controls/Select.jsx new file mode 100644 index 000000000..7ff92e343 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Select.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import MuiSelect from '../base-controls/MuiSelect'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const SelectComponent = ({ refFunction, ...properties }) => { + const noneOption = { label: '', value: '' }; + properties.options = [noneOption, ...properties.options]; + + return ; +}; + + +registerComponent('FormComponentSelect', SelectComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/SelectMultiple.jsx b/packages/vulcan-ui-material/components/forms/controls/SelectMultiple.jsx new file mode 100644 index 000000000..5b3f552ce --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/SelectMultiple.jsx @@ -0,0 +1,13 @@ +import React from 'react'; +import MuiSelect from '../base-controls/MuiSelect'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const SelectMultiple = ({ refFunction, ...properties }) => { + properties.multiple = true; + + return ; +}; + + +registerComponent('FormComponentSelectMultiple', SelectMultiple); diff --git a/packages/vulcan-ui-material/components/forms/controls/Textarea.jsx b/packages/vulcan-ui-material/components/forms/controls/Textarea.jsx new file mode 100644 index 000000000..988960741 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Textarea.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const TextareaComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentTextarea', TextareaComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/Time.jsx b/packages/vulcan-ui-material/components/forms/controls/Time.jsx new file mode 100644 index 000000000..e52bb3cbb --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Time.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; + + +export const styles = theme => ({ + + '@global': { + 'input[type=time]::-ms-clear, input[type=time]::-ms-reveal': { + display: 'none', + width: 0, + height: 0, + }, + 'input[type=time]::-webkit-search-cancel-button': { + display: 'none', + '-webkit-appearance': 'none', + }, + 'input[type="time"]::-webkit-clear-button': { + display: 'none', + '-webkit-appearance': 'none', + }, + + 'input[type="time"]::-webkit-inner-spin-button,input[type="time"]::-webkit-outer-spin-button': { + '-webkit-appearance': 'none', + margin: 0, + }, + }, + +}); + + +const TimeComponent = ({ refFunction, classes, ...properties }) => + ; + + +registerComponent('FormComponentTime', TimeComponent, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/controls/TimeRdt.jsx b/packages/vulcan-ui-material/components/forms/controls/TimeRdt.jsx new file mode 100644 index 000000000..cf8334241 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/TimeRdt.jsx @@ -0,0 +1,73 @@ +// Deprecated react-datetime version + +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import DateTimePicker from 'react-datetime'; +import { registerComponent } from 'meteor/vulcan:core'; + +class TimeRdt extends PureComponent { + + constructor(props) { + super(props); + this.updateDate = this.updateDate.bind(this); + } + + // when the datetime picker has mounted, SmartForm will catch the date value (no formsy mixin in this component) + // componentDidMount() { + // if (this.props.value) { + // this.context.updateCurrentValues({[this.props.path]: this.props.value}); + // } + // } + + updateDate(mDate) { + // if this is a properly formatted moment date, update time + if (typeof mDate === 'object') { + this.context.updateCurrentValues({[this.props.path]: mDate.format('HH:mm')}); + } + } + + render() { + + const date = new Date(); + + // transform time string into date object to work inside datetimepicker + const time = this.props.value; + if (time) { + date.setHours(parseInt(time.substr(0,2)), parseInt(time.substr(3,5))); + } else { + date.setHours(0,0); + } + + return ( +
+ +
+ this.updateDate(newDate)} + inputProps={{name: this.props.name}} + /> +
+
+ ); + } +} + +TimeRdt.propTypes = { + control: PropTypes.any, + datatype: PropTypes.any, + group: PropTypes.any, + label: PropTypes.string, + name: PropTypes.string, + value: PropTypes.any, +}; + +TimeRdt.contextTypes = { + updateCurrentValues: PropTypes.func, +}; + +export default TimeRdt; diff --git a/packages/vulcan-ui-material/components/forms/controls/Url.jsx b/packages/vulcan-ui-material/components/forms/controls/Url.jsx new file mode 100644 index 000000000..d5d9ebf13 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/Url.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import MuiInput from '../base-controls/MuiInput'; +import { registerComponent } from 'meteor/vulcan:core'; + + +const UrlComponent = ({ refFunction, ...properties }) => + ; + + +registerComponent('FormComponentUrl', UrlComponent); diff --git a/packages/vulcan-ui-material/components/forms/controls/countries.js b/packages/vulcan-ui-material/components/forms/controls/countries.js new file mode 100644 index 000000000..a2d84b1e7 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/controls/countries.js @@ -0,0 +1,402 @@ +export const countries = [ + { value: 'AF', label: 'Afghanistan' }, + { value: 'AX', label: 'Åland Islands' }, + { value: 'AL', label: 'Albania' }, + { value: 'DZ', label: 'Algeria' }, + { value: 'AS', label: 'American Samoa' }, + { value: 'AD', label: 'Andorra' }, + { value: 'AO', label: 'Angola' }, + { value: 'AI', label: 'Anguilla' }, + { value: 'AQ', label: 'Antarctica' }, + { value: 'AG', label: 'Antigua and Barbuda' }, + { value: 'AR', label: 'Argentina' }, + { value: 'AM', label: 'Armenia' }, + { value: 'AW', label: 'Aruba' }, + { value: 'AU', label: 'Australia' }, + { value: 'AT', label: 'Austria' }, + { value: 'AZ', label: 'Azerbaijan' }, + { value: 'BS', label: 'Bahamas' }, + { value: 'BH', label: 'Bahrain' }, + { value: 'BD', label: 'Bangladesh' }, + { value: 'BB', label: 'Barbados' }, + { value: 'BY', label: 'Belarus' }, + { value: 'BE', label: 'Belgium' }, + { value: 'BZ', label: 'Belize' }, + { value: 'BJ', label: 'Benin' }, + { value: 'BM', label: 'Bermuda' }, + { value: 'BT', label: 'Bhutan' }, + { value: 'BO', label: 'Bolivia' }, + { value: 'BQ', label: 'Bonaire, Sint Eustatius, Saba' }, + { value: 'BA', label: 'Bosnia and Herzegovina' }, + { value: 'BW', label: 'Botswana' }, + { value: 'BV', label: 'Bouvet Island' }, + { value: 'BR', label: 'Brazil' }, + { value: 'IO', label: 'British Indian Ocean Territory' }, + { value: 'BN', label: 'Brunei Darussalam' }, + { value: 'BG', label: 'Bulgaria' }, + { value: 'BF', label: 'Burkina Faso' }, + { value: 'BI', label: 'Burundi' }, + { value: 'CV', label: 'Cabo Verde' }, + { value: 'KH', label: 'Cambodia' }, + { value: 'CM', label: 'Cameroon' }, + { value: 'CA', label: 'Canada' }, + { value: 'KY', label: 'Cayman Islands' }, + { value: 'CF', label: 'Central African Republic' }, + { value: 'TD', label: 'Chad' }, + { value: 'CL', label: 'Chile' }, + { value: 'CN', label: 'China' }, + { value: 'CX', label: 'Christmas Island' }, + { value: 'CC', label: 'Cocos (Keeling) Islands' }, + { value: 'CO', label: 'Colombia' }, + { value: 'KM', label: 'Comoros' }, + { value: 'CG', label: 'Congo' }, + { value: 'CD', label: 'Congo (Democratic Republic of the)' }, + { value: 'CK', label: 'Cook Islands' }, + { value: 'CR', label: 'Costa Rica' }, + { value: 'CI', label: 'Côte d’Ivoire' }, + { value: 'HR', label: 'Croatia' }, + { value: 'CU', label: 'Cuba' }, + { value: 'CW', label: 'Curaçao' }, + { value: 'CY', label: 'Cyprus' }, + { value: 'CZ', label: 'Czechia' }, + { value: 'DK', label: 'Denmark' }, + { value: 'DJ', label: 'Djibouti' }, + { value: 'DM', label: 'Dominica' }, + { value: 'DO', label: 'Dominican Republic' }, + { value: 'EC', label: 'Ecuador' }, + { value: 'EG', label: 'Egypt' }, + { value: 'SV', label: 'El Salvador' }, + { value: 'GQ', label: 'Equatorial Guinea' }, + { value: 'ER', label: 'Eritrea' }, + { value: 'EE', label: 'Estonia' }, + { value: 'ET', label: 'Ethiopia' }, + { value: 'FK', label: 'Falkland Islands' }, + { value: 'FO', label: 'Faroe Islands' }, + { value: 'FJ', label: 'Fiji' }, + { value: 'FI', label: 'Finland' }, + { value: 'FR', label: 'France' }, + { value: 'GF', label: 'French Guiana' }, + { value: 'PF', label: 'French Polynesia' }, + { value: 'TF', label: 'French Southern Territories' }, + { value: 'GA', label: 'Gabon' }, + { value: 'GM', label: 'Gambia' }, + { value: 'GE', label: 'Georgia' }, + { value: 'DE', label: 'Germany' }, + { value: 'GH', label: 'Ghana' }, + { value: 'GI', label: 'Gibraltar' }, + { value: 'GR', label: 'Greece' }, + { value: 'GL', label: 'Greenland' }, + { value: 'GD', label: 'Grenada' }, + { value: 'GP', label: 'Guadeloupe' }, + { value: 'GU', label: 'Guam' }, + { value: 'GT', label: 'Guatemala' }, + { value: 'GG', label: 'Guernsey' }, + { value: 'GN', label: 'Guinea' }, + { value: 'GW', label: 'Guinea-Bissau' }, + { value: 'GY', label: 'Guyana' }, + { value: 'HT', label: 'Haiti' }, + { value: 'HM', label: 'Heard Island, Mcdonald Islands' }, + { value: 'VA', label: 'Vatican City State' }, + { value: 'HN', label: 'Honduras' }, + { value: 'HK', label: 'Hong Kong' }, + { value: 'HU', label: 'Hungary' }, + { value: 'IS', label: 'Iceland' }, + { value: 'IN', label: 'India' }, + { value: 'ID', label: 'Indonesia' }, + { value: 'IR', label: 'Iran' }, + { value: 'IQ', label: 'Iraq' }, + { value: 'IE', label: 'Ireland' }, + { value: 'IM', label: 'Isle of Man' }, + { value: 'IL', label: 'Israel' }, + { value: 'IT', label: 'Italy' }, + { value: 'JM', label: 'Jamaica' }, + { value: 'JP', label: 'Japan' }, + { value: 'JE', label: 'Jersey' }, + { value: 'JO', label: 'Jordan' }, + { value: 'KZ', label: 'Kazakhstan' }, + { value: 'KE', label: 'Kenya' }, + { value: 'KI', label: 'Kiribati' }, + { value: 'KW', label: 'Kuwait' }, + { value: 'KG', label: 'Kyrgyzstan' }, + { value: 'LA', label: 'Lao' }, + { value: 'LV', label: 'Latvia' }, + { value: 'LB', label: 'Lebanon' }, + { value: 'LS', label: 'Lesotho' }, + { value: 'LR', label: 'Liberia' }, + { value: 'LY', label: 'Libya' }, + { value: 'LI', label: 'Liechtenstein' }, + { value: 'LT', label: 'Lithuania' }, + { value: 'LU', label: 'Luxembourg' }, + { value: 'MO', label: 'Macao' }, + { value: 'MK', label: 'Macedonia' }, + { value: 'MG', label: 'Madagascar' }, + { value: 'MW', label: 'Malawi' }, + { value: 'MY', label: 'Malaysia' }, + { value: 'MV', label: 'Maldives' }, + { value: 'ML', label: 'Mali' }, + { value: 'MT', label: 'Malta' }, + { value: 'MH', label: 'Marshall Islands' }, + { value: 'MQ', label: 'Martinique' }, + { value: 'MR', label: 'Mauritania' }, + { value: 'MU', label: 'Mauritius' }, + { value: 'YT', label: 'Mayotte' }, + { value: 'MX', label: 'Mexico' }, + { value: 'FM', label: 'Micronesia' }, + { value: 'MD', label: 'Moldova' }, + { value: 'MC', label: 'Monaco' }, + { value: 'MN', label: 'Mongolia' }, + { value: 'ME', label: 'Montenegro' }, + { value: 'MS', label: 'Montserrat' }, + { value: 'MA', label: 'Morocco' }, + { value: 'MZ', label: 'Mozambique' }, + { value: 'MM', label: 'Myanmar' }, + { value: 'NA', label: 'Namibia' }, + { value: 'NR', label: 'Nauru' }, + { value: 'NP', label: 'Nepal' }, + { value: 'NL', label: 'Netherlands' }, + { value: 'NC', label: 'New Caledonia' }, + { value: 'NZ', label: 'New Zealand' }, + { value: 'NI', label: 'Nicaragua' }, + { value: 'NE', label: 'Niger' }, + { value: 'NG', label: 'Nigeria' }, + { value: 'NU', label: 'Niue' }, + { value: 'NF', label: 'Norfolk Island' }, + { value: 'KP', label: 'North Korea' }, + { value: 'MP', label: 'Northern Mariana Islands' }, + { value: 'NO', label: 'Norway' }, + { value: 'OM', label: 'Oman' }, + { value: 'PK', label: 'Pakistan' }, + { value: 'PW', label: 'Palau' }, + { value: 'PS', label: 'Palestine' }, + { value: 'PA', label: 'Panama' }, + { value: 'PG', label: 'Papua New Guinea' }, + { value: 'PY', label: 'Paraguay' }, + { value: 'PE', label: 'Peru' }, + { value: 'PH', label: 'Philippines' }, + { value: 'PN', label: 'Pitcairn' }, + { value: 'PL', label: 'Poland' }, + { value: 'PT', label: 'Portugal' }, + { value: 'PR', label: 'Puerto Rico' }, + { value: 'QA', label: 'Qatar' }, + { value: 'RE', label: 'Réunion' }, + { value: 'RO', label: 'Romania' }, + { value: 'RU', label: 'Russian Federation' }, + { value: 'RW', label: 'Rwanda' }, + { value: 'BL', label: 'Saint Barthélemy' }, + { value: 'SH', label: 'Saint Helena, Ascension, Tristan Da Cunha' }, + { value: 'KN', label: 'Saint Kitts and Nevis' }, + { value: 'LC', label: 'Saint Lucia' }, + { value: 'MF', label: 'Saint Martin (French Portion)' }, + { value: 'PM', label: 'Saint Pierre and Miquelon' }, + { value: 'VC', label: 'Saint Vincent and the Grenadines' }, + { value: 'WS', label: 'Samoa' }, + { value: 'SM', label: 'San Marino' }, + { value: 'ST', label: 'Sao Tome and Principe' }, + { value: 'SA', label: 'Saudi Arabia' }, + { value: 'SN', label: 'Senegal' }, + { value: 'RS', label: 'Serbia' }, + { value: 'SC', label: 'Seychelles' }, + { value: 'SL', label: 'Sierra Leone' }, + { value: 'SG', label: 'Singapore' }, + { value: 'SX', label: 'Sint Maarten (Dutch part)' }, + { value: 'SK', label: 'Slovakia' }, + { value: 'SI', label: 'Slovenia' }, + { value: 'SB', label: 'Solomon Islands' }, + { value: 'SO', label: 'Somalia' }, + { value: 'ZA', label: 'South Africa' }, + { value: 'GS', label: 'South Georgia, South Sandwich Islands' }, + { value: 'KR', label: 'South Korea' }, + { value: 'SS', label: 'South Sudan' }, + { value: 'ES', label: 'Spain' }, + { value: 'LK', label: 'Sri Lanka' }, + { value: 'SD', label: 'Sudan' }, + { value: 'SR', label: 'Suriname' }, + { value: 'SJ', label: 'Svalbard and Jan Mayen' }, + { value: 'SZ', label: 'Swaziland' }, + { value: 'SE', label: 'Sweden' }, + { value: 'CH', label: 'Switzerland' }, + { value: 'SY', label: 'Syria' }, + { value: 'TW', label: 'Taiwan' }, + { value: 'TJ', label: 'Tajikistan' }, + { value: 'TZ', label: 'Tanzania' }, + { value: 'TH', label: 'Thailand' }, + { value: 'TL', label: 'Timor-Leste' }, + { value: 'TG', label: 'Togo' }, + { value: 'TK', label: 'Tokelau' }, + { value: 'TO', label: 'Tonga' }, + { value: 'TT', label: 'Trinidad and Tobago' }, + { value: 'TN', label: 'Tunisia' }, + { value: 'TR', label: 'Turkey' }, + { value: 'TM', label: 'Turkmenistan' }, + { value: 'TC', label: 'Turks and Caicos Islands' }, + { value: 'TV', label: 'Tuvalu' }, + { value: 'UG', label: 'Uganda' }, + { value: 'UA', label: 'Ukraine' }, + { value: 'AE', label: 'United Arab Emirates' }, + { value: 'GB', label: 'United Kingdom' }, + { value: 'US', label: 'United States' }, + { value: 'UM', label: 'United States Minor Outlying Islands' }, + { value: 'UY', label: 'Uruguay' }, + { value: 'UZ', label: 'Uzbekistan' }, + { value: 'VU', label: 'Vanuatu' }, + { value: 'VE', label: 'Venezuela' }, + { value: 'VN', label: 'Viet Nam' }, + { value: 'VG', label: 'Virgin Islands, British' }, + { value: 'VI', label: 'Virgin Islands, U.S.' }, + { value: 'WF', label: 'Wallis and Futuna' }, + { value: 'EH', label: 'Western Sahara' }, + { value: 'YE', label: 'Yemen' }, + { value: 'ZM', label: 'Zambia' }, + { value: 'ZW', label: 'Zimbabwe' }, +]; + + +export const countryInfo = { + US: { + regionLabel: 'State', + postalLabel: 'Zip code', + regions: [ + { value: 'AL', label: 'Alabama' }, + { value: 'AK', label: 'Alaska' }, + { value: 'AZ', label: 'Arizona' }, + { value: 'AR', label: 'Arkansas' }, + { value: 'CA', label: 'California' }, + { value: 'CO', label: 'Colorado' }, + { value: 'CT', label: 'Connecticut' }, + { value: 'DE', label: 'Delaware' }, + { value: 'FL', label: 'Florida' }, + { value: 'GA', label: 'Georgia' }, + { value: 'HI', label: 'Hawaii' }, + { value: 'ID', label: 'Idaho' }, + { value: 'IL', label: 'Illinois' }, + { value: 'IN', label: 'Indiana' }, + { value: 'IA', label: 'Iowa' }, + { value: 'KS', label: 'Kansas' }, + { value: 'KY', label: 'Kentucky' }, + { value: 'LA', label: 'Louisiana' }, + { value: 'ME', label: 'Maine' }, + { value: 'MD', label: 'Maryland' }, + { value: 'MA', label: 'Massachusetts' }, + { value: 'MI', label: 'Michigan' }, + { value: 'MN', label: 'Minnesota' }, + { value: 'MS', label: 'Mississippi' }, + { value: 'MO', label: 'Missouri' }, + { value: 'MT', label: 'Montana' }, + { value: 'NE', label: 'Nebraska' }, + { value: 'NV', label: 'Nevada' }, + { value: 'NH', label: 'New Hampshire' }, + { value: 'NJ', label: 'New Jersey' }, + { value: 'NM', label: 'New Mexico' }, + { value: 'NY', label: 'New York' }, + { value: 'NC', label: 'North Carolina' }, + { value: 'ND', label: 'North Dakota' }, + { value: 'OH', label: 'Ohio' }, + { value: 'OK', label: 'Oklahoma' }, + { value: 'OR', label: 'Oregon' }, + { value: 'PA', label: 'Pennsylvania' }, + { value: 'RI', label: 'Rhode Island' }, + { value: 'SC', label: 'South Carolina' }, + { value: 'SD', label: 'South Dakota' }, + { value: 'TN', label: 'Tennessee' }, + { value: 'TX', label: 'Texas' }, + { value: 'UT', label: 'Utah' }, + { value: 'VT', label: 'Vermont' }, + { value: 'VA', label: 'Virginia' }, + { value: 'WA', label: 'Washington' }, + { value: 'WV', label: 'West Virginia' }, + { value: 'WI', label: 'Wisconsin' }, + { value: 'WY', label: 'Wyoming' }, + ] + }, + CA: { + regionLabel: 'Province', + postalLabel: 'Postal code', + regions: [ + { value: 'AB', label: 'Alberta' }, + { value: 'BC', label: 'British Columbia' }, + { value: 'MB', label: 'Manitoba' }, + { value: 'NB', label: 'New Brunswick' }, + { value: 'NL', label: 'Newfoundland and Labrador' }, + { value: 'NS', label: 'Nova Scotia' }, + { value: 'NT', label: 'Northwest Territories' }, + { value: 'NU', label: 'Nunavut' }, + { value: 'ON', label: 'Ontario' }, + { value: 'PE', label: 'Prince Edward Island' }, + { value: 'QC', label: 'Quebec' }, + { value: 'SK', label: 'Saskatchewan' }, + { value: 'YT', label: 'Yukon' }, + ], + }, + AU: { + regionLabel: 'State', + postalLabel: 'Postcode', + regions: [ + { value: 'ACT', label: 'Australian Capital Territory' }, + { value: 'NSW', label: 'New South Wales' }, + { value: 'NT', label: 'Northern Territory' }, + { value: 'QLD', label: 'Queensland' }, + { value: 'SA', label: 'South Australia' }, + { value: 'TAS', label: 'Tasmania' }, + { value: 'VIC', label: 'Victoria' }, + { value: 'WA', label: 'Western Australia' }, + ], + }, + UK: { + regionLabel: 'County', + postalLabel: 'Postcode', + }, +}; + + +export const getCountryLabel = (countryValue) => { + const country = countries.find(country => country.value === countryValue); + return country ? country.label : ''; +}; + + +export const getCountryContinent = (countryValue) => { + const country = countries.find(country => country.value === countryValue); + return country ? country.continent : ''; +}; + + +export const getRegionLabel = (countryValue, regionValue) => { + if (!countryInfo[countryValue] || !countryInfo[countryValue].regions) { + return regionValue; + } + + const regions = countryInfo[countryValue].regions; + + let region = regions.find(nextRegion => nextRegion.value === regionValue); + + if (region) { + return region.label; + } else { + return regionValue; + } +}; + + +// Given a region value or label, returns the region value (QC or Quebec => QC) +// or false if the regionValue is invalid +export const validateRegion = (countryValue, regionValue) => { + if (!countryInfo[countryValue] || !countryInfo[countryValue].regions) { + return regionValue; + } + + const regions = countryInfo[countryValue].regions; + + let region = regions.find(nextRegion => nextRegion.value === regionValue); + + if (region) { + return regionValue; + } + + region = regions.find(nextRegion => nextRegion.label === regionValue); + + if (region) { + return region.value; + } else { + return false; + } +}; diff --git a/packages/vulcan-ui-material/components/index.js b/packages/vulcan-ui-material/components/index.js new file mode 100644 index 000000000..e619680ee --- /dev/null +++ b/packages/vulcan-ui-material/components/index.js @@ -0,0 +1,58 @@ +import './accounts/AccountsButton'; +import './accounts/AccountsButtons'; +import './accounts/AccountsField'; +import './accounts/AccountsFields'; +import './accounts/AccountsForm'; +import './accounts/AccountsPasswordOrService'; +import './accounts/AccountsSocialButtons'; + +import './bonus/LoadMore'; +import './bonus/SearchInput'; +import './bonus/TooltipIntl'; +import './bonus/TooltipIconButton'; + +import './core/Card'; +import './core/Datatable'; +import './core/EditButton'; +import './core/Loading'; +import './core/ModalTrigger'; +import './core/NewButton'; + +import './forms/FormComponentInner'; +import './forms/FormErrors'; +import './forms/FormGroup'; +import './forms/FormGroupNone'; +import './forms/FormGroupWithLine'; +import './forms/FormNested'; +import './forms/FormNestedDivider'; +import './forms/FormNestedFoot'; +import './forms/FormNestedHead'; +import './forms/FormSubmit'; +import './forms/controls/Checkbox'; +import './forms/controls/CheckboxGroup'; +import './forms/controls/CountrySelect'; +import './forms/controls/Date'; +import './forms/controls/DateRdt'; +import './forms/controls/DateTime'; +import './forms/controls/DateTimeRdt'; +import './forms/controls/Default'; +import './forms/controls/Email'; +import './forms/controls/Number'; +import './forms/controls/PostalCode'; +import './forms/controls/RadioGroup'; +import './forms/controls/RegionSelect'; +import './forms/controls/Select'; +import './forms/controls/Textarea'; +import './forms/controls/Time'; +import './forms/controls/TimeRdt'; +import './forms/controls/Url'; + +import './theme/ThemeStyles'; + +import './ui/Button'; +import './ui/Alert'; + +import './upload/UploadImage'; +import './upload/UploadInner'; + +export * from './forms/controls/countries'; diff --git a/packages/vulcan-ui-material/components/theme/JssCleanup.jsx b/packages/vulcan-ui-material/components/theme/JssCleanup.jsx new file mode 100644 index 000000000..4d344294b --- /dev/null +++ b/packages/vulcan-ui-material/components/theme/JssCleanup.jsx @@ -0,0 +1,23 @@ +import React, { PureComponent } from 'react'; + + +class JssCleanup extends PureComponent { + + + // Remove the server-side injected CSS. + componentDidMount() { + if (!document || !document.getElementById) return; + + const jssStyles = document.getElementById('jss-server-side'); + if (jssStyles && jssStyles.parentNode) { +// jssStyles.parentNode.removeChild(jssStyles); + } + } + + render() { + return this.props.children; + } +} + + +export default JssCleanup; diff --git a/packages/vulcan-ui-material/components/theme/ThemeStyles.jsx b/packages/vulcan-ui-material/components/theme/ThemeStyles.jsx new file mode 100644 index 000000000..7441fc01b --- /dev/null +++ b/packages/vulcan-ui-material/components/theme/ThemeStyles.jsx @@ -0,0 +1,232 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import withTheme from '@material-ui/core/styles/withTheme'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import Paper from '@material-ui/core/Paper'; +import { getContrastRatio } from '@material-ui/core/styles/colorManipulator'; +import classNames from 'classnames'; + + +const describeTypography = (theme, className) => { + const typography = className ? theme.typography[className] : theme.typography; + const fontFamily = typography.fontFamily.split(',')[0]; + return `${fontFamily} ${typography.fontWeight} ${typography.fontSize}px`; +}; + + +const mainPalette = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900]; +const altPalette = ['A100', 'A200', 'A400', 'A700']; + + +function getColorBlock(theme, classes, colorName, colorValue, colorTitle) { + const bgColor = theme.palette[colorName][colorValue]; + + let fgColor = theme.palette.common.black; + if (getContrastRatio(bgColor, fgColor) < 7) { + fgColor = theme.palette.common.white; + } + + let blockTitle; + if (colorTitle) { + blockTitle =
{colorName}
; + } + + let rowStyle = { + backgroundColor: bgColor, + color: fgColor, + listStyle: 'none', + padding: 15, + }; + + if (colorValue.toString().indexOf('A1') === 0) { + rowStyle = { + ...rowStyle, + marginTop: 4, + }; + } + + return ( +
  • + {blockTitle} +
    + {colorValue} + {bgColor.toUpperCase()} +
    +
  • + ); +} + +function getColorGroup(options) { + const { theme, classes, color, showAltPalette } = options; + const cssColor = color.replace(' ', '').replace(color.charAt(0), color.charAt(0).toLowerCase()); + let colorsList = []; + colorsList = mainPalette.map(mainValue => getColorBlock(theme, classes, cssColor, mainValue)); + + if (showAltPalette) { + altPalette.forEach(altValue => { + colorsList.push(getColorBlock(theme, classes, cssColor, altValue)); + }); + } + + return ( +
      + {getColorBlock(theme, classes, cssColor, 500, true)} +
      + {colorsList} +
    + ); +} + + +const styles = theme => ({ + root: {}, + paper: { + padding: theme.spacing.unit * 3, + }, + name: { + marginBottom: 60, + }, + blockSpace: { + height: 4, + }, + colorContainer: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + colorGroup: { + padding: '16px 0', + margin: '0 15px 0 0', + flexGrow: 1, + [theme.breakpoints.up('sm')]: { + flexGrow: 0, + }, + }, + colorValue: { + ...theme.typography.caption, + color: 'inherit', + }, +}); + + +const latin = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur justo quam, ' + + 'pellentesque ultrices ex a, aliquet porttitor ante. Donec tellus arcu, viverra ut lorem id, ' + + 'ultrices ultricies enim. Donec enim metus, sollicitudin id lobortis id, iaculis ut arcu. ' + + 'Maecenas sollicitudin congue nisi. Donec convallis, ipsum ac ultricies dignissim, orci ex ' + + 'efficitur lectus, ac lacinia risus nunc at diam. Nam gravida bibendum lectus. Donec ' + + 'scelerisque sem nec urna vestibulum vehicula.'; + + +const ThemeStyles = ({ theme, classes }) => { + return ( + + + + + h1: {describeTypography(theme, 'h1')} + + + h2: {describeTypography(theme, 'h2')} + + + h3: {describeTypography(theme, 'h3')} + + + h4: {describeTypography(theme, 'h4')} + + + + + + + Headline: {describeTypography(theme, 'h5')} + + + Title: {describeTypography(theme, 'h6')} + + + Subtitle1: {describeTypography(theme, 'subtitle1')} + + + Body 1: {describeTypography(theme, 'body1')} - {latin} + + + {latin} + + + Body 2: {describeTypography(theme, 'body2')} - {latin} + + + {latin} + + + Caption: {describeTypography(theme, 'caption')} + + + Base: {describeTypography(theme)} - {latin} + + + Button - {describeTypography(theme)} + + + + + + { + getColorGroup({ + theme, + classes, + color: 'primary', + showAltPalette: true, + }) + } + + + + { + getColorGroup({ + theme, + classes, + color: 'secondary', + showAltPalette: true, + }) + } + + + + { + getColorGroup({ + theme, + classes, + color: 'error', + showAltPalette: true, + }) + } + + + + { + getColorGroup({ + theme, + classes, + color: 'background', + showAltPalette: true, + }) + } + + + + ); +}; + + +ThemeStyles.propTypes = { + theme: PropTypes.object.isRequired, + classes: PropTypes.object.isRequired, +}; + + +registerComponent('ThemeStyles', ThemeStyles, [withTheme, null], [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/ui/Alert.jsx b/packages/vulcan-ui-material/components/ui/Alert.jsx new file mode 100644 index 000000000..7a1254f5d --- /dev/null +++ b/packages/vulcan-ui-material/components/ui/Alert.jsx @@ -0,0 +1,31 @@ +/** + * @Author: Apollinaire Lecocq + * @Date: 09-01-19 + * @Last modified by: apollinaire + * @Last modified time: 10-01-19 + */ +import React from 'react'; +import withStyles from '@material-ui/core/styles/withStyles'; +import { registerComponent } from 'meteor/vulcan:core'; + +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; + +const AlertStyle = theme => ({ + error: { + color: theme.palette.error.main, + backgroundColor: theme.palette.error[100], + fontFamily: theme.typography.fontFamily, + }, + other: { + fontFamily: theme.typography.fontFamily, + }, +}); + +const Alert = ({ children, variant, classes, ...rest }) => ( + + {children} + +); + +registerComponent({ name: 'Alert', component: Alert, hocs: [[withStyles, AlertStyle]] }); diff --git a/packages/vulcan-ui-material/components/ui/Button.jsx b/packages/vulcan-ui-material/components/ui/Button.jsx new file mode 100644 index 000000000..23ea96d84 --- /dev/null +++ b/packages/vulcan-ui-material/components/ui/Button.jsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Components, registerComponent } from 'meteor/vulcan:core'; +import MuiButton from '@material-ui/core/Button'; +import MuiIconButton from '@material-ui/core/IconButton'; + + +const Button = ({ children, color, variant, size, iconButton, ...rest }) => { + switch(variant) { + case 'success': + color = 'primary'; + variant = null; + break; + case 'danger': + color = 'default'; + variant = null; + break; + case 'inverse': + color = 'inherit'; + variant = null; + break; + } + + switch(size) { + case 'xsmall': + size = 'small'; + break; + case 'small': + size = 'medium'; + break; + case 'large': + size = 'large'; + break; + } + + if (iconButton) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +}; + + +registerComponent('Button', Button); diff --git a/packages/vulcan-ui-material/components/upload/UploadImage.jsx b/packages/vulcan-ui-material/components/upload/UploadImage.jsx new file mode 100755 index 000000000..6904e2fba --- /dev/null +++ b/packages/vulcan-ui-material/components/upload/UploadImage.jsx @@ -0,0 +1,117 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent } from 'meteor/vulcan:lib'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import withStyles from '@material-ui/core/styles/withStyles'; +import IconButton from '@material-ui/core/IconButton'; +import DeleteIcon from 'mdi-material-ui/Delete'; +import classNames from 'classnames'; + +/** + * Used by UploadInner to display a single image + */ +const styles = theme => ({ + + uploadImage: { + textAlign: 'center', + marginBottom: theme.spacing.unit * -1, + marginLeft: theme.spacing.unit * 0.5, + marginRight: theme.spacing.unit * 0.5, + }, + + uploadImageContents: { + position: 'relative', + }, + + uploadImageImg: { + display: 'block', + maxWidth: 150, + maxHeight: 150, + }, + + uploadLoading: { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + background: 'rgba(255,255,255,0.8)', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + span: { + display: 'block', + fontSize: '1.5rem', + } + }, + + deleteButton: { + } + +}); + + +class UploadImage extends PureComponent { + + constructor (props) { + super(props); + this.handleClear = this.handleClear.bind(this); + } + + handleClear (event) { + event.preventDefault(); + this.props.clearImage(this.props.index); + } + + // Get the URL of an image or the first in an array of images + getImageUrl (imageOrImageArray) { + // if image is actually an array of formats, use first format + const image = Array.isArray(imageOrImageArray) ? imageOrImageArray[0] : imageOrImageArray; + + // if image is an object, return secure_url; else return image itself + return typeof image === 'string' ? image : image.secure_url; + } + + render () { + const { loading, error, image, style, classes } = this.props; + + return ( +
    + +
    + + + { + loading && + +
    + +
    + } +
    + + + + + +
    + ); + } +} + + +UploadImage.propTypes = { + clearImage: PropTypes.func.isRequired, + index: PropTypes.number.isRequired, + image: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.object]), + loading: PropTypes.bool, + error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), + style: PropTypes.object, + classes: PropTypes.object.isRequired, +}; + + +UploadImage.displayName = 'UploadImageMui'; + + +registerComponent('UploadImage', UploadImage, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/upload/UploadInner.jsx b/packages/vulcan-ui-material/components/upload/UploadInner.jsx new file mode 100755 index 000000000..b3c4ab007 --- /dev/null +++ b/packages/vulcan-ui-material/components/upload/UploadInner.jsx @@ -0,0 +1,181 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent, getComponent } from 'meteor/vulcan:lib'; +import Dropzone from 'react-dropzone'; +import withStyles from '@material-ui/core/styles/withStyles'; +import { FormattedMessage } from 'meteor/vulcan:i18n'; +import FormControl from '@material-ui/core/FormControl'; +import FormLabel from '@material-ui/core/FormLabel'; +import FormHelperText from '@material-ui/core/FormHelperText'; + + +/* + +Material UI GUI for Cloudinary Image Upload component + +*/ + + +const styles = theme => ({ + + root: {}, + + label: {}, + + uploadField: { + marginTop: theme.spacing.unit, + }, + + dropzoneBase: { + borderWidth: 3, + borderStyle: 'dashed', + borderColor: theme.palette.background[900], + backgroundColor: theme.palette.background[100], + color: theme.palette.common.lightBlack, + padding: '30px 60px', + transition: 'all 0.5s', + cursor: 'pointer', + position: 'relative', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + '&[aria-disabled="false"]:hover': { + color: theme.palette.common.midBlack, + borderColor: theme.palette.background['A200'], + } + }, + + dropzoneActive: { + borderStyle: 'solid', + borderColor: theme.palette.status.info, + }, + + dropzoneReject: { + borderStyle: 'solid', + borderColor: theme.palette.status.danger, + }, + + uploadState: {}, + + uploadImages: { + border: `1px solid ${theme.palette.background[500]}`, + backgroundColor: theme.palette.background[100], + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'center', + paddingTop: theme.spacing.unit, + paddingRight: theme.spacing.unit * 0.5, + paddingBottom: theme.spacing.unit, + paddingLeft: theme.spacing.unit * 0.5, + }, +}); + + +const UploadInner = (props) => { + const { + uploading, + images, + disabled, + maxCount, + label, + help, + options, + enableMultiple, + onDrop, + isDeleted, + clearImage, + classes + } = props; + + const UploadImage = getComponent(options.uploadImageComponentName || 'UploadImage'); + + return ( + + + + {label} + + { + help && + + {help} + } +
    + { + (disabled && !enableMultiple) + ? + null + : + +
    + +
    + {uploading && ( +
    + + + +
    + )} +
    + } + + {!!images.length && ( +
    +
    + {images.map( + (image, index) => + !isDeleted(index) && ( + + ) + )} +
    +
    + )} +
    + +
    + ); +}; + + +UploadInner.propTypes = { + uploading: PropTypes.bool, + images: PropTypes.array.isRequired, + disabled: PropTypes.bool, + maxCount: PropTypes.number.isRequired, + label: PropTypes.string, + help: PropTypes.string, + options: PropTypes.object.isRequired, + enableMultiple: PropTypes.bool, + onDrop: PropTypes.func.isRequired, + isDeleted: PropTypes.func.isRequired, + clearImage: PropTypes.func.isRequired, + classes: PropTypes.object.isRequired, +}; + + +UploadInner.displayName = 'UploadInnerMui'; + + +registerComponent('UploadInner', UploadInner, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/en_US.js b/packages/vulcan-ui-material/en_US.js new file mode 100644 index 000000000..57fb41cec --- /dev/null +++ b/packages/vulcan-ui-material/en_US.js @@ -0,0 +1,12 @@ +import { addStrings } from 'meteor/vulcan:core'; + + +addStrings('en', { + + "search.search": "Search", + "search.clear": "Clear search", + "load_more.load_more": "Load more", + "load_more.loaded_count": "Loaded {count} of {totalCount}", + "load_more.loaded_all": "{totalCount, plural, =0 {No items} one {One item} other {# items}}", + +}); diff --git a/packages/vulcan-ui-material/example/Header.jsx b/packages/vulcan-ui-material/example/Header.jsx new file mode 100644 index 000000000..7ae557a4f --- /dev/null +++ b/packages/vulcan-ui-material/example/Header.jsx @@ -0,0 +1,98 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; +import MenuIcon from 'mdi-material-ui/Menu'; +import ChevronLeftIcon from 'mdi-material-ui/ChevronLeft'; +import withStyles from '@material-ui/core/styles/withStyles'; +import { getSetting, Components, registerComponent } from 'meteor/vulcan:core'; +import classNames from 'classnames'; + + +const drawerWidth = 240; +const topBarHeight = 100; + + +const styles = theme => ({ + appBar: { + position: 'absolute', + transition: theme.transitions.create(['margin', 'width'], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(['margin', 'width'], { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + }, + toolbar: { + height: `${topBarHeight}px`, + minHeight: `${topBarHeight}px`, + }, + headerMid: { + flexGrow: 1, + display: 'flex', + alignItems: 'center', + '& h1': { + margin: '0 24px 0 0', + fontSize: '18px', + lineHeight: 1, + } + }, + menuButton: { + marginRight: theme.spacing.unit * 3, + }, +}); + + +const Header = (props, context) => { + const classes = props.classes; + const isSideNavOpen = props.isSideNavOpen; + const toggleSideNav = props.toggleSideNav; + + const siteTitle = getSetting('title', 'My App'); + + return ( + + + + toggleSideNav()} + className={classNames(classes.menuButton)} + color="inherit" + > + {isSideNavOpen ? : } + + +
    + + + {siteTitle} + + +
    + +
    +
    + ); +}; + + +Header.propTypes = { + classes: PropTypes.object.isRequired, + isSideNavOpen: PropTypes.bool, + toggleSideNav: PropTypes.func, +}; + + +Header.displayName = 'Header'; + + +registerComponent('Header', Header, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/example/Layout.jsx b/packages/vulcan-ui-material/example/Layout.jsx new file mode 100644 index 000000000..3d6d52885 --- /dev/null +++ b/packages/vulcan-ui-material/example/Layout.jsx @@ -0,0 +1,136 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Drawer from '@material-ui/core/Drawer'; +import AppBar from '@material-ui/core/AppBar'; +import Toolbar from '@material-ui/core/Toolbar'; +import { Components, replaceComponent, Utils } from 'meteor/vulcan:core'; +import withStyles from '@material-ui/core/styles/withStyles'; +import classNames from 'classnames'; + + +const drawerWidth = 240; +const topBarHeight = 100; + + +const styles = theme => { + const contentPadding = theme.spacing.unit * 8; + + return { + '@global': { + html: { + background: theme.palette.background.default, + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + overflow: 'hidden', + }, + body: { + margin: 0, + }, + }, + root: { + width: '100%', + zIndex: 1, + overflow: 'hidden', + }, + appFrame: { + position: 'relative', + display: 'flex', + height: '100vh', + alignItems: 'stretch', + }, + drawerPaper: { + position: 'relative', + width: drawerWidth, + backgroundColor: theme.palette.background[200], + }, + drawerHeader: { + height: `${topBarHeight}px !important`, + minHeight: `${topBarHeight}px !important`, + position: 'relative !important', + }, + content: { + padding: contentPadding, + width: '100%', + marginLeft: -drawerWidth, + flexGrow: 1, + backgroundColor: theme.palette.background.default, + color: theme.palette.text.primary, + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + height: `calc(100% - ${topBarHeight}px - ${contentPadding * 2}px)`, + marginTop: topBarHeight, + overflowY: 'scroll', + }, + mainShift: { + marginLeft: 0, + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + }, + }; +}; + + +class Layout extends React.Component { + state = { + isOpen: { sideNav: true } + }; + + toggle = (item, openOrClose) => { + const newState = { isOpen: {} }; + newState.isOpen[item] = typeof openOrClose === 'string' ? + openOrClose === 'open' : + !this.state.isOpen[item]; + this.setState(newState); + }; + + render = () => { + const routeName = Utils.slugify(this.props.currentRoute.name); + const classes = this.props.classes; + const isOpen = this.state.isOpen; + + return ( +
    +
    + + + this.toggle('sideNav', openOrClose)} /> + + + + + + + + + +
    + {this.props.children} +
    + + + +
    +
    + ); + }; +} + + +Layout.propTypes = { + classes: PropTypes.object.isRequired, + children: PropTypes.node, +}; + + +Layout.displayName = 'Layout'; + + +replaceComponent('Layout', Layout, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/example/SideNavigation.jsx b/packages/vulcan-ui-material/example/SideNavigation.jsx new file mode 100644 index 000000000..2d30e417f --- /dev/null +++ b/packages/vulcan-ui-material/example/SideNavigation.jsx @@ -0,0 +1,105 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; +import { withRouter } from 'react-router-dom'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Divider from '@material-ui/core/Divider'; +import Collapse from '@material-ui/core/Collapse'; +import ExpandLessIcon from 'mdi-material-ui/ChevronUp'; +import ExpandMoreIcon from 'mdi-material-ui/ChevronDown'; +import LockIcon from 'mdi-material-ui/Lock'; +import UsersIcon from 'mdi-material-ui/AccountMultiple'; +import ThemeIcon from 'mdi-material-ui/Palette'; +import HomeIcon from 'mdi-material-ui/Home'; +import withStyles from '@material-ui/core/styles/withStyles'; +import Users from 'meteor/vulcan:users'; + + +const styles = theme => ({ + root: {}, + nested: { + paddingLeft: theme.spacing.unit * 4, + }, +}); + + +class SideNavigation extends React.Component { + state = { + isOpen: { admin: false } + }; + + toggle = (item) => { + const newState = { isOpen: {} }; + newState.isOpen[item] = !this.state.isOpen[item]; + this.setState(newState); + }; + + render () { + const currentUser = this.props.currentUser; + const classes = this.props.classes; + const isOpen = this.state.isOpen; + + return ( +
    + + + {this.props.history.push('/');}}> + + + + + + + + { + Users.isAdmin(currentUser) && + +
    + + + this.toggle('admin')}> + + + + + {isOpen.admin ? : } + + + {this.props.history.push('/admin');}}> + + + + + + {this.props.history.push('/theme');}}> + + + + + + + +
    + } + +
    + ); + } +} + + +SideNavigation.propTypes = { + classes: PropTypes.object.isRequired, + currentUser: PropTypes.object, +}; + + +SideNavigation.displayName = 'SideNavigation'; + + +registerComponent('SideNavigation', SideNavigation, [withStyles, styles], withCurrentUser, withRouter); diff --git a/packages/vulcan-ui-material/forms.css b/packages/vulcan-ui-material/forms.css new file mode 100644 index 000000000..219fee574 --- /dev/null +++ b/packages/vulcan-ui-material/forms.css @@ -0,0 +1,16 @@ +.form-nested-item { + display: flex; +} + +.form-nested-item-inner { + flex-grow: 1; +} + +.form-nested-item-remove { + padding-top: 8px; + margin-left: 8px; +} + +.form-nested-item-remove > button { + margin-right: -4px; +} diff --git a/packages/vulcan-ui-material/fr_FR.js b/packages/vulcan-ui-material/fr_FR.js new file mode 100644 index 000000000..2d5c04745 --- /dev/null +++ b/packages/vulcan-ui-material/fr_FR.js @@ -0,0 +1,8 @@ +import { addStrings } from 'meteor/vulcan:core'; + +addStrings('fr', { + + "search.search": "Recherche", + "search.clear": "Effacer la recherche", + +}); diff --git a/packages/vulcan-ui-material/history.md b/packages/vulcan-ui-material/history.md new file mode 100644 index 000000000..8613491ff --- /dev/null +++ b/packages/vulcan-ui-material/history.md @@ -0,0 +1,109 @@ +1.12.8_17 / 2019-02-02 +====================== + + * TooltipIntl: Changed display from 'inline-block' to 'inherit' for more flexibility + * Countries: Added getRegionLabel function + +1.12.8_16 / 2019-01-21 +====================== + + * Countries: Fixed bug in validateRegion + +1.12.8_15 / 2019-01-21 +====================== + + * Countries: Fixed bug in validateRegion + +1.12.8_14 / 2019-01-20 +====================== + + * Countries: Added validateRegion function, which given a region value or label, will return the region value ('NY' or 'New York' => 'NY) + * The contents of countries is now exported - this may be refactored out of the core vulcan-material-ui as some point + +1.12.8_13 / 2019-01-14 +====================== + + * ModalTrigger: Added boolean dialogOverflow prop for use cases like popups that can go beyond the size of the dialog box + * MuiSuggest: Fixed bug - The disabled state was not displayed correctly + * MuiSuggest: Fixed bug - After selecting a suggestion, clicking on the control did not re-open the suggestions menu + +1.12.8_12 / 2019-01-12 +====================== + + * Upgraded to Meteor 1.8.0.2 + +1.12.8_11 / 2018-12-21 +====================== + + * SearchInput: Added install autosize-input to readme + * Datatable: Fixed sorting delay + * Datatable: Added tableHeadCell class + * Datatable: Added cellClass column property, which can be a string or a function: column.cellClass({ column, document, currentUser }) + +1.12.8_10 / 2018-12-09 +====================== + + * TooltipIntl: Added icon class + * FormGroupWithLine: Moved caret from the right side to next to the title + * Changed load_more.loaded_all string + +1.12.8_9 / 2018-11-26 +===================== + + * Fixed bug that displayed invalid total count at the bottom of data tables + +1.12.8_8 / 2018-11-23 +===================== + + * Improved the functionality of the LoadMore component + * The showNoMore property has been deprecated + * A showCount property has been added (true by default) that shows a count of loaded and total items + * The load more icon or button is displayed even when infiniteScroll is enabled + +1.12.8_7 / 2018-11-10 +===================== + + * Fixed bug in Datatable.jsx + * Updated ReadMe + +1.12.8_6 / 2018-11-06 +===================== + + * Fixed bug in Datatable.jsx + * Reduced spacing of form components + +1.12.8_5 / 2018-10-31 +===================== + + * Fixed bugs in Datatable pagination + * Set Datatable paginate prop to false by default + +1.12.8_4 / 2018-10-31 +===================== + + * Removed 'fr_FR.js' from package.js because any french strings loaded activates the french language + * Fixed delete button and its tooltips positioning in FormSubmit + * Added pagination to Datatable + +1.12.8_2 / 2018-10-29 +===================== + + * Fixed localization in "clear search" tooltip + * Added name and aria-haspopup properties to the input component to improve compliance and facilitate UAT + * Replaced Date, Time and DateTime form controls with native controls as recommended by MUI. + The deprecated react-datetime version of the controls are still there as DateRdt, TimeRdt and DateTimeRdt, but they are not registered. + * Updated readme + +1.12.8_1 / 2018-10-22 +===================== + + * Made form components compatible with new Form.formComponents property + +1.12.8 / 2018-10-19 +=================== + + * Made improvements to the search box, including keyboard shortcuts (s: focus search; c: clear search) + * Added support in TooltipIntl for tooltips in popovers + * Added action prop to ModalTrigger that enables a parent component to call openModal and closeModal + * Started using MUI tables in Card component + * Fixed bugs in MuiSuggest component diff --git a/packages/vulcan-ui-material/modules/index.js b/packages/vulcan-ui-material/modules/index.js new file mode 100644 index 000000000..5a65d1ab1 --- /dev/null +++ b/packages/vulcan-ui-material/modules/index.js @@ -0,0 +1,4 @@ +export * from './themes'; +export JssCleanup from '../components/theme/JssCleanup'; +import './sampleTheme'; +import './routes'; diff --git a/packages/vulcan-ui-material/modules/routes.js b/packages/vulcan-ui-material/modules/routes.js new file mode 100755 index 000000000..ee9c6cf06 --- /dev/null +++ b/packages/vulcan-ui-material/modules/routes.js @@ -0,0 +1,8 @@ +import { addRoute } from 'meteor/vulcan:core'; + + +addRoute({ + name: 'theme', + path: '/theme', + componentName: 'ThemeStyles', +}); diff --git a/packages/vulcan-ui-material/modules/sampleTheme.js b/packages/vulcan-ui-material/modules/sampleTheme.js new file mode 100644 index 000000000..a382078a7 --- /dev/null +++ b/packages/vulcan-ui-material/modules/sampleTheme.js @@ -0,0 +1,76 @@ +import { registerTheme } from './themes'; +import indigo from '@material-ui/core/colors/indigo'; +import blue from '@material-ui/core/colors/blue'; +import red from '@material-ui/core/colors/red'; + + +/** @ignore */ + +/** + * + * Sample theme to get you out of the gate quickly + * + * For a complete list of configuration variables see: + * https://material-ui.com/customization/themes/ + * + */ + + +const theme = { + + palette: { + primary: indigo, + secondary: blue, + error: red, + }, + + utils: { + + tooltipEnterDelay: 700, + + errorMessage: { + textAlign: 'center', + backgroundColor: red[500], + color: 'white', + borderRadius: '4px', + fontWeight: 'bold', + }, + + denseTable: { + '& > thead > tr > th, & > tbody > tr > td': { + padding: '4px 16px 4px 16px', + }, + '& > thead > tr > th:last-child, & > tbody > tr > td:last-child': { + paddingRight: '16px', + }, + }, + + flatTable: { + '& > thead > tr > th, & > tbody > tr > td': { + padding: '4px 16px 4px 16px', + whiteSpace: 'nowrap', + }, + '& > thead > tr > th:last-child, & > tbody > tr > td:last-child': { + paddingRight: '16px', + }, + }, + + denserTable: { + '& > thead > tr, & > tbody > tr': { + height: '40px', + }, + '& > thead > tr > th, & > tbody > tr > td': { + padding: '4px 16px 4px 16px', + whiteSpace: 'nowrap', + }, + '& > thead > tr > th:last-child, & > tbody > tr > td:last-child': { + paddingRight: '16px', + }, + }, + + }, + +}; + + +registerTheme('Sample', theme); diff --git a/packages/vulcan-ui-material/modules/themes.js b/packages/vulcan-ui-material/modules/themes.js new file mode 100644 index 000000000..eb9b57744 --- /dev/null +++ b/packages/vulcan-ui-material/modules/themes.js @@ -0,0 +1,67 @@ +/** @module vulcan-material-ui */ + +import createMuiTheme from '@material-ui/core/styles/createMuiTheme'; +import { registerSetting, getSetting } from 'meteor/vulcan:core'; + + +registerSetting('muiTheme', 'Sample', 'Material UI theme used by erikdakota:vulcan-material-ui'); + + +export const ThemesTable = {}; // storage for info about themes + + +/** + * Register a theme with a name + * + * @param {String} name The name of the theme to register + * @param {Object} theme The theme object - see defaultTheme.js + * + */ +export const registerTheme = (name, theme) => { + const themeInfo = { + name, + theme, + }; + + ThemesTable[name] = themeInfo; +}; + + +/** + * Get a theme registered with registerTheme() + * + * @param {String} name The name of the theme to get + * + * @returns {Object} A theme object + */ +export const getTheme = (name) => { + const themeInfo = ThemesTable[name]; + if (!themeInfo) return null; + themeInfo.theme.typography = { ...themeInfo.theme.typography, useNextVariants: true } + return createMuiTheme(themeInfo.theme); +}; + +/** + * Get the raw theme object registered with registerTheme() + * + * @param {String} name The name of the theme to get + * + * @returns {Object} The object passed to registerTheme + */ + +export const getRawTheme = (name) => { + const themeInfo = ThemesTable[name]; + if (!themeInfo) return null; + return themeInfo.theme; +} + +/** + * Get the theme specified in the 'muiTheme' setting + * + * @returns {Object} + */ +export const getCurrentTheme = () => { + const themeName = getSetting('muiTheme', 'Sample'); + const theme = getTheme(themeName); + return theme; +}; diff --git a/packages/vulcan-ui-material/package.js b/packages/vulcan-ui-material/package.js new file mode 100644 index 000000000..024850c42 --- /dev/null +++ b/packages/vulcan-ui-material/package.js @@ -0,0 +1,26 @@ +Package.describe({ + name: 'vulcan:ui-material', + version: '1.12.17', + summary: 'Replacement for Vulcan (http://vulcanjs.org/) components using material-ui', + documentation: 'README.md' +}); + + +Package.onUse(function (api) { + api.versionsFrom('METEOR@1.6'); + + api.use([ + 'ecmascript', + 'vulcan:core@1.12.8', + 'vulcan:accounts@1.12.8', + 'vulcan:forms@1.12.8', + ]); + + api.addFiles([ + 'accounts.css', + 'forms.css', + ], ['client', 'server']); + + api.mainModule('client/main.js', 'client'); + api.mainModule('server/main.js', 'server'); +}); diff --git a/packages/vulcan-ui-material/readme.md b/packages/vulcan-ui-material/readme.md new file mode 100644 index 000000000..05f71b4b4 --- /dev/null +++ b/packages/vulcan-ui-material/readme.md @@ -0,0 +1,208 @@ +# erikdakoda:vulcan-material-ui 1.12.8_13 + +Replacement for [Vulcan](http://vulcanjs.org/) components using [Material-UI](https://material-ui.com/). +This version has been tested against Vulcan 1.12.8 and Material-UI 3.1.0. + +To give me feedback open an issue on GitHub or you can reach me on the [Vulcan Slack](https://vulcanjs.slack.com) +channel as erikdakoda. + +There are some nice bonus features like a CountrySelect with autocomplete and theming. + +All components in vulcan:ui-bootstrap, vulcan:forms and vulcan:accounts have been implemented except for Icon. + +## Installation + +To add vulcan-material-ui to an existing Vulcan project, enter the following: + +``` sh +meteor add erikdakoda:vulcan-material-ui + +meteor npm install --save @material-ui/core +meteor npm install --save react-jss +meteor npm install --save mdi-material-ui +meteor npm install --save react-autosuggest +meteor npm install --save autosuggest-highlight +meteor npm install --save react-isolated-scroll +meteor npm install --save-exact react-keyboard-event-handler@1.3.2 +meteor npm install --save autosize-input +``` + +> NOTE: If you want to avoid deprecation warnings added in MUI versions after 3.1.0, you can lock MUI to the currently supported version using `meteor npm install --save @material-ui/core@3.1.0`. Don't for get to remove or update the version number when you update this package in the future. + + +> IMPORTANT: Please note that I have abandoned material-ui-icons in favor of mdi-material-ui because it has a much larger [selection of icons](https://materialdesignicons.com/). + +This package no longer depends on `vulcan:ui-boostrap`, so you can remove it. + +To activate the example layout copy the three components to your project and import them: + +``` javascript +import './example/Header', +import './example/Layout', +import './example/SideNavigation', +``` + +## Theming + +For an example theme see `modules/sampleTheme.js`. For a complete list of values you can customize, +see the [MUI Default Theme](https://material-ui-next.com/customization/default-theme/). + +Register your theme in the Vulcan environment by giving it a name: `registerTheme('MyTheme', theme);`. +You can have multiple themes registered and you can specify which one to use in your settings file using the `muiTheme` public setting. + +In addition to the Material UI spec, I use a `utils` section in my themes where I place global variables for reusable styles. +For example the sample theme contains + +``` +const theme = { + + . . . + + utils: { + + tooltipEnterDelay: 700, + + errorMessage: { + textAlign: 'center', + backgroundColor: red[500], + color: 'white', + borderRadius: '4px', + }, + + . . . + + // additional utils definitions go here + + }, + +}; +``` + +You can use tooltipEnterDelay (or any other variable you define in utils) anywhere you include the withTheme HOC. See `/components/bonus/TooltipIconButton.jsx` for an example. + +You can use errorMessage (or any other style fragment you define in utils) anywhere you include the withStyles HOC. See `/components/accounts/Form.jsx` for an example. + +## Server Side Rendering (SSR) + +Material UI and Vulcan support SSR, but this is a complex beast with pitfalls. Sometimes you will see a warning like this: + +`Warning: Prop className did not match. Server: "MuiChip-label-131" Client: "MuiChip-label-130"` + +Sometimes the React rendered on the server and the client don't match exactly and this causes a problem with [JSS](https://material-ui-next.com/customization/css-in-js/#jss). This is a complicated issue that has multiple causes and I will be working on solving each of the issues causing this over time. + +Your pages should still render correctly, but there may be a blink and redraw when the first page after SSR loads in the browser. + +In your own code, make sure that your components will render the same on the server and the client. This means not referring to client-side object such as `document` or `jQuery`. If you have a misbehaving component, try wrapping it with [react-no-ssr](https://github.com/kadirahq/react-no-ssr). + +## Form Controls + +You can pass a couple of extra options to inputs from the `form` property of your schema: + +``` javascript + userKey: { + type: String, + label: 'User key', + description: 'The user’s key', + optional: true, + hidden: function ({ document }) { + return !document.platformId || !document.usePlatformApp; + }, + inputProperties: { + autoFocus: true, // focus this input when the form loads + addonBefore: , // adorn the start of the input + addonAfter: , // adorn the end of the input + inputClassName: 'halfWidthLeft', // use 'halfWidthLeft' or 'halfWidthRight' + // to display two controls side by side + hideLabel: true, // hide the label + rows: 10, // for textareas you can specify the rows + variant: 'switch', // for checkboxgroups you can use either + // 'checkbox' (default) or 'switch' + inputProps: { step: 'any' } // Attributes applied to the input element, for + // ex pass the step attr to a number input + }, + group: platformGroup, + canRead: ['members'], + canCreate: ['members'], + canUpdate: ['members'], + }, +``` + +> Note: `form.getHidden` has been deprecated. Now you can just pass a function to `hidden`. + +## Form Groups + +You can pass a couple of extra options to form groups as well: + +``` javascript + const platformGroup: { + name: 'shops.platform', + order: 4, + startComponent: 'ShopsPlatformTitle', // component to put at the top of the form group + endComponent: 'ShopsConnectButtons', // component to put at the bottom of the form group + }, +``` + +## DataTable + +You can pass the DataTable component an `editComponent` property in addition to or instead of `showEdit`. Here is a simplified example: + +``` javascript +const AgendaJobActions = ({ collection, document }) => { + const scheduleAgent = () => { + Meteor.call('scheduleAgent', document.agentId); + }; + + return } + onClick={scheduleAgent}/>; +}; + +AgendaJobActionsInner.propTypes = { + collecion: PropTypes.object.isRequired, + document: PropTypes.object.isRequired, +}; + + +``` + +You can also control the spacing of the table cells using the `dense` property. Valid values are: + +| Value | Description | +| ------- | ------------ | +| dense | right cell padding of 16px instead of 56px | +| flat | right cell padding of 16px and nowrap | +| denser | right cell padding of 16px, nowrap, and row height of 40px instead of 56px | + +You can also use other string values, as long as you define a `utils` entry named the same + `Table`, for example `myCustomTable`. Check out the sample theme for examples. + + +## CountrySelect + +There is an additional component, an autosuggest-based country select. + +``` javascript + country: { + type: String, + label: 'Country', + input: 'CountrySelect', + canRead: ['guests'], + canCreate: ['members'], + canUpdate: ['members'], + }, +``` + +Countries are stored as their 2-letter country codes. I have included a helper function for displaying the country name: + +``` javascript +import Typography from '@material-ui/core/Typography'; +import { getCountryLabel } from 'meteor/erikdakoda:vulcan-material-ui'; + + + {getCountryLabel(supplier.country)} + +``` + diff --git a/packages/vulcan-ui-material/server/main.js b/packages/vulcan-ui-material/server/main.js new file mode 100644 index 000000000..1ed6f2fb0 --- /dev/null +++ b/packages/vulcan-ui-material/server/main.js @@ -0,0 +1,3 @@ +export * from '../components/index'; +export * from '../modules/index'; +import './wrapWithMuiTheme'; diff --git a/packages/vulcan-ui-material/server/wrapWithMuiTheme.jsx b/packages/vulcan-ui-material/server/wrapWithMuiTheme.jsx new file mode 100644 index 000000000..32933c0e5 --- /dev/null +++ b/packages/vulcan-ui-material/server/wrapWithMuiTheme.jsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { addCallback } from 'meteor/vulcan:core'; +import JssProvider from 'react-jss/lib/JssProvider'; +import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider'; +import createGenerateClassName from '@material-ui/core/styles/createGenerateClassName'; +import { getCurrentTheme } from '../modules/themes'; +import { SheetsRegistry } from 'react-jss/lib/jss'; +import JssCleanup from '../components/theme/JssCleanup'; + + +function wrapWithMuiTheme (app, {context }) { + const sheetsRegistry = new SheetsRegistry(); + context.sheetsRegistry = sheetsRegistry; + + const sheetsManager = new Map(); + + const theme = getCurrentTheme(); + + const generateClassName = createGenerateClassName({ seed: '' }); + + return ( + + + + {app} + + + + ); +} + + +function injectJss(sink, { context }) { + const sheets = context.sheetsRegistry.toString(); + sink.appendToHead( + `` + ); + return sink; +} + + +addCallback('router.server.wrapper', wrapWithMuiTheme); +addCallback('router.server.postRender', injectJss); From b7b872591a9ca35fe8b7d0dd36ef773f9e8d0af3 Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 10:11:56 +0100 Subject: [PATCH 06/35] create vulcan:ui-material, update to Apollo2 RR4 --- .../imports/ui/components/EnrollAccount.jsx | 2 +- packages/vulcan-ui-material/.editorconfig | 15 -- packages/vulcan-ui-material/.eslintrc | 86 -------- packages/vulcan-ui-material/.gitignore | 3 + .../components/core/EditButton.jsx | 86 +++++--- .../components/core/ModalTrigger.jsx | 89 ++++---- .../components/forms/FormComponentInner.jsx | 2 +- .../components/forms/FormGroup.jsx | 205 ------------------ .../forms/FormNestedArrayLayout.jsx | 24 ++ .../components/forms/FormNestedDivider.jsx | 16 +- .../components/forms/FormNestedFoot.jsx | 4 +- .../forms/base-controls/MuiRadioGroup.jsx | 6 +- .../forms/base-controls/mixins/component.jsx | 1 + .../vulcan-ui-material/components/index.js | 5 +- packages/vulcan-ui-material/en_US.js | 10 +- .../vulcan-ui-material/example/Layout.jsx | 93 ++++---- .../example/SideNavigation.jsx | 11 +- packages/vulcan-ui-material/fr_FR.js | 7 +- packages/vulcan-ui-material/modules/routes.js | 14 +- packages/vulcan-ui-material/package.js | 3 +- packages/vulcan-ui-material/readme.md | 14 +- 21 files changed, 217 insertions(+), 479 deletions(-) delete mode 100644 packages/vulcan-ui-material/.editorconfig delete mode 100755 packages/vulcan-ui-material/.eslintrc delete mode 100644 packages/vulcan-ui-material/components/forms/FormGroup.jsx create mode 100644 packages/vulcan-ui-material/components/forms/FormNestedArrayLayout.jsx diff --git a/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx b/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx index 012f65456..015f8c6b2 100644 --- a/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx +++ b/packages/vulcan-accounts/imports/ui/components/EnrollAccount.jsx @@ -6,7 +6,7 @@ import { STATES } from '../../helpers.js'; class AccountsEnrollAccount extends PureComponent { componentDidMount() { - const token = this.props.params.token; + const token = this.props.match.params.token; Accounts._loginButtonsSession.set('enrollAccountToken', token); } diff --git a/packages/vulcan-ui-material/.editorconfig b/packages/vulcan-ui-material/.editorconfig deleted file mode 100644 index 68b3a5241..000000000 --- a/packages/vulcan-ui-material/.editorconfig +++ /dev/null @@ -1,15 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.js] -trim_trailing_whitespace = false - -[*.md] -trim_trailing_whitespace = false diff --git a/packages/vulcan-ui-material/.eslintrc b/packages/vulcan-ui-material/.eslintrc deleted file mode 100755 index 39277e6db..000000000 --- a/packages/vulcan-ui-material/.eslintrc +++ /dev/null @@ -1,86 +0,0 @@ -{ - "extends": [ - "eslint:recommended", - //"airbnb", - "plugin:meteor/recommended", - "plugin:react/recommended" - ], - "parser": "babel-eslint", - "parserOptions": { - "allowImportExportEverywhere": true, - "ecmaVersion": 6, - "sourceType": "module" - }, - "rules": { - "babel/generator-star-spacing": 0, - "babel/new-cap": [1, { - "capIsNewExceptions": [ - "Optional", - "OneOf", - "Maybe", - "MailChimpAPI", - "Juice", - "Run", - "AppComposer", - "Query", - "InArray" - ] - }], - "babel/array-bracket-spacing": 0, - "babel/object-curly-spacing": 0, - "babel/object-shorthand": 0, - "babel/arrow-parens": 0, - "babel/no-await-in-loop": 1, - "comma-dangle": 0, - "key-spacing": 0, - "no-extra-boolean-cast": 0, - "no-undef": 1, - "no-unused-vars": [1, { - "vars": "all", - "args": "none", - "varsIgnorePattern": "React|PropTypes|Component" - }], - "react/prop-types": 0, - "react/display-name": 0, - "meteor/audit-argument-checks": 0, - "meteor/no-session": 0, - "no-case-declarations": 0, - "no-console": 0, - "semi": "error", - "quotes": [ - 1, - "single" - ] - }, - "env": { - "browser": true, - "commonjs": true, - "es6": true, - "meteor": true, - "node": true - }, - "plugins": [ - "babel", - "meteor", - "react", - "jsx-a11y" - ], - "settings": { - "import/resolver": { - "meteor": { - "paths": [ - "/usr/local/share/global_modules" - ], - "moduleDirectory": [ - "node_modules", - "packages" - ] - } - } - }, - "root": true, - "globals": { - "param": true, - "returns": true - } -} diff --git a/packages/vulcan-ui-material/.gitignore b/packages/vulcan-ui-material/.gitignore index e0ac94aec..f8ca86071 100644 --- a/packages/vulcan-ui-material/.gitignore +++ b/packages/vulcan-ui-material/.gitignore @@ -1,3 +1,6 @@ npm-debug.log node_modules .idea/workspace.xml + +### eslint-config +.eslintrc diff --git a/packages/vulcan-ui-material/components/core/EditButton.jsx b/packages/vulcan-ui-material/components/core/EditButton.jsx index 7e391a648..ba1f9ff77 100644 --- a/packages/vulcan-ui-material/components/core/EditButton.jsx +++ b/packages/vulcan-ui-material/components/core/EditButton.jsx @@ -4,31 +4,40 @@ import { Components, registerComponent } from 'meteor/vulcan:core'; import { intlShape } from 'meteor/vulcan:i18n'; import EditIcon from 'mdi-material-ui/Pencil'; - -const EditButton = ({ - collection, - document, - color = 'default', - variant, - triggerClasses, - buttonClasses, - ...props - }, { intl }) => ( - +const EditButton = ( + { + collection, + document, + color = 'default', + variant, + triggerClasses, + buttonClasses, + showRemove, + ...props + }, + { intl } +) => ( } - color={color} - variant={variant} - classes={buttonClasses} - />} + component={ + } + color={color} + variant={variant} + classes={buttonClasses} + /> + } > - + ); - EditButton.propTypes = { collection: PropTypes.object.isRequired, document: PropTypes.object.isRequired, @@ -36,27 +45,32 @@ EditButton.propTypes = { variant: PropTypes.string, triggerClasses: PropTypes.object, buttonClasses: PropTypes.object, + showRemove: PropTypes.bool }; - EditButton.contextTypes = { intl: intlShape }; - EditButton.displayName = 'EditButton'; - registerComponent('EditButton', EditButton); - /* EditForm Component */ -const EditForm = ({ collection, document, closeModal, options, successCallback, removeSuccessCallback,...props }) => { - +const EditForm = ({ + collection, + document, + closeModal, + options, + successCallback, + removeSuccessCallback, + showRemove, + ...props +}) => { const success = successCallback ? () => { successCallback(); @@ -70,17 +84,17 @@ const EditForm = ({ collection, document, closeModal, options, successCallback, closeModal(); } : closeModal; - + return ( - -); -} + + ); +}; registerComponent('EditForm', EditForm); diff --git a/packages/vulcan-ui-material/components/core/ModalTrigger.jsx b/packages/vulcan-ui-material/components/core/ModalTrigger.jsx index 8a3dbd159..928256805 100644 --- a/packages/vulcan-ui-material/components/core/ModalTrigger.jsx +++ b/packages/vulcan-ui-material/components/core/ModalTrigger.jsx @@ -1,12 +1,15 @@ import React, { PureComponent } from 'react'; import PropTypes from 'prop-types'; import { intlShape } from 'meteor/vulcan:i18n'; -import { registerComponent } from 'meteor/vulcan:core'; +import { registerComponent, Components } from 'meteor/vulcan:core'; import withStyles from '@material-ui/core/styles/withStyles'; import Dialog from '@material-ui/core/Dialog'; -import DialogContent from '@material-ui/core/DialogContent'; import DialogTitle from '@material-ui/core/DialogTitle'; +import DialogContent from '@material-ui/core/DialogContent'; import Button from '@material-ui/core/Button'; +import Tooltip from '@material-ui/core/Tooltip'; +import Close from 'mdi-material-ui/Close'; + import classNames from 'classnames'; @@ -17,27 +20,29 @@ const styles = theme => ({ button: {}, anchor: {}, dialog: {}, - dialogPaper: {}, - dialogTitle: {}, + dialogPaper: { + overflowY: 'visible', + }, + dialogTitle: { + padding: theme.spacing.unit * 4, + }, dialogContent: { paddingTop: '4px', }, - dialogOverflow: { - overflowY: 'visible', - }, + closeButton: { + position: 'absolute', + right: theme.spacing.unit, + top: theme.spacing.unit, + } }); - class ModalTrigger extends PureComponent { - + constructor (props) { super(props); - this.state = { modalIsOpen: false }; - - } - + componentDidMount() { if (this.props.action) { this.props.action({ @@ -50,11 +55,11 @@ class ModalTrigger extends PureComponent { openModal = () => { this.setState({ modalIsOpen: true }); }; - + closeModal = () => { this.setState({ modalIsOpen: false }); }; - + render () { const { className, @@ -67,9 +72,9 @@ class ModalTrigger extends PureComponent { children, classes, } = this.props; - + const intl = this.context.intl; - + const label = labelId ? intl.formatMessage({ id: labelId }) : this.props.label; const title = titleId ? intl.formatMessage({ id: titleId }) : this.props.title; const overflowClass = dialogOverflow && classes.dialogOverflow; @@ -83,35 +88,39 @@ class ModalTrigger extends PureComponent { :
    {label}; - + const childrenComponent = typeof children.type === 'function' ? React.cloneElement(children, { closeModal: this.closeModal }) : children; - + return ( - + {triggerComponent} - - - - { - title && - - {title} - } - - - {childrenComponent} - - - - + + + {title} + + + + + + + + + + + + {childrenComponent} + + + + ); } diff --git a/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx b/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx index 473f39e6d..de8fdf7f2 100644 --- a/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx +++ b/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx @@ -118,7 +118,7 @@ FormComponentInner.propTypes = { showCharsRemaining: PropTypes.bool.isRequired, charsRemaining: PropTypes.number, charsCount: PropTypes.number, - max: PropTypes.number, + max: PropTypes.oneOfType([PropTypes.number, PropTypes.instanceOf(Date)]), formInput: PropTypes.func.isRequired, }; diff --git a/packages/vulcan-ui-material/components/forms/FormGroup.jsx b/packages/vulcan-ui-material/components/forms/FormGroup.jsx deleted file mode 100644 index 3ad7781db..000000000 --- a/packages/vulcan-ui-material/components/forms/FormGroup.jsx +++ /dev/null @@ -1,205 +0,0 @@ -import React, { PureComponent } from 'react'; -import PropTypes from 'prop-types'; -import { Components, replaceComponent, instantiateComponent, } from 'meteor/vulcan:core'; -import Users from 'meteor/vulcan:users'; -import withStyles from '@material-ui/core/styles/withStyles'; -import Collapse from '@material-ui/core/Collapse'; -import Typography from '@material-ui/core/Typography'; -import Paper from '@material-ui/core/Paper'; -import ExpandLessIcon from 'mdi-material-ui/ChevronUp'; -import ExpandMoreIcon from 'mdi-material-ui/ChevronDown'; -import classNames from 'classnames'; - - -const styles = theme => ({ - root: { - minWidth: '320px' - }, - paper: { - padding: theme.spacing.unit * 3 - }, - subtitle1: { - display: 'flex', - alignItems: 'center', - paddingLeft: theme.spacing.unit / 2, - marginTop: theme.spacing.unit * 5, - marginBottom: theme.spacing.unit, - color: theme.palette.primary[500], - }, - collapsible: { - cursor: 'pointer', - }, - label: {}, - toggle: { - '& svg': { - width: 21, - height: 21, - display: 'block', - } - }, - container: { - paddingLeft: 4, - paddingRight: 4, - marginLeft: -4, - marginRight: -4, - }, - entered: { - overflow: 'visible', - }, -}); - - -class FormGroup extends PureComponent { - - - constructor (props) { - super(props); - - this.isAdmin = props.name === 'admin'; - - this.state = { - collapsed: props.startCollapsed || this.isAdmin, - }; - } - - - toggle = () => { - const collapsible = this.props.collapsible || this.isAdmin; - if (!collapsible) return; - - this.setState({ - collapsed: !this.state.collapsed - }); - }; - - - renderHeading = () => { - const { classes, label } = this.props; - const collapsible = this.props.collapsible || this.isAdmin; - - return ( - - -
    - {label} -
    - - { - collapsible && - -
    - { - this.state.collapsed - ? - - : - - } -
    - } - -
    - ); - }; - - - // if at least one of the fields in the group has an error, the group as a whole has an error - hasErrors = () => _.some(this.props.fields, field => { - return !!this.props.errors.filter(error => error.path === field.path).length; - }); - - - render () { - const { - name, - hidden, - classes, - currentUser, - } = this.props; - - if (this.isAdmin && !Users.isAdmin(currentUser)) { - return null; - } - - if (typeof hidden === 'function' ? hidden({ ...this.props }) : hidden) { - return null; - } - - //do not display if no fields, no startComponent and no endComponent - if (!this.props.startComponent && !this.props.endComponent && !this.props.fields.length) { - return null; - } - - const anchorName = name.split('.').length > 1 ? name.split('.')[1] : name; - const collapseIn = !this.state.collapsed || this.hasErrors(); - - const FormComponents = this.props.formComponents; - - return ( - - ); - } - - -} - - -FormGroup.propTypes = { - name: PropTypes.string, - label: PropTypes.string, - order: PropTypes.number, - hidden: PropTypes.bool, - fields: PropTypes.array, - collapsible: PropTypes.bool, - startCollapsed: PropTypes.bool, - updateCurrentValues: PropTypes.func, - startComponent: PropTypes.node, - endComponent: PropTypes.node, - currentUser: PropTypes.object, -}; - - -replaceComponent('FormGroup', FormGroup, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormNestedArrayLayout.jsx b/packages/vulcan-ui-material/components/forms/FormNestedArrayLayout.jsx new file mode 100644 index 000000000..1a7e4e4a0 --- /dev/null +++ b/packages/vulcan-ui-material/components/forms/FormNestedArrayLayout.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { replaceComponent } from 'meteor/vulcan:core'; + +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; + +const FormNestedArrayLayout = ({ hasErrors, label, content }) => ( +
    + + {label} + +
    {content}
    +
    +); +FormNestedArrayLayout.propTypes = { + hasErrors: PropTypes.bool, + label: PropTypes.node, + content: PropTypes.node, +}; +replaceComponent({ + name: 'FormNestedArrayLayout', + component: FormNestedArrayLayout, +}); diff --git a/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx b/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx index 344ba4fdb..4f49615d4 100644 --- a/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx +++ b/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx @@ -1,24 +1,20 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { registerComponent } from 'meteor/vulcan:core'; -import { FormattedMessage } from 'meteor/vulcan:i18n'; +import { replaceComponent } from 'meteor/vulcan:core'; +// import { FormattedMessage } from 'meteor/vulcan:i18n'; import withStyles from '@material-ui/core/styles/withStyles'; import Divider from '@material-ui/core/Divider'; - const styles = theme => ({ - divider: { - marginLeft: -24, - marginRight: -24, + // marginLeft: -24, + // marginRight: -24, marginTop: 16, marginBottom: 23, }, - }); - -const FormNestedDivider = ({ classes, label, addItem }) => ; +const FormNestedDivider = ({ classes, label, addItem }) => ; FormNestedDivider.propTypes = { classes: PropTypes.object.isRequired, @@ -26,4 +22,4 @@ FormNestedDivider.propTypes = { addItem: PropTypes.func, }; -registerComponent('FormNestedDivider', FormNestedDivider, [withStyles, styles]); +replaceComponent('FormNestedDivider', FormNestedDivider, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx b/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx index 7cb6b98eb..c78ab73ad 100644 --- a/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx +++ b/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx @@ -5,8 +5,8 @@ import { FormattedMessage } from 'meteor/vulcan:i18n'; import Grid from '@material-ui/core/Grid'; const FormNestedFoot = ({ label, addItem }) => ( - - + + diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx index d111dd111..229b3d7c6 100644 --- a/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx @@ -73,11 +73,7 @@ const MuiRadioGroup = createReactClass({ options: PropTypes.array.isRequired }, - getInitialState: function () { - if (this.props.refFunction) { - this.props.refFunction(this); - } - }, + getDefaultProps: function () { return { diff --git a/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx b/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx index 7913cbfc5..fa74a6414 100644 --- a/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx +++ b/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx @@ -105,6 +105,7 @@ export default { 'description', 'clearField', 'regEx', + 'allowedValues', 'mustComplete', 'renderComponent', 'formInput', diff --git a/packages/vulcan-ui-material/components/index.js b/packages/vulcan-ui-material/components/index.js index e619680ee..ebfdc6fcd 100644 --- a/packages/vulcan-ui-material/components/index.js +++ b/packages/vulcan-ui-material/components/index.js @@ -7,7 +7,7 @@ import './accounts/AccountsPasswordOrService'; import './accounts/AccountsSocialButtons'; import './bonus/LoadMore'; -import './bonus/SearchInput'; +// import './bonus/SearchInput'; import './bonus/TooltipIntl'; import './bonus/TooltipIconButton'; @@ -20,10 +20,11 @@ import './core/NewButton'; import './forms/FormComponentInner'; import './forms/FormErrors'; -import './forms/FormGroup'; +//import './forms/FormGroup'; import './forms/FormGroupNone'; import './forms/FormGroupWithLine'; import './forms/FormNested'; +import './forms/FormNestedArrayLayout'; import './forms/FormNestedDivider'; import './forms/FormNestedFoot'; import './forms/FormNestedHead'; diff --git a/packages/vulcan-ui-material/en_US.js b/packages/vulcan-ui-material/en_US.js index 57fb41cec..29711d64d 100644 --- a/packages/vulcan-ui-material/en_US.js +++ b/packages/vulcan-ui-material/en_US.js @@ -3,10 +3,10 @@ import { addStrings } from 'meteor/vulcan:core'; addStrings('en', { - "search.search": "Search", - "search.clear": "Clear search", - "load_more.load_more": "Load more", - "load_more.loaded_count": "Loaded {count} of {totalCount}", - "load_more.loaded_all": "{totalCount, plural, =0 {No items} one {One item} other {# items}}", + 'search.search': 'Search', + 'search.clear': 'Clear search', + 'load_more.load_more': 'Load more', + 'load_more.loaded_count': 'Loaded {count} of {totalCount}', + 'load_more.loaded_all': '{totalCount, plural, =0 {No items} one {One item} other {# items}}', }); diff --git a/packages/vulcan-ui-material/example/Layout.jsx b/packages/vulcan-ui-material/example/Layout.jsx index 3d6d52885..63c472239 100644 --- a/packages/vulcan-ui-material/example/Layout.jsx +++ b/packages/vulcan-ui-material/example/Layout.jsx @@ -74,63 +74,64 @@ const styles = theme => { }; -class Layout extends React.Component { - state = { - isOpen: { sideNav: true } - }; - toggle = (item, openOrClose) => { - const newState = { isOpen: {} }; - newState.isOpen[item] = typeof openOrClose === 'string' ? - openOrClose === 'open' : - !this.state.isOpen[item]; - this.setState(newState); - }; + class Layout extends React.Component { + state = { + isOpen: { sideNav: true } + }; - render = () => { - const routeName = Utils.slugify(this.props.currentRoute.name); - const classes = this.props.classes; - const isOpen = this.state.isOpen; + toggle = (item, openOrClose) => { + const newState = { isOpen: {} }; + newState.isOpen[item] = typeof openOrClose === 'string' ? + openOrClose === 'open' : + !this.state.isOpen[item]; + this.setState(newState); + }; - return ( -
    -
    + render = () => { + const routeName = Utils.slugify(this.props.currentRoute.name); + const classes = this.props.classes; + const isOpen = this.state.isOpen; - - this.toggle('sideNav', openOrClose)} /> + return ( +
    +
    - - - - - - - + + this.toggle('sideNav', openOrClose)} /> -
    - {this.props.children} -
    + + + + + + + - +
    + {this.props.children} +
    + + +
    -
    - ); + ); + }; + } + + + Layout.propTypes = { + classes: PropTypes.object.isRequired, + children: PropTypes.node, }; -} -Layout.propTypes = { - classes: PropTypes.object.isRequired, - children: PropTypes.node, -}; + Layout.displayName = 'Layout'; -Layout.displayName = 'Layout'; - - -replaceComponent('Layout', Layout, [withStyles, styles]); + replaceComponent('Layout', Layout, [withStyles, styles]); diff --git a/packages/vulcan-ui-material/example/SideNavigation.jsx b/packages/vulcan-ui-material/example/SideNavigation.jsx index 2d30e417f..9f3cf0edb 100644 --- a/packages/vulcan-ui-material/example/SideNavigation.jsx +++ b/packages/vulcan-ui-material/example/SideNavigation.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core'; -import { withRouter } from 'react-router-dom'; +import { withRouter } from 'react-router'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; @@ -38,15 +38,14 @@ class SideNavigation extends React.Component { }; render () { - const currentUser = this.props.currentUser; - const classes = this.props.classes; + const { currentUser, classes, history } = this.props; const isOpen = this.state.isOpen; return (
    - {this.props.history.push('/');}}> + {history.push('/');}}> @@ -69,14 +68,14 @@ class SideNavigation extends React.Component { {this.props.history.push('/admin');}}> + onClick={() => {browserHistory.push('/admin');}}> {this.props.history.push('/theme');}}> + onClick={() => {browserHistory.push('/theme');}}> diff --git a/packages/vulcan-ui-material/fr_FR.js b/packages/vulcan-ui-material/fr_FR.js index 2d5c04745..7ba3c6d2f 100644 --- a/packages/vulcan-ui-material/fr_FR.js +++ b/packages/vulcan-ui-material/fr_FR.js @@ -1,8 +1,7 @@ import { addStrings } from 'meteor/vulcan:core'; addStrings('fr', { - - "search.search": "Recherche", - "search.clear": "Effacer la recherche", - + 'search.search': 'Recherche', + 'search.clear': 'Effacer la recherche', + 'modal.close': 'Fermer', }); diff --git a/packages/vulcan-ui-material/modules/routes.js b/packages/vulcan-ui-material/modules/routes.js index ee9c6cf06..55d267e77 100755 --- a/packages/vulcan-ui-material/modules/routes.js +++ b/packages/vulcan-ui-material/modules/routes.js @@ -1,8 +1,10 @@ import { addRoute } from 'meteor/vulcan:core'; - -addRoute({ - name: 'theme', - path: '/theme', - componentName: 'ThemeStyles', -}); +//Only create route on dev mode, not production. +if (Meteor.isDevelopment) { + addRoute({ + name: 'theme', + path: '/theme', + componentName: 'ThemeStyles', + }); +} diff --git a/packages/vulcan-ui-material/package.js b/packages/vulcan-ui-material/package.js index 024850c42..0e5d542b1 100644 --- a/packages/vulcan-ui-material/package.js +++ b/packages/vulcan-ui-material/package.js @@ -5,7 +5,6 @@ Package.describe({ documentation: 'README.md' }); - Package.onUse(function (api) { api.versionsFrom('METEOR@1.6'); @@ -23,4 +22,4 @@ Package.onUse(function (api) { api.mainModule('client/main.js', 'client'); api.mainModule('server/main.js', 'server'); -}); +}); \ No newline at end of file diff --git a/packages/vulcan-ui-material/readme.md b/packages/vulcan-ui-material/readme.md index 05f71b4b4..c644b6807 100644 --- a/packages/vulcan-ui-material/readme.md +++ b/packages/vulcan-ui-material/readme.md @@ -1,10 +1,10 @@ -# erikdakoda:vulcan-material-ui 1.12.8_13 +# vulcan:ui-material 1.12.8_13 + +Package initially created by [Erik Dakoda](https://github.com/ErikDakoda) ([`erikdakoda:vulcan-material-ui`](https://github.com/ErikDakoda/vulcan-material-ui)) + Replacement for [Vulcan](http://vulcanjs.org/) components using [Material-UI](https://material-ui.com/). -This version has been tested against Vulcan 1.12.8 and Material-UI 3.1.0. -To give me feedback open an issue on GitHub or you can reach me on the [Vulcan Slack](https://vulcanjs.slack.com) -channel as erikdakoda. There are some nice bonus features like a CountrySelect with autocomplete and theming. @@ -15,9 +15,9 @@ All components in vulcan:ui-bootstrap, vulcan:forms and vulcan:accounts have bee To add vulcan-material-ui to an existing Vulcan project, enter the following: ``` sh -meteor add erikdakoda:vulcan-material-ui +meteor add vulcan:ui-material -meteor npm install --save @material-ui/core +meteor npm install --save @material-ui/core@3.1.0 meteor npm install --save react-jss meteor npm install --save mdi-material-ui meteor npm install --save react-autosuggest @@ -48,7 +48,7 @@ For an example theme see `modules/sampleTheme.js`. For a complete list of values see the [MUI Default Theme](https://material-ui-next.com/customization/default-theme/). Register your theme in the Vulcan environment by giving it a name: `registerTheme('MyTheme', theme);`. -You can have multiple themes registered and you can specify which one to use in your settings file using the `muiTheme` public setting. +You can have multiple themes registered and you can specify which one to use in your settings file using the `muiTheme` **public** setting. In addition to the Material UI spec, I use a `utils` section in my themes where I place global variables for reusable styles. For example the sample theme contains From 9db0f47106d96d66279908b88886621389f50f0a Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 13:30:01 +0100 Subject: [PATCH 07/35] lintfix --- .eslintrc | 1 + package-lock.json | 208 ++++++++++-------- package.json | 2 +- .../accounts/AccountsSocialButtons.jsx | 6 +- .../components/bonus/ScrollTrigger.jsx | 4 +- .../forms/base-controls/MuiInput.jsx | 2 +- .../components/forms/controls/DateTimeRdt.jsx | 2 +- .../components/upload/UploadInner.jsx | 2 +- .../example/SideNavigation.jsx | 4 +- packages/vulcan-ui-material/modules/themes.js | 4 +- 10 files changed, 132 insertions(+), 103 deletions(-) diff --git a/.eslintrc b/.eslintrc index 845d06457..d4af31278 100644 --- a/.eslintrc +++ b/.eslintrc @@ -54,6 +54,7 @@ "single", "avoid-escape" ], + "react/display-name": 2, "react/prop-types": 0, "semi": [2, "always"] }, diff --git a/package-lock.json b/package-lock.json index 279f2043d..cdf6b7e43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -330,13 +330,22 @@ "warning": "^3.0.0" }, "dependencies": { - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" } } } @@ -6724,6 +6733,13 @@ "safe-buffer": "^5.0.1", "string_decoder": "~1.0.0", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + } } }, "rimraf": { @@ -7797,31 +7813,31 @@ } }, "react-bootstrap": { - "version": "1.0.0-beta.3", - "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0-beta.3.tgz", - "integrity": "sha512-/eiSmRZE92q6m7uen3oAsOTGY4uBJkZDv32fwxUeyjesf834GUDaEhu1dzj10fmxSeVHW9O6UOKj9GkbwIIkMg==", + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-1.0.0-beta.5.tgz", + "integrity": "sha512-Osm0OtTbYwfsT1rpu88ESWuAHZxfaHFNKFiW8w3w+6YY9/bLEPHbGRZA6W21fg5yvcuKN9hJKT857TTHgY7SoQ==", "requires": { - "@babel/runtime": "^7.0.0", + "@babel/runtime": "^7.2.0", "@react-bootstrap/react-popper": "1.2.1", "classnames": "^2.2.6", - "dom-helpers": "^3.2.0", + "dom-helpers": "^3.4.0", "invariant": "^2.2.3", "keycode": "^2.1.2", - "popper.js": "^1.14.3", + "popper.js": "^1.14.6", "prop-types": "^15.6.2", "prop-types-extra": "^1.1.0", - "react-context-toolbox": "^1.2.3", - "react-overlays": "^1.0.0-beta.17", + "react-context-toolbox": "^2.0.2", + "react-overlays": "^1.0.0", "react-prop-types": "^0.4.0", - "react-transition-group": "^2.4.0", + "react-transition-group": "^2.5.1", "uncontrollable": "^6.0.0", "warning": "^4.0.1" }, "dependencies": { "@babel/runtime": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", - "integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", + "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", "requires": { "regenerator-runtime": "^0.12.0" } @@ -7831,6 +7847,14 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" }, + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -7840,12 +7864,23 @@ } }, "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + }, + "dependencies": { + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + } } }, "regenerator-runtime": { @@ -7854,9 +7889,9 @@ "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" }, "warning": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", - "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { "loose-envify": "^1.0.0" } @@ -7889,9 +7924,9 @@ } }, "react-context-toolbox": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/react-context-toolbox/-/react-context-toolbox-1.2.3.tgz", - "integrity": "sha512-ArHw0UFDM6X8Z9lHZ1rZOhMcn8TXWC9y9sFpeJm11YTIlQsN4A0MadKcps2pCGMwWKmH0o/67t+TmVapwWm5Sw==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-context-toolbox/-/react-context-toolbox-2.0.2.tgz", + "integrity": "sha512-tY4j0imkYC3n5ZlYSgFkaw7fmlCp3IoQQ6DxpqeNHzcD0hf+6V+/HeJxviLUZ1Rv1Yn3N3xyO2EhkkZwHn0m1A==" }, "react-cookie": { "version": "2.1.6", @@ -8008,9 +8043,9 @@ } }, "react-is": { - "version": "16.4.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.4.1.tgz", - "integrity": "sha512-xpb0PpALlFWNw/q13A+1aHeyJyLYCg0/cCHPUA43zYluZuIPHaHL3k8OBsTgQtxqW0FhyDEMvi8fZ/+7+r4OSQ==" + "version": "16.8.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.4.tgz", + "integrity": "sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==" }, "react-lifecycles-compat": { "version": "3.0.4", @@ -8045,9 +8080,9 @@ "integrity": "sha512-IBivBP7xayM7SbbVlAnKgHgoWdfCVqnNBNgQRY5x9iFQm55tFdolR02hX1fCJJtTEKnbaL1stB72/TZc6+p2+Q==" }, "react-overlays": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-1.0.0.tgz", - "integrity": "sha512-YDuUwqWBuVvQvvxPTxKpCuaEZRegDhfgJdQUbVmWVI4gag53zukN/6tNxt0XZpgODQVzLf/w7dFuoDq7YMhygg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-1.2.0.tgz", + "integrity": "sha512-i/FCV8wR6aRaI+Kz/dpJhOdyx+ah2tN1RhT9InPrexyC4uzf3N4bNayFTGtUeQVacj57j1Mqh1CwV60/5153Iw==", "requires": { "classnames": "^2.2.6", "dom-helpers": "^3.4.0", @@ -8055,13 +8090,14 @@ "prop-types-extra": "^1.1.0", "react-context-toolbox": "^2.0.2", "react-popper": "^1.3.2", + "uncontrollable": "^6.0.0", "warning": "^4.0.2" }, "dependencies": { "@babel/runtime": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", - "integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", + "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", "requires": { "regenerator-runtime": "^0.12.0" } @@ -8079,19 +8115,23 @@ "@babel/runtime": "^7.1.2" } }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "js-tokens": "^3.0.0 || ^4.0.0" } }, - "react-context-toolbox": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/react-context-toolbox/-/react-context-toolbox-2.0.2.tgz", - "integrity": "sha512-tY4j0imkYC3n5ZlYSgFkaw7fmlCp3IoQQ6DxpqeNHzcD0hf+6V+/HeJxviLUZ1Rv1Yn3N3xyO2EhkkZwHn0m1A==" + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } }, "regenerator-runtime": { "version": "0.12.1", @@ -8099,9 +8139,9 @@ "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" }, "warning": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", - "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { "loose-envify": "^1.0.0" } @@ -8118,9 +8158,9 @@ } }, "react-popper": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.2.tgz", - "integrity": "sha512-UbFWj55Yt9uqvy0oZ+vULDL2Bw1oxeZF9/JzGyxQ5ypgauRH/XlarA5+HLZWro/Zss6Ht2kqpegtb6sYL8GUGw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.3.tgz", + "integrity": "sha512-ynMZBPkXONPc5K4P5yFWgZx5JGAUIP3pGGLNs58cfAPgK67olx7fmLp+AdpZ0+GoQ+ieFDa/z4cdV6u7sioH6w==", "requires": { "@babel/runtime": "^7.1.2", "create-react-context": "<=0.2.2", @@ -8131,9 +8171,9 @@ }, "dependencies": { "@babel/runtime": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.2.0.tgz", - "integrity": "sha512-oouEibCbHMVdZSDlJBO6bZmID/zA/G/Qx3H1d3rSNPTD+L8UNKvCat7aKWSJ74zYbm5zWGh0GQN0hKj8zYFTCg==", + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz", + "integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==", "requires": { "regenerator-runtime": "^0.12.0" } @@ -8147,13 +8187,22 @@ "gud": "^1.0.0" } }, - "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" } }, "regenerator-runtime": { @@ -8167,9 +8216,9 @@ "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" }, "warning": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", - "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", "requires": { "loose-envify": "^1.0.0" } @@ -8209,27 +8258,6 @@ "prop-types": "^15.5.10" } }, - "react-router": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-3.2.0.tgz", - "integrity": "sha512-sXlLOg0TRCqnjCVskqBHGjzNjcJKUqXEKnDSuxMYJSPJNq9hROE9VsiIW2kfIq7Ev+20Iz0nxayekXyv0XNmsg==", - "requires": { - "create-react-class": "^15.5.1", - "history": "^3.0.0", - "hoist-non-react-statics": "^1.2.0", - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "prop-types": "^15.5.6", - "warning": "^3.0.0" - }, - "dependencies": { - "hoist-non-react-statics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz", - "integrity": "sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs=" - } - } - }, "react-router-bootstrap": { "version": "0.24.4", "resolved": "https://registry.npmjs.org/react-router-bootstrap/-/react-router-bootstrap-0.24.4.tgz", @@ -9576,9 +9604,9 @@ "optional": true }, "uncontrollable": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.0.0.tgz", - "integrity": "sha512-gmy2ESW40LGbijSbW5piBGiPv55IgyDbjQcMr7LkDR5icpw/06UgMqULAGDBAcFn2a9d/SRPgcb3oo8hdEUfIw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-6.1.0.tgz", + "integrity": "sha512-2TzEm0pLKauMBZfAZXsgQvLpZHEp95891frCZdGDrSG7dWYaIQhedwLAzi0X8pR8KHNqlmuYEb2cEgbQzr050A==", "requires": { "invariant": "^2.2.4" }, diff --git a/package.json b/package.json index 7a34e65e9..db7b08586 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "react": "^16.2.0", "react-addons-pure-render-mixin": "^15.4.1", "react-apollo": "^2.4.1", - "react-bootstrap": "^1.0.0-beta.3", + "react-bootstrap": "^1.0.0-beta.5", "react-bootstrap-datetimepicker": "0.0.22", "react-cookie": "^2.1.4", "react-datetime": "^2.11.1", diff --git a/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx b/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx index 2ce19f254..4667b8150 100644 --- a/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx +++ b/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { Components, replaceComponent } from 'meteor/vulcan:core'; -import CardActions from '@material-ui/core/CardActions' -import withStyles from '@material-ui/core/styles/withStyles' -import classNames from 'classnames' +import CardActions from '@material-ui/core/CardActions'; +import withStyles from '@material-ui/core/styles/withStyles'; +import classNames from 'classnames'; const styles = theme => ({ root: { diff --git a/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx b/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx index 0340f2536..6cbf40aa2 100644 --- a/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx +++ b/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx @@ -31,8 +31,8 @@ class ScrollTrigger extends Component { supportsPassive = true; } }); - window.addEventListener("testPassive", null, opts); - window.removeEventListener("testPassive", null, opts); + window.addEventListener('testPassive', null, opts); + window.removeEventListener('testPassive', null, opts); //eslint-disable-next-line no-empty } catch (e) {} return supportsPassive; diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx b/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx index 0f5775c4f..f8cec0cb6 100644 --- a/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx +++ b/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx @@ -77,7 +77,7 @@ const MuiInput = createReactClass({ const { type, value } = this.props; if (type === 'url' && !!value && value !== fixUrl(value)) { - this.changeValue(fixUrl(value)) + this.changeValue(fixUrl(value)); } }, diff --git a/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx b/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx index 21c308d9e..5ce0c84d0 100644 --- a/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx +++ b/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx @@ -36,7 +36,7 @@ class DateTimeRdt extends PureComponent { value={date} // newDate argument is a Moment object given by react-datetime onChange={newDate => this.updateDate(newDate._d)} - format={"x"} + format={'x'} inputProps={{name: this.props.name}} />
    diff --git a/packages/vulcan-ui-material/components/upload/UploadInner.jsx b/packages/vulcan-ui-material/components/upload/UploadInner.jsx index b3c4ab007..1392ec066 100755 --- a/packages/vulcan-ui-material/components/upload/UploadInner.jsx +++ b/packages/vulcan-ui-material/components/upload/UploadInner.jsx @@ -124,7 +124,7 @@ const UploadInner = (props) => { {uploading && (
    - +
    )} diff --git a/packages/vulcan-ui-material/example/SideNavigation.jsx b/packages/vulcan-ui-material/example/SideNavigation.jsx index 9f3cf0edb..745821389 100644 --- a/packages/vulcan-ui-material/example/SideNavigation.jsx +++ b/packages/vulcan-ui-material/example/SideNavigation.jsx @@ -68,14 +68,14 @@ class SideNavigation extends React.Component { {browserHistory.push('/admin');}}> + onClick={() => {history.push('/admin');}}> {browserHistory.push('/theme');}}> + onClick={() => {history.push('/theme');}}> diff --git a/packages/vulcan-ui-material/modules/themes.js b/packages/vulcan-ui-material/modules/themes.js index eb9b57744..d829dc36d 100644 --- a/packages/vulcan-ui-material/modules/themes.js +++ b/packages/vulcan-ui-material/modules/themes.js @@ -37,7 +37,7 @@ export const registerTheme = (name, theme) => { export const getTheme = (name) => { const themeInfo = ThemesTable[name]; if (!themeInfo) return null; - themeInfo.theme.typography = { ...themeInfo.theme.typography, useNextVariants: true } + themeInfo.theme.typography = { ...themeInfo.theme.typography, useNextVariants: true }; return createMuiTheme(themeInfo.theme); }; @@ -53,7 +53,7 @@ export const getRawTheme = (name) => { const themeInfo = ThemesTable[name]; if (!themeInfo) return null; return themeInfo.theme; -} +}; /** * Get the theme specified in the 'muiTheme' setting From dfc4b4515991e2507989d01572e97de82d961174 Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 14:21:17 +0100 Subject: [PATCH 08/35] respect usual structure --- packages/vulcan-ui-material/{ => lib}/client/main.js | 1 - .../vulcan-ui-material/{ => lib}/client/wrapWithMuiTheme.jsx | 0 .../{ => lib}/components/accounts/AccountsButton.jsx | 0 .../{ => lib}/components/accounts/AccountsButtons.jsx | 0 .../{ => lib}/components/accounts/AccountsField.jsx | 0 .../{ => lib}/components/accounts/AccountsFields.jsx | 0 .../{ => lib}/components/accounts/AccountsForm.jsx | 0 .../components/accounts/AccountsPasswordOrService.jsx | 0 .../{ => lib}/components/accounts/AccountsSocialButtons.jsx | 0 .../{ => lib}/components/bonus/LoadMore.jsx | 0 .../{ => lib}/components/bonus/ScrollTrigger.jsx | 0 .../{ => lib}/components/bonus/SearchInput.jsx | 0 .../{ => lib}/components/bonus/TooltipIconButton.jsx | 0 .../{ => lib}/components/bonus/TooltipIntl.jsx | 0 .../vulcan-ui-material/{ => lib}/components/core/Card.jsx | 0 .../{ => lib}/components/core/Datatable.jsx | 0 .../{ => lib}/components/core/EditButton.jsx | 0 .../vulcan-ui-material/{ => lib}/components/core/Loading.jsx | 0 .../{ => lib}/components/core/ModalTrigger.jsx | 0 .../{ => lib}/components/core/NewButton.jsx | 0 .../{ => lib}/components/forms/FormComponentInner.jsx | 0 .../{ => lib}/components/forms/FormErrors.jsx | 0 .../{ => lib}/components/forms/FormGroupNone.jsx | 0 .../{ => lib}/components/forms/FormGroupWithLine.jsx | 0 .../{ => lib}/components/forms/FormNested.jsx | 0 .../{ => lib}/components/forms/FormNestedArrayLayout.jsx | 0 .../{ => lib}/components/forms/FormNestedDivider.jsx | 0 .../{ => lib}/components/forms/FormNestedFoot.jsx | 0 .../{ => lib}/components/forms/FormNestedHead.jsx | 0 .../{ => lib}/components/forms/FormSubmit.jsx | 0 .../{ => lib}/components/forms/base-controls/EndAdornment.jsx | 0 .../components/forms/base-controls/MuiCheckboxGroup.jsx | 0 .../components/forms/base-controls/MuiFormControl.jsx | 0 .../components/forms/base-controls/MuiFormHelper.jsx | 0 .../{ => lib}/components/forms/base-controls/MuiInput.jsx | 0 .../components/forms/base-controls/MuiRadioGroup.jsx | 0 .../{ => lib}/components/forms/base-controls/MuiSelect.jsx | 0 .../{ => lib}/components/forms/base-controls/MuiSuggest.jsx | 0 .../{ => lib}/components/forms/base-controls/MuiSwitch.jsx | 0 .../components/forms/base-controls/StartAdornment.jsx | 0 .../components/forms/base-controls/mixins/component.jsx | 0 .../{ => lib}/components/forms/controls/Checkbox.jsx | 0 .../{ => lib}/components/forms/controls/CheckboxGroup.jsx | 0 .../{ => lib}/components/forms/controls/CountrySelect.jsx | 0 .../{ => lib}/components/forms/controls/Date.jsx | 0 .../{ => lib}/components/forms/controls/DateRdt.jsx | 0 .../{ => lib}/components/forms/controls/DateTime.jsx | 0 .../{ => lib}/components/forms/controls/DateTimeRdt.jsx | 0 .../{ => lib}/components/forms/controls/Default.jsx | 0 .../{ => lib}/components/forms/controls/Email.jsx | 0 .../{ => lib}/components/forms/controls/Number.jsx | 0 .../{ => lib}/components/forms/controls/PostalCode.jsx | 0 .../{ => lib}/components/forms/controls/RadioGroup.jsx | 0 .../{ => lib}/components/forms/controls/RegionSelect.jsx | 0 .../{ => lib}/components/forms/controls/Select.jsx | 0 .../{ => lib}/components/forms/controls/SelectMultiple.jsx | 0 .../{ => lib}/components/forms/controls/Textarea.jsx | 0 .../{ => lib}/components/forms/controls/Time.jsx | 0 .../{ => lib}/components/forms/controls/TimeRdt.jsx | 0 .../{ => lib}/components/forms/controls/Url.jsx | 0 .../{ => lib}/components/forms/controls/countries.js | 0 packages/vulcan-ui-material/{ => lib}/components/index.js | 0 .../{ => lib}/components/theme/JssCleanup.jsx | 0 .../{ => lib}/components/theme/ThemeStyles.jsx | 0 packages/vulcan-ui-material/{ => lib}/components/ui/Alert.jsx | 0 .../vulcan-ui-material/{ => lib}/components/ui/Button.jsx | 0 .../{ => lib}/components/upload/UploadImage.jsx | 0 .../{ => lib}/components/upload/UploadInner.jsx | 0 packages/vulcan-ui-material/{ => lib}/example/Header.jsx | 0 packages/vulcan-ui-material/{ => lib}/example/Layout.jsx | 0 .../vulcan-ui-material/{ => lib}/example/SideNavigation.jsx | 0 packages/vulcan-ui-material/lib/modules/components.js | 1 + packages/vulcan-ui-material/{ => lib}/modules/index.js | 1 + packages/vulcan-ui-material/{ => lib}/modules/routes.js | 0 packages/vulcan-ui-material/{ => lib}/modules/sampleTheme.js | 0 packages/vulcan-ui-material/{ => lib}/modules/themes.js | 0 packages/vulcan-ui-material/{ => lib}/server/main.js | 1 - .../vulcan-ui-material/{ => lib}/server/wrapWithMuiTheme.jsx | 0 packages/vulcan-ui-material/package.js | 4 ++-- 79 files changed, 4 insertions(+), 4 deletions(-) rename packages/vulcan-ui-material/{ => lib}/client/main.js (63%) rename packages/vulcan-ui-material/{ => lib}/client/wrapWithMuiTheme.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsButton.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsButtons.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsField.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsFields.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsForm.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsPasswordOrService.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/accounts/AccountsSocialButtons.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/bonus/LoadMore.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/bonus/ScrollTrigger.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/bonus/SearchInput.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/bonus/TooltipIconButton.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/bonus/TooltipIntl.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/core/Card.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/core/Datatable.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/core/EditButton.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/core/Loading.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/core/ModalTrigger.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/core/NewButton.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormComponentInner.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormErrors.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormGroupNone.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormGroupWithLine.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormNested.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormNestedArrayLayout.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormNestedDivider.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormNestedFoot.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormNestedHead.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/FormSubmit.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/EndAdornment.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiCheckboxGroup.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiFormControl.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiFormHelper.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiInput.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiRadioGroup.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiSelect.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiSuggest.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/MuiSwitch.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/StartAdornment.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/base-controls/mixins/component.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Checkbox.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/CheckboxGroup.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/CountrySelect.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Date.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/DateRdt.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/DateTime.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/DateTimeRdt.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Default.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Email.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Number.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/PostalCode.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/RadioGroup.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/RegionSelect.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Select.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/SelectMultiple.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Textarea.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Time.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/TimeRdt.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/Url.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/forms/controls/countries.js (100%) rename packages/vulcan-ui-material/{ => lib}/components/index.js (100%) rename packages/vulcan-ui-material/{ => lib}/components/theme/JssCleanup.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/theme/ThemeStyles.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/ui/Alert.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/ui/Button.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/upload/UploadImage.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/components/upload/UploadInner.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/example/Header.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/example/Layout.jsx (100%) rename packages/vulcan-ui-material/{ => lib}/example/SideNavigation.jsx (100%) create mode 100644 packages/vulcan-ui-material/lib/modules/components.js rename packages/vulcan-ui-material/{ => lib}/modules/index.js (80%) rename packages/vulcan-ui-material/{ => lib}/modules/routes.js (100%) rename packages/vulcan-ui-material/{ => lib}/modules/sampleTheme.js (100%) rename packages/vulcan-ui-material/{ => lib}/modules/themes.js (100%) rename packages/vulcan-ui-material/{ => lib}/server/main.js (63%) rename packages/vulcan-ui-material/{ => lib}/server/wrapWithMuiTheme.jsx (100%) diff --git a/packages/vulcan-ui-material/client/main.js b/packages/vulcan-ui-material/lib/client/main.js similarity index 63% rename from packages/vulcan-ui-material/client/main.js rename to packages/vulcan-ui-material/lib/client/main.js index 1ed6f2fb0..dadfe15cf 100644 --- a/packages/vulcan-ui-material/client/main.js +++ b/packages/vulcan-ui-material/lib/client/main.js @@ -1,3 +1,2 @@ -export * from '../components/index'; export * from '../modules/index'; import './wrapWithMuiTheme'; diff --git a/packages/vulcan-ui-material/client/wrapWithMuiTheme.jsx b/packages/vulcan-ui-material/lib/client/wrapWithMuiTheme.jsx similarity index 100% rename from packages/vulcan-ui-material/client/wrapWithMuiTheme.jsx rename to packages/vulcan-ui-material/lib/client/wrapWithMuiTheme.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsButton.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsButton.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsButton.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsButton.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsButtons.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsButtons.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsButtons.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsButtons.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsField.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsField.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsField.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsField.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsFields.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsFields.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsFields.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsFields.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsForm.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsForm.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsForm.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsForm.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsPasswordOrService.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsPasswordOrService.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsPasswordOrService.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsPasswordOrService.jsx diff --git a/packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx b/packages/vulcan-ui-material/lib/components/accounts/AccountsSocialButtons.jsx similarity index 100% rename from packages/vulcan-ui-material/components/accounts/AccountsSocialButtons.jsx rename to packages/vulcan-ui-material/lib/components/accounts/AccountsSocialButtons.jsx diff --git a/packages/vulcan-ui-material/components/bonus/LoadMore.jsx b/packages/vulcan-ui-material/lib/components/bonus/LoadMore.jsx similarity index 100% rename from packages/vulcan-ui-material/components/bonus/LoadMore.jsx rename to packages/vulcan-ui-material/lib/components/bonus/LoadMore.jsx diff --git a/packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx b/packages/vulcan-ui-material/lib/components/bonus/ScrollTrigger.jsx similarity index 100% rename from packages/vulcan-ui-material/components/bonus/ScrollTrigger.jsx rename to packages/vulcan-ui-material/lib/components/bonus/ScrollTrigger.jsx diff --git a/packages/vulcan-ui-material/components/bonus/SearchInput.jsx b/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx similarity index 100% rename from packages/vulcan-ui-material/components/bonus/SearchInput.jsx rename to packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx diff --git a/packages/vulcan-ui-material/components/bonus/TooltipIconButton.jsx b/packages/vulcan-ui-material/lib/components/bonus/TooltipIconButton.jsx similarity index 100% rename from packages/vulcan-ui-material/components/bonus/TooltipIconButton.jsx rename to packages/vulcan-ui-material/lib/components/bonus/TooltipIconButton.jsx diff --git a/packages/vulcan-ui-material/components/bonus/TooltipIntl.jsx b/packages/vulcan-ui-material/lib/components/bonus/TooltipIntl.jsx similarity index 100% rename from packages/vulcan-ui-material/components/bonus/TooltipIntl.jsx rename to packages/vulcan-ui-material/lib/components/bonus/TooltipIntl.jsx diff --git a/packages/vulcan-ui-material/components/core/Card.jsx b/packages/vulcan-ui-material/lib/components/core/Card.jsx similarity index 100% rename from packages/vulcan-ui-material/components/core/Card.jsx rename to packages/vulcan-ui-material/lib/components/core/Card.jsx diff --git a/packages/vulcan-ui-material/components/core/Datatable.jsx b/packages/vulcan-ui-material/lib/components/core/Datatable.jsx similarity index 100% rename from packages/vulcan-ui-material/components/core/Datatable.jsx rename to packages/vulcan-ui-material/lib/components/core/Datatable.jsx diff --git a/packages/vulcan-ui-material/components/core/EditButton.jsx b/packages/vulcan-ui-material/lib/components/core/EditButton.jsx similarity index 100% rename from packages/vulcan-ui-material/components/core/EditButton.jsx rename to packages/vulcan-ui-material/lib/components/core/EditButton.jsx diff --git a/packages/vulcan-ui-material/components/core/Loading.jsx b/packages/vulcan-ui-material/lib/components/core/Loading.jsx similarity index 100% rename from packages/vulcan-ui-material/components/core/Loading.jsx rename to packages/vulcan-ui-material/lib/components/core/Loading.jsx diff --git a/packages/vulcan-ui-material/components/core/ModalTrigger.jsx b/packages/vulcan-ui-material/lib/components/core/ModalTrigger.jsx similarity index 100% rename from packages/vulcan-ui-material/components/core/ModalTrigger.jsx rename to packages/vulcan-ui-material/lib/components/core/ModalTrigger.jsx diff --git a/packages/vulcan-ui-material/components/core/NewButton.jsx b/packages/vulcan-ui-material/lib/components/core/NewButton.jsx similarity index 100% rename from packages/vulcan-ui-material/components/core/NewButton.jsx rename to packages/vulcan-ui-material/lib/components/core/NewButton.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormComponentInner.jsx b/packages/vulcan-ui-material/lib/components/forms/FormComponentInner.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormComponentInner.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormComponentInner.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormErrors.jsx b/packages/vulcan-ui-material/lib/components/forms/FormErrors.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormErrors.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormErrors.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormGroupNone.jsx b/packages/vulcan-ui-material/lib/components/forms/FormGroupNone.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormGroupNone.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormGroupNone.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormGroupWithLine.jsx b/packages/vulcan-ui-material/lib/components/forms/FormGroupWithLine.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormGroupWithLine.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormGroupWithLine.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormNested.jsx b/packages/vulcan-ui-material/lib/components/forms/FormNested.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormNested.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormNested.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormNestedArrayLayout.jsx b/packages/vulcan-ui-material/lib/components/forms/FormNestedArrayLayout.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormNestedArrayLayout.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormNestedArrayLayout.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx b/packages/vulcan-ui-material/lib/components/forms/FormNestedDivider.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormNestedDivider.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormNestedDivider.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx b/packages/vulcan-ui-material/lib/components/forms/FormNestedFoot.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormNestedFoot.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormNestedFoot.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormNestedHead.jsx b/packages/vulcan-ui-material/lib/components/forms/FormNestedHead.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormNestedHead.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormNestedHead.jsx diff --git a/packages/vulcan-ui-material/components/forms/FormSubmit.jsx b/packages/vulcan-ui-material/lib/components/forms/FormSubmit.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/FormSubmit.jsx rename to packages/vulcan-ui-material/lib/components/forms/FormSubmit.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/EndAdornment.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/EndAdornment.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/EndAdornment.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/EndAdornment.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiCheckboxGroup.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiCheckboxGroup.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiCheckboxGroup.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiCheckboxGroup.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiFormControl.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiFormControl.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiFormControl.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiFormControl.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiFormHelper.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiFormHelper.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiFormHelper.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiFormHelper.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiInput.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiInput.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiInput.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiRadioGroup.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiRadioGroup.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiRadioGroup.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiSelect.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSelect.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiSelect.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSelect.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiSuggest.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSuggest.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiSuggest.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSuggest.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/MuiSwitch.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/MuiSwitch.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/StartAdornment.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/StartAdornment.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/StartAdornment.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/StartAdornment.jsx diff --git a/packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/mixins/component.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/base-controls/mixins/component.jsx rename to packages/vulcan-ui-material/lib/components/forms/base-controls/mixins/component.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Checkbox.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Checkbox.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Checkbox.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Checkbox.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/CheckboxGroup.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/CheckboxGroup.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/CheckboxGroup.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/CheckboxGroup.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/CountrySelect.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/CountrySelect.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/CountrySelect.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/CountrySelect.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Date.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Date.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Date.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Date.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/DateRdt.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/DateRdt.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/DateRdt.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/DateRdt.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/DateTime.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/DateTime.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/DateTime.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/DateTime.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/DateTimeRdt.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/DateTimeRdt.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/DateTimeRdt.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Default.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Default.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Default.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Default.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Email.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Email.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Email.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Email.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Number.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Number.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Number.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Number.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/PostalCode.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/PostalCode.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/PostalCode.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/PostalCode.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/RadioGroup.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/RadioGroup.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/RadioGroup.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/RadioGroup.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/RegionSelect.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/RegionSelect.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/RegionSelect.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/RegionSelect.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Select.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Select.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Select.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Select.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/SelectMultiple.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/SelectMultiple.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/SelectMultiple.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/SelectMultiple.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Textarea.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Textarea.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Textarea.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Textarea.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Time.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Time.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Time.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Time.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/TimeRdt.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/TimeRdt.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/TimeRdt.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/TimeRdt.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/Url.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Url.jsx similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/Url.jsx rename to packages/vulcan-ui-material/lib/components/forms/controls/Url.jsx diff --git a/packages/vulcan-ui-material/components/forms/controls/countries.js b/packages/vulcan-ui-material/lib/components/forms/controls/countries.js similarity index 100% rename from packages/vulcan-ui-material/components/forms/controls/countries.js rename to packages/vulcan-ui-material/lib/components/forms/controls/countries.js diff --git a/packages/vulcan-ui-material/components/index.js b/packages/vulcan-ui-material/lib/components/index.js similarity index 100% rename from packages/vulcan-ui-material/components/index.js rename to packages/vulcan-ui-material/lib/components/index.js diff --git a/packages/vulcan-ui-material/components/theme/JssCleanup.jsx b/packages/vulcan-ui-material/lib/components/theme/JssCleanup.jsx similarity index 100% rename from packages/vulcan-ui-material/components/theme/JssCleanup.jsx rename to packages/vulcan-ui-material/lib/components/theme/JssCleanup.jsx diff --git a/packages/vulcan-ui-material/components/theme/ThemeStyles.jsx b/packages/vulcan-ui-material/lib/components/theme/ThemeStyles.jsx similarity index 100% rename from packages/vulcan-ui-material/components/theme/ThemeStyles.jsx rename to packages/vulcan-ui-material/lib/components/theme/ThemeStyles.jsx diff --git a/packages/vulcan-ui-material/components/ui/Alert.jsx b/packages/vulcan-ui-material/lib/components/ui/Alert.jsx similarity index 100% rename from packages/vulcan-ui-material/components/ui/Alert.jsx rename to packages/vulcan-ui-material/lib/components/ui/Alert.jsx diff --git a/packages/vulcan-ui-material/components/ui/Button.jsx b/packages/vulcan-ui-material/lib/components/ui/Button.jsx similarity index 100% rename from packages/vulcan-ui-material/components/ui/Button.jsx rename to packages/vulcan-ui-material/lib/components/ui/Button.jsx diff --git a/packages/vulcan-ui-material/components/upload/UploadImage.jsx b/packages/vulcan-ui-material/lib/components/upload/UploadImage.jsx similarity index 100% rename from packages/vulcan-ui-material/components/upload/UploadImage.jsx rename to packages/vulcan-ui-material/lib/components/upload/UploadImage.jsx diff --git a/packages/vulcan-ui-material/components/upload/UploadInner.jsx b/packages/vulcan-ui-material/lib/components/upload/UploadInner.jsx similarity index 100% rename from packages/vulcan-ui-material/components/upload/UploadInner.jsx rename to packages/vulcan-ui-material/lib/components/upload/UploadInner.jsx diff --git a/packages/vulcan-ui-material/example/Header.jsx b/packages/vulcan-ui-material/lib/example/Header.jsx similarity index 100% rename from packages/vulcan-ui-material/example/Header.jsx rename to packages/vulcan-ui-material/lib/example/Header.jsx diff --git a/packages/vulcan-ui-material/example/Layout.jsx b/packages/vulcan-ui-material/lib/example/Layout.jsx similarity index 100% rename from packages/vulcan-ui-material/example/Layout.jsx rename to packages/vulcan-ui-material/lib/example/Layout.jsx diff --git a/packages/vulcan-ui-material/example/SideNavigation.jsx b/packages/vulcan-ui-material/lib/example/SideNavigation.jsx similarity index 100% rename from packages/vulcan-ui-material/example/SideNavigation.jsx rename to packages/vulcan-ui-material/lib/example/SideNavigation.jsx diff --git a/packages/vulcan-ui-material/lib/modules/components.js b/packages/vulcan-ui-material/lib/modules/components.js new file mode 100644 index 000000000..4aba840ab --- /dev/null +++ b/packages/vulcan-ui-material/lib/modules/components.js @@ -0,0 +1 @@ +export * from '../components'; \ No newline at end of file diff --git a/packages/vulcan-ui-material/modules/index.js b/packages/vulcan-ui-material/lib/modules/index.js similarity index 80% rename from packages/vulcan-ui-material/modules/index.js rename to packages/vulcan-ui-material/lib/modules/index.js index 5a65d1ab1..e614e7b6b 100644 --- a/packages/vulcan-ui-material/modules/index.js +++ b/packages/vulcan-ui-material/lib/modules/index.js @@ -1,3 +1,4 @@ +export * from './components'; export * from './themes'; export JssCleanup from '../components/theme/JssCleanup'; import './sampleTheme'; diff --git a/packages/vulcan-ui-material/modules/routes.js b/packages/vulcan-ui-material/lib/modules/routes.js similarity index 100% rename from packages/vulcan-ui-material/modules/routes.js rename to packages/vulcan-ui-material/lib/modules/routes.js diff --git a/packages/vulcan-ui-material/modules/sampleTheme.js b/packages/vulcan-ui-material/lib/modules/sampleTheme.js similarity index 100% rename from packages/vulcan-ui-material/modules/sampleTheme.js rename to packages/vulcan-ui-material/lib/modules/sampleTheme.js diff --git a/packages/vulcan-ui-material/modules/themes.js b/packages/vulcan-ui-material/lib/modules/themes.js similarity index 100% rename from packages/vulcan-ui-material/modules/themes.js rename to packages/vulcan-ui-material/lib/modules/themes.js diff --git a/packages/vulcan-ui-material/server/main.js b/packages/vulcan-ui-material/lib/server/main.js similarity index 63% rename from packages/vulcan-ui-material/server/main.js rename to packages/vulcan-ui-material/lib/server/main.js index 1ed6f2fb0..dadfe15cf 100644 --- a/packages/vulcan-ui-material/server/main.js +++ b/packages/vulcan-ui-material/lib/server/main.js @@ -1,3 +1,2 @@ -export * from '../components/index'; export * from '../modules/index'; import './wrapWithMuiTheme'; diff --git a/packages/vulcan-ui-material/server/wrapWithMuiTheme.jsx b/packages/vulcan-ui-material/lib/server/wrapWithMuiTheme.jsx similarity index 100% rename from packages/vulcan-ui-material/server/wrapWithMuiTheme.jsx rename to packages/vulcan-ui-material/lib/server/wrapWithMuiTheme.jsx diff --git a/packages/vulcan-ui-material/package.js b/packages/vulcan-ui-material/package.js index 0e5d542b1..4759b53f1 100644 --- a/packages/vulcan-ui-material/package.js +++ b/packages/vulcan-ui-material/package.js @@ -20,6 +20,6 @@ Package.onUse(function (api) { 'forms.css', ], ['client', 'server']); - api.mainModule('client/main.js', 'client'); - api.mainModule('server/main.js', 'server'); + api.mainModule('lib/client/main.js', 'client'); + api.mainModule('lib/server/main.js', 'server'); }); \ No newline at end of file From ca34d828b902309594c6db0be411e52e1c3b991d Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 15:14:09 +0100 Subject: [PATCH 09/35] provide a default FormElement --- packages/vulcan-forms/lib/components/FormElement.jsx | 8 ++++++++ packages/vulcan-forms/lib/modules/components.js | 1 + .../lib/components/forms/FormElement.jsx | 4 ++-- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 packages/vulcan-forms/lib/components/FormElement.jsx diff --git a/packages/vulcan-forms/lib/components/FormElement.jsx b/packages/vulcan-forms/lib/components/FormElement.jsx new file mode 100644 index 000000000..3fccb89b4 --- /dev/null +++ b/packages/vulcan-forms/lib/components/FormElement.jsx @@ -0,0 +1,8 @@ +import React from 'react'; +import { registerComponent } from 'meteor/vulcan:core'; + +const FormElement = ({children}) =>
    {children}
    ; +registerComponent({ + name:'FormElement', + component: FormElement +}); \ No newline at end of file diff --git a/packages/vulcan-forms/lib/modules/components.js b/packages/vulcan-forms/lib/modules/components.js index f820acd89..9d6c649e7 100644 --- a/packages/vulcan-forms/lib/modules/components.js +++ b/packages/vulcan-forms/lib/modules/components.js @@ -1,4 +1,5 @@ import '../components/FieldErrors.jsx'; +import '../components/FormElement.jsx'; import '../components/FormErrors.jsx'; import '../components/FormError.jsx'; import '../components/FormComponent.jsx'; diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx index 73eaf4527..c88b238d1 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx @@ -1,5 +1,5 @@ // import React from 'react'; import Form from 'react-bootstrap/Form'; -import { registerComponent } from 'meteor/vulcan:core'; +import { replaceComponent } from 'meteor/vulcan:core'; -registerComponent('FormElement', Form); +replaceComponent('FormElement', Form); From 8d691ae30221fae399f66a4d0386c437ae3d11f1 Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 15:15:53 +0100 Subject: [PATCH 10/35] changed display name into a warning --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index d4af31278..40d668c83 100644 --- a/.eslintrc +++ b/.eslintrc @@ -54,7 +54,7 @@ "single", "avoid-escape" ], - "react/display-name": 2, + "react/display-name": 1, "react/prop-types": 0, "semi": [2, "always"] }, From 7cfaac24501c07f1b138dcc9ace5dbc11ef5c0e5 Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 15:16:24 +0100 Subject: [PATCH 11/35] changed missing semi into a warning --- .eslintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 40d668c83..8254b5ac7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -56,7 +56,7 @@ ], "react/display-name": 1, "react/prop-types": 0, - "semi": [2, "always"] + "semi": [1, "always"] }, "env": { "browser": true, From 339215e6f31f907772b3b98705dbb6a9291b4d74 Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 15:29:58 +0100 Subject: [PATCH 12/35] formElement must be a class component to accept a ref --- packages/vulcan-forms/lib/components/FormElement.jsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/vulcan-forms/lib/components/FormElement.jsx b/packages/vulcan-forms/lib/components/FormElement.jsx index 3fccb89b4..fc0a619df 100644 --- a/packages/vulcan-forms/lib/components/FormElement.jsx +++ b/packages/vulcan-forms/lib/components/FormElement.jsx @@ -1,7 +1,13 @@ import React from 'react'; import { registerComponent } from 'meteor/vulcan:core'; -const FormElement = ({children}) =>
    {children}
    ; +// this component receives a ref, so it must be a class component +class FormElement extends React.Component { + render(){ + const { children, ...otherProps } = this.props; + return
    {children}
    ; + } +} registerComponent({ name:'FormElement', component: FormElement From c7965e3b0ffc7ccb94e9ea23d7c43d8eaee5630c Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 15:55:02 +0100 Subject: [PATCH 13/35] updated warnUnsavedChanges --- packages/vulcan-forms/lib/components/Form.jsx | 143 +++++++++++------- 1 file changed, 88 insertions(+), 55 deletions(-) diff --git a/packages/vulcan-forms/lib/components/Form.jsx b/packages/vulcan-forms/lib/components/Form.jsx index 511342d4f..226e2d7b5 100644 --- a/packages/vulcan-forms/lib/components/Form.jsx +++ b/packages/vulcan-forms/lib/components/Form.jsx @@ -66,7 +66,7 @@ import { callbackProps } from './propTypes'; // props that should trigger a form reset const RESET_PROPS = [ - 'collection', 'collectionName', 'typeName', 'document', 'schema', 'currentUser', + 'collection', 'collectionName', 'typeName', 'document', 'schema', 'currentUser', 'fields', 'removeFields', 'prefilledProps' // TODO: prefilledProps should be merged instead? ]; @@ -104,17 +104,17 @@ const getInitialStateFromProps = nextProps => { nextProps.prefilledProps, nextProps.document ); - + //if minCount is specified, go ahead and create empty nested documents Object.keys(convertedSchema).forEach(key => { let minCount = convertedSchema[key].minCount; - if(minCount) { + if (minCount) { initialDocument[key] = initialDocument[key] || []; - while(initialDocument[key].length < minCount) + while (initialDocument[key].length < minCount) initialDocument[key].push({}); } }); - + // remove all instances of the `__typename` property from document Utils.removeProperty(initialDocument, '__typename'); @@ -154,7 +154,7 @@ class SmartForm extends Component { }; } - defaultValues = {}; + defaultValues = {}; submitFormCallbacks = []; successFormCallbacks = []; @@ -252,7 +252,7 @@ class SmartForm extends Component { }); // run data object through submitForm callbacks - data = runCallbacks({ callbacks: this.submitFormCallbacks, iterator: data, properties: { form: this }}); + data = runCallbacks({ callbacks: this.submitFormCallbacks, iterator: data, properties: { form: this } }); return data; }; @@ -720,8 +720,72 @@ class SmartForm extends Component { /* - Warn the user if there are unsaved changes + Install a route leave hook to warn the user if there are unsaved changes + */ + componentDidMount = () => { + this.checkRouteChange(); + this.checkBrowserClosing(); + } + + /* + Remove the closing browser check on component unmount + see https://gist.github.com/mknabe/bfcb6db12ef52323954a28655801792d + */ + componentWillUnmount = () => { + if (this.getWarnUnsavedChanges()) { + // unblock route change + if (this.unblock) { + this.unblock(); + } + // unblock browser change + window.onbeforeunload = undefined; //undefined instead of null to support IE + } + }; + + + // -------------------- Check on form leaving ----- // + + /** + * Check if we must warn user on unsaved change + */ + getWarnUnsavedChanges = () => { + let warnUnsavedChanges = getSetting('forms.warnUnsavedChanges'); + if (typeof this.props.warnUnsavedChanges === 'boolean') { + warnUnsavedChanges = this.props.warnUnsavedChanges; + } + return warnUnsavedChanges; + } + + // check for route change, prevent form content loss + checkRouteChange = () => { + // @see https://github.com/ReactTraining/react-router/issues/4635#issuecomment-297828995 + // @see https://github.com/ReactTraining/history#blocking-transitions + if (this.getWarnUnsavedChanges()) { + this.unblock = this.props.history.block((location, action) => { + // return the message that will pop into a window.confirm alert + // if returns nothing, the message won't appear and the user won't be blocked + return this.handleRouteLeave(); + + /* + // React-router 3 implementtion + const routes = this.props.router.routes; + const currentRoute = routes[routes.length - 1]; + this.props.router.setRouteLeaveHook(currentRoute, this.handleRouteLeave); + + */ + }); + } + } + // check for browser closing + checkBrowserClosing = () => { + //check for closing the browser with unsaved changes too + window.onbeforeunload = this.handlePageLeave; + } + + /* + Check if the user has unsaved changes, returns a message if yes + and nothing if not */ handleRouteLeave = () => { if (this.isChanged()) { @@ -733,9 +797,13 @@ class SmartForm extends Component { } }; - //see https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload - //the message returned is actually ignored by most browsers and a default message 'Are you sure you want to leave this page? You might have unsaved changes' is displayed. See the Notes section on the mozilla docs above - handlePageLeave = event => { + /** + * Same for browser closing + * + * see https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload + * the message returned is actually ignored by most browsers and a default message 'Are you sure you want to leave this page? You might have unsaved changes' is displayed. See the Notes section on the mozilla docs above + */ + handlePageLeave = (event) => { if (this.isChanged()) { const message = this.context.intl.formatMessage({ id: 'forms.confirm_discard', @@ -748,41 +816,6 @@ class SmartForm extends Component { return message; } }; - - /* - - Install a route leave hook to warn the user if there are unsaved changes - - */ - componentDidMount = () => { - let warnUnsavedChanges = getSetting('forms.warnUnsavedChanges'); - if (typeof this.props.warnUnsavedChanges === 'boolean') { - warnUnsavedChanges = this.props.warnUnsavedChanges; - } - if (warnUnsavedChanges) { - const routes = this.props.router.routes; - const currentRoute = routes[routes.length - 1]; - this.props.router.setRouteLeaveHook(currentRoute, this.handleRouteLeave); - - //check for closing the browser with unsaved changes - window.onbeforeunload = this.handlePageLeave; - } - }; - - /* - Remove the closing browser check on component unmount - see https://gist.github.com/mknabe/bfcb6db12ef52323954a28655801792d - */ - componentWillUnmount = () => { - let warnUnsavedChanges = getSetting('forms.warnUnsavedChanges'); - if (typeof this.props.warnUnsavedChanges === 'boolean') { - warnUnsavedChanges = this.props.warnUnsavedChanges; - } - if (warnUnsavedChanges) { - window.onbeforeunload = undefined; //undefined instead of null to support IE - } - }; - /* Returns true if there are any differences between the initial document and the current one @@ -882,7 +915,7 @@ class SmartForm extends Component { } // run document through mutation success callbacks - document = runCallbacks({ callbacks: this.successFormCallbacks, iterator: document, properties: { form: this }}); + document = runCallbacks({ callbacks: this.successFormCallbacks, iterator: document, properties: { form: this } }); // run success callback if it exists if (this.props.successCallback) this.props.successCallback(document, { form: this }); @@ -898,7 +931,7 @@ class SmartForm extends Component { console.log(error); // run mutation failure callbacks on error, we do not allow the callbacks to change the error - runCallbacks({ callbacks: this.failureFormCallbacks, iterator: error, properties: { error, form: this }}); + runCallbacks({ callbacks: this.failureFormCallbacks, iterator: error, properties: { error, form: this } }); if (!_.isEmpty(error)) { // add error to state @@ -920,7 +953,7 @@ class SmartForm extends Component { submitForm = event => { event && event.preventDefault(); - + // if form is disabled (there is already a submit handler running) don't do anything if (this.state.disabled) { return; @@ -986,7 +1019,7 @@ class SmartForm extends Component { } }; - + // --------------------------------------------------------------------- // // ------------------------- Props to Pass ----------------------------- // // --------------------------------------------------------------------- // @@ -1028,15 +1061,15 @@ class SmartForm extends Component { cancelCallback: this.props.cancelCallback, revertCallback: this.props.revertCallback, document: this.getDocument(), - deleteDocument: + deleteDocument: (this.getFormType() === 'edit' && this.props.showRemove && this.deleteDocument) || null, - collectionName:this.props.collectionName, - currentValues:this.state.currentValues, - deletedValues:this.state.deletedValues, - errors:this.state.errors, + collectionName: this.props.collectionName, + currentValues: this.state.currentValues, + deletedValues: this.state.deletedValues, + errors: this.state.errors, }); // --------------------------------------------------------------------- // From 2ebabd2d5c1591bbfcf783b43d8591c6b1667267 Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 15 Mar 2019 16:20:24 +0100 Subject: [PATCH 14/35] fix onChange API change (does not need the name anymore) --- .../lib/components/forms/base-controls/MuiInput.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiInput.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiInput.jsx index f8cec0cb6..e10e80da1 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiInput.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiInput.jsx @@ -70,7 +70,7 @@ const MuiInput = createReactClass({ }, changeValue: function (value) { - this.props.onChange(this.props.name, value); + this.props.onChange(value); }, handleBlur: function (event) { From 57904118614b00d36e577184107c997c688a880e Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sat, 16 Mar 2019 17:35:51 +0900 Subject: [PATCH 15/35] Add support for extraFields props for AccountsLoginForm --- .../imports/ui/components/LoginFormInner.jsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx b/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx index c32117135..eb114416c 100644 --- a/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx +++ b/packages/vulcan-accounts/imports/ui/components/LoginFormInner.jsx @@ -265,9 +265,20 @@ export class AccountsLoginFormInner extends TrackerComponent { } fields() { - const loginFields = []; + let loginFields = []; const { formState } = this.state; + // if extra fields have been specified, add onChange handler to them + if (this.props.extraFields) { + loginFields = this.props.extraFields.map(field => { + const { id } = field; + return { + ...field, + onChange: this.handleChange.bind(this, id), + } + }); + } + if (!hasPasswordService() && getLoginServices().length == 0) { loginFields.push({ label: 'No login service added, i.e. accounts-password', @@ -766,6 +777,13 @@ export class AccountsLoginFormInner extends TrackerComponent { onSubmitHook } = this.state; + // add extra fields to options + if (this.props.extraFields) { + this.props.extraFields.forEach(({ id })=> { + options[id] = this.state[id]; + }); + } + const self = this; let error = false; From 66fd1aedaf2ab6b4b1190c3c8ab2f1b00aeb43ed Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sat, 16 Mar 2019 17:45:32 +0900 Subject: [PATCH 16/35] Add explicit formProps prop to edit/new buttons --- .../vulcan-core/lib/modules/components/EditButton.jsx | 8 ++++---- packages/vulcan-core/lib/modules/components/NewButton.jsx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/vulcan-core/lib/modules/components/EditButton.jsx b/packages/vulcan-core/lib/modules/components/EditButton.jsx index 7f15bef7d..9e0d6e28a 100644 --- a/packages/vulcan-core/lib/modules/components/EditButton.jsx +++ b/packages/vulcan-core/lib/modules/components/EditButton.jsx @@ -2,7 +2,7 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -const EditButton = ({ style = 'primary', label, size, showId, modalProps, ...props }, { intl }) => ( +const EditButton = ({ style = 'primary', label, size, showId, modalProps, formProps, ...props }, { intl }) => ( - + ); @@ -29,7 +29,7 @@ registerComponent('EditButton', EditButton); EditForm Component */ -const EditForm = ({ closeModal, successCallback, removeSuccessCallback, ...props }) => { +const EditForm = ({ closeModal, successCallback, removeSuccessCallback, formProps, ...props }) => { const success = successCallback ? document => { @@ -46,7 +46,7 @@ const EditForm = ({ closeModal, successCallback, removeSuccessCallback, ...props : closeModal; return ( - + ); }; registerComponent('EditForm', EditForm); diff --git a/packages/vulcan-core/lib/modules/components/NewButton.jsx b/packages/vulcan-core/lib/modules/components/NewButton.jsx index 262eba4af..e966577d7 100644 --- a/packages/vulcan-core/lib/modules/components/NewButton.jsx +++ b/packages/vulcan-core/lib/modules/components/NewButton.jsx @@ -2,7 +2,7 @@ import { Components, registerComponent } from 'meteor/vulcan:lib'; import React from 'react'; import { FormattedMessage, intlShape } from 'meteor/vulcan:i18n'; -const NewButton = ({ collection, size, label, style = 'primary', ...props }, { intl }) => ( +const NewButton = ({ collection, size, label, style = 'primary', formProps, ...props }, { intl }) => ( } > - + ); @@ -28,7 +28,7 @@ registerComponent('NewButton', NewButton); NewForm Component */ -const NewForm = ({ closeModal, successCallback, ...props }) => { +const NewForm = ({ closeModal, successCallback, formProps, ...props }) => { const success = successCallback ? document => { @@ -37,6 +37,6 @@ const NewForm = ({ closeModal, successCallback, ...props }) => { } : closeModal; - return ; + return ; }; registerComponent('NewForm', NewForm); From 86f919e54a386ce4f0e97a423765867dbd172044 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sun, 17 Mar 2019 10:40:13 +0900 Subject: [PATCH 17/35] Make queryOne support any selector --- packages/vulcan-lib/lib/server/query.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-lib/lib/server/query.js b/packages/vulcan-lib/lib/server/query.js index 15f60eb0e..ae573769b 100644 --- a/packages/vulcan-lib/lib/server/query.js +++ b/packages/vulcan-lib/lib/server/query.js @@ -93,9 +93,10 @@ Meteor.startup(() => { Collections.forEach(collection => { const typeName = collection.options.typeName; - collection.queryOne = async (documentId, { fragmentName, fragmentText, context }) => { + collection.queryOne = async (documentIdOrSelector, { fragmentName, fragmentText, context }) => { + const selector = typeof documentIdOrSelector === 'string' ? { documentId: documentIdOrSelector } : documentIdOrSelector; const query = buildQuery(collection, { fragmentName, fragmentText }); - const result = await runQuery(query, { input: { selector: { documentId } } }, context); + const result = await runQuery(query, { input: { selector } }, context); return result.data[Utils.camelCaseify(typeName)].result; }; }); From 8eca92bb2ac2c8e758cea147e5d3edfab27a0bcb Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Sun, 17 Mar 2019 10:57:50 +0900 Subject: [PATCH 18/35] Export registerApolloServerOptions --- packages/vulcan-lib/lib/server/main.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vulcan-lib/lib/server/main.js b/packages/vulcan-lib/lib/server/main.js index 731dafd3f..181b49f18 100644 --- a/packages/vulcan-lib/lib/server/main.js +++ b/packages/vulcan-lib/lib/server/main.js @@ -18,4 +18,6 @@ export * from './intl.js'; export * from './accounts_helpers.js'; export * from './source_version.js'; +export * from './apollo-server/settings.js'; + import './apollo-server/startup'; From c23c6e0d4cef2e449c8420c8e1cf4a549955559e Mon Sep 17 00:00:00 2001 From: Harold Date: Mon, 18 Mar 2019 18:16:45 -0600 Subject: [PATCH 19/35] Fix onChange function on MuiSwitch component --- .../lib/components/forms/base-controls/MuiSwitch.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx index 52181f865..1ac58529a 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx @@ -24,7 +24,7 @@ const MuiSwitch = createReactClass({ const value = target.checked; //this.setValue(value); - this.props.onChange(this.props.name, value); + this.props.onChange(value); setTimeout(() => {document.activeElement.blur();}); }, From 348be320527a222e0be61f3a66d82bbe2c6238d4 Mon Sep 17 00:00:00 2001 From: Harold Date: Mon, 18 Mar 2019 18:19:04 -0600 Subject: [PATCH 20/35] Remove comments --- .../lib/components/forms/base-controls/MuiSwitch.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx index 1ac58529a..1d4c2a5f2 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSwitch.jsx @@ -23,7 +23,6 @@ const MuiSwitch = createReactClass({ const target = event.target; const value = target.checked; - //this.setValue(value); this.props.onChange(value); setTimeout(() => {document.activeElement.blur();}); From c3656688172441760a82287ece3df14fb57aa7f1 Mon Sep 17 00:00:00 2001 From: Harold Date: Mon, 18 Mar 2019 18:57:02 -0600 Subject: [PATCH 21/35] Fix onChange function on MuiCheckboxGroup component --- .../lib/components/forms/base-controls/MuiCheckboxGroup.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiCheckboxGroup.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiCheckboxGroup.jsx index 4d80d8492..19070819d 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiCheckboxGroup.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiCheckboxGroup.jsx @@ -82,8 +82,8 @@ const MuiCheckboxGroup = createReactClass({ value.push(option.value); } }.bind(this)); - //this.setValue(value); - this.props.onChange(this.props.name, value); + + this.props.onChange(value); }, validate: function () { From 3e033cdfbbcd98f1ea44a6b2ef36d29ce241878a Mon Sep 17 00:00:00 2001 From: Harold Date: Mon, 18 Mar 2019 19:11:23 -0600 Subject: [PATCH 22/35] Fix onChange function on MuiSelect component --- .../lib/components/forms/base-controls/MuiSelect.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSelect.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSelect.jsx index 6c50f1bbd..8bd7cdfcd 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSelect.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiSelect.jsx @@ -86,7 +86,7 @@ const MuiSelect = createReactClass({ }, changeValue: function (value) { - this.props.onChange(this.props.name, value); + this.props.onChange(value); }, render: function () { From 23eadbe89e59e73451f4f985a997912c1e4aacbf Mon Sep 17 00:00:00 2001 From: James Babcock Date: Thu, 21 Mar 2019 13:31:22 -0700 Subject: [PATCH 23/35] Fix denormalization when bio set to empty --- packages/vulcan-users/lib/server/callbacks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-users/lib/server/callbacks.js b/packages/vulcan-users/lib/server/callbacks.js index d1e719710..c044bef4f 100644 --- a/packages/vulcan-users/lib/server/callbacks.js +++ b/packages/vulcan-users/lib/server/callbacks.js @@ -39,7 +39,7 @@ addCallback('users.new.sync', usersMakeAdmin); function usersEditGenerateHtmlBio (modifier) { - if (modifier.$set && modifier.$set.bio) { + if (modifier.$set && 'bio' in modifier.$set) { modifier.$set.htmlBio = Utils.sanitize(marked(modifier.$set.bio)); } return modifier; From bf9b4fda1469908d76fe473b5d2e93972173c4f7 Mon Sep 17 00:00:00 2001 From: enzo - Eduardo Garcia Date: Fri, 22 Mar 2019 15:41:02 +1100 Subject: [PATCH 24/35] update SearchInput --- .../lib/components/bonus/SearchInput.jsx | 159 ++++++++---------- 1 file changed, 74 insertions(+), 85 deletions(-) diff --git a/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx b/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx index fa611fdd0..8ec9df601 100644 --- a/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx +++ b/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx @@ -5,34 +5,31 @@ import withStyles from '@material-ui/core/styles/withStyles'; import SearchIcon from 'mdi-material-ui/Magnify'; import ClearIcon from 'mdi-material-ui/CloseCircle'; import Input from '@material-ui/core/Input'; +import TextField from '@material-ui/core/TextField'; import NoSsr from '@material-ui/core/NoSsr'; import classNames from 'classnames'; import _debounce from 'lodash/debounce'; import KeyboardEventHandler from 'react-keyboard-event-handler'; -import autosizeInput from 'autosize-input'; const styles = theme => ({ - + '@global': { 'input[type=text]::-ms-clear, input[type=text]::-ms-reveal': - { - display: 'none', - width: 0, - height: 0, - }, + { + display: 'none', + width: 0, + height: 0, + }, 'input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button': - { display: 'none' }, + { display: 'none' }, 'input[type="search"]::-webkit-search-results-button, input[type="search"]::-webkit-search-results-decoration': - { display: 'none' }, + { display: 'none' }, }, - + root: { - display: 'inline-flex', - backgroundColor: theme.palette.common.faintBlack, - borderRadius: 20, - padding: 6, + marginTop: 0 }, - + clear: { transition: theme.transitions.create('opacity,transform', { duration: theme.transitions.duration.short, @@ -48,29 +45,25 @@ const styles = theme => ({ }, flexDirection: 'column', }, - + clearDense: { width: 32, height: 32, margin: -4, marginLeft: 0, }, - + clearDisabled: { opacity: 0, pointerEvents: 'none', }, - - dense: { - padding: 4, - }, - + icon: { color: theme.palette.common.lightBlack, marginLeft: theme.spacing.unit, marginRight: theme.spacing.unit, }, - + input: { lineHeight: 1, paddingTop: 2, @@ -81,51 +74,41 @@ const styles = theme => ({ }),*/ minWidth: 130, }, - + }); class SearchInput extends PureComponent { - + constructor (props) { super(props); - + this.state = { value: props.defaultValue || '', }; - + this.input = null; - this.removeAutosize = null; - this.triggerResize = null; - + this.updateQuery = _debounce(this.updateQuery, 500); } - + componentDidMount () { if (!document) return; const element = document.querySelector(`.search-input-${this.props.name} input[type=search]`); - - // We have to patch into addEventListener because autosizeInput provides no way to trigger resize + element._addEventListener = element.addEventListener; element.addEventListener = function(type, listener, useCapture) { if(useCapture === undefined) useCapture = false; this._addEventListener(type, listener, useCapture); - this.triggerResize = listener; }; - this.removeAutosize = autosizeInput(element); - - this.triggerResize = element.triggerResize; element.addEventListener = element._addEventListener; } - + componentWillUnmount () { - if (this.removeAutosize) { - this.removeAutosize(); - } } - + handleShortcutKeys = (key, event) => { switch (key) { case 's': @@ -139,34 +122,34 @@ class SearchInput extends PureComponent { break; } }; - + handleFocus = () => { this.input.select(); }; - + focusInput = (event) => { this.input.focus(); }; - + clearSearch = (event, dontFocus) => { - this.setState({ value: '' }, this.triggerResize); + this.setState({ value: '' }); this.updateQuery(''); - + if (!dontFocus) { this.focusInput(); } }; - + updateSearch = (event) => { const value = event.target.value; this.setState({ value: value }); this.updateQuery(value); }; - + updateQuery = (value) => { this.props.updateQuery(value); }; - + render () { const { classes, @@ -175,47 +158,53 @@ class SearchInput extends PureComponent { noShortcuts, name, } = this.props; - + const searchIcon = ; - + const clearButton = } - onClick={this.clearSearch} - classes={{ - root: classNames(!this.state.value && classes.clearDisabled), - button: classNames('clear-button', classes.clear, dense && classes.clearDense), - }} - disabled={!this.state.value} + titleId="search.clear" + icon={} + onClick={this.clearSearch} + classes={{ + root: classNames(!this.state.value && classes.clearDisabled), + button: classNames('clear-button', classes.clear, dense && classes.clearDense), + }} + disabled={!this.state.value} />; - + return ( - - this.input = input} - value={this.state.value} - type="search" - onChange={this.updateSearch} - onFocus={this.handleFocus} - disableUnderline={true} - startAdornment={searchIcon} - endAdornment={clearButton} - /> - - { - // KeyboardEventHandler is not valid on the server, where its name is undefined - typeof window !== 'undefined' && KeyboardEventHandler.name && !noShortcuts && - - - } - - + + this.input = input} + fullWidth + className={classNames('search-input', `search-input-${name}`, classes.root, dense && classes.inputTypeSearch, className, classes.textField)} + margin="normal" + variant="outlined" + onChange={this.updateSearch} + onFocus={this.handleFocus} + InputProps={{ + startAdornment: searchIcon, + endAdornment: clearButton + }} + /> + + { + // KeyboardEventHandler is not valid on the server, where its name is undefined + typeof window !== 'undefined' && KeyboardEventHandler.name && !noShortcuts && + + + } + + ); } - + } From 91275b2f3d7321077ac981def00380b83f5507b4 Mon Sep 17 00:00:00 2001 From: enzo - Eduardo Garcia Date: Fri, 22 Mar 2019 15:43:51 +1100 Subject: [PATCH 25/35] Update Datatable, import SearchInput --- .../lib/components/core/Datatable.jsx | 533 +++++++++--------- .../lib/components/index.js | 2 +- 2 files changed, 270 insertions(+), 265 deletions(-) diff --git a/packages/vulcan-ui-material/lib/components/core/Datatable.jsx b/packages/vulcan-ui-material/lib/components/core/Datatable.jsx index 982a404d6..2c5fd274f 100644 --- a/packages/vulcan-ui-material/lib/components/core/Datatable.jsx +++ b/packages/vulcan-ui-material/lib/components/core/Datatable.jsx @@ -39,14 +39,14 @@ const baseStyles = theme => ({ alignItems: 'center', }, addButton: { - position: 'absolute', - top: '-8px', - right: 0, + top: '9.5rem', + right: '2rem', + position: 'fixed', + bottom: 'auto', }, - search: { - marginBottom: theme.spacing.unit * 8, + table: { + marginTop:0 }, - table: {}, denseTable: {}, denserTable: {}, flatTable: {}, @@ -70,19 +70,19 @@ const delay = (function () { })(); class Datatable extends PureComponent { - + constructor (props) { super(props); - + this.updateQuery = this.updateQuery.bind(this); - + this.state = { value: '', query: '', currentSort: {}, }; } - + toggleSort = column => { let currentSort; if (!this.state.currentSort[column]) { @@ -94,7 +94,7 @@ class Datatable extends PureComponent { } this.setState({ currentSort }); }; - + updateQuery (value) { this.setState({ value: value @@ -105,22 +105,22 @@ class Datatable extends PureComponent { }); }, 700); } - + render () { if (this.props.data) { - + return ; - + } else { - + const { className, collection, @@ -130,51 +130,52 @@ class Datatable extends PureComponent { currentUser, classes, } = this.props; - + const listOptions = { collection: collection, ...options, }; - + const DatatableWithMulti = withMulti(listOptions)(Components.DatatableContents); - + // add _id to orderBy when we want to sort a column, to avoid breaking the graphql() hoc; // see https://github.com/VulcanJS/Vulcan/issues/2090#issuecomment-433860782 // this.state.currentSort !== {} is always false, even when console.log(this.state.currentSort) displays // {}. So we test on the length of keys for this object. const orderBy = Object.keys(this.state.currentSort).length == 0 ? {} : - { ...this.state.currentSort, _id: -1 }; - + { ...this.state.currentSort, _id: -1 }; + return ( -
    - {/* DatatableAbove Component part*/} - { - showSearch && - - + {/* DatatableAbove Component part*/} + { + showSearch && + + + } + { + showNew && + + + } + + - } - { - showNew && - - - } - - -
    +
    ); } } @@ -262,20 +263,20 @@ const DatatableContents = ({ paginationTerms, setPaginationTerms }) => { - + if (loading) { return ; } else if (!results || !results.length) { return emptyState || null; } - + if (queryDataRef) queryDataRef(this.props); - + const denseClass = dense && classes[dense + 'Table']; - + // Pagination functions const getPage = (paginationTerms) => (parseInt((paginationTerms.limit - 1) / paginationTerms.itemsPerPage)); - + const onChangePage = (event, page) => { setPaginationTerms({ itemsPerPage: paginationTerms.itemsPerPage, @@ -283,7 +284,7 @@ const DatatableContents = ({ offset: page * paginationTerms.itemsPerPage }); }; - + const onChangeRowsPerPage = (event) => { let value = event.target.value; let offset = Math.max(0, parseInt((paginationTerms.limit - paginationTerms.itemsPerPage) / value) * value); @@ -294,117 +295,121 @@ const DatatableContents = ({ offset: offset }); }; - + return ( - - { - title && - - - title - - - } - - - - { - _.sortBy(columns, column => column.order).map( - (column, index) => - - ) - } - { - (showEdit || editComponent) && - - - } - - - + { - results && - - - { - results.map( - (document, index) => - ) - } - + (title)? + + + title + + + :null } - +
    + { + columns && + + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + + ) + } + { + (showEdit || editComponent) && + + + } + + + } + + { + results && + + + { + results.map( + (document, index) => + ) + } + + } + + { + footerData && + + + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + + {footerData[index]} + + ) + } + { + (showEdit || editComponent) && + + + } + + + + } + +
    { - footerData && - - - - { - _.sortBy(columns, column => column.order).map( - (column, index) => - - {footerData[index]} - - ) - } - { - (showEdit || editComponent) && - - - } - - - + paginate && + + } - - - { - paginate && - - - } - { - !paginate && loadMore && - - - } -
    + { + !paginate && loadMore && + + + } + ); }; @@ -420,10 +425,10 @@ DatatableHeader Component const DatatableHeader = ({ collection, intlNamespace, column, classes, toggleSort, currentSort }, { intl }) => { const columnName = typeof column === 'string' ? column : column.name || column.label; let formattedLabel = ''; - + if (collection) { const schema = collection.simpleSchema()._schema; - + /* use either: @@ -433,16 +438,16 @@ const DatatableHeader = ({ collection, intlNamespace, column, classes, toggleSor */ const defaultMessage = schema[columnName] ? schema[columnName].label : Utils.camelToSpaces(columnName); formattedLabel = typeof columnName === 'string' ? - intl.formatMessage({ - id: `${collection._name}.${columnName}`, - defaultMessage: defaultMessage - }) : - ''; - + intl.formatMessage({ + id: `${collection._name}.${columnName}`, + defaultMessage: defaultMessage + }) : + ''; + // if sortable is a string, use it as the name of the property to sort by. If it's just `true`, use // column.name const sortPropertyName = typeof column.sortable === 'string' ? column.sortable : column.name; - + if (column.sortable) { return {formattedLabel}; + className={classNames(classes.tableHeadCell, column.headerClass)}>{formattedLabel}; }; @@ -482,23 +487,23 @@ DatatableSorter Component */ const DatatableSorter = ({ name, label, toggleSort, currentSort, sortable }) => - - - toggleSort(name)} + - {label} - - - ; + toggleSort(name)} + > + {label} + + + ; replaceComponent('DatatableSorter', DatatableSorter); @@ -532,52 +537,52 @@ const DatatableRow = ({ handleRowClick, classes, }, { intl }) => { - + const EditComponent = editComponent; - + if (typeof rowClass === 'function') { rowClass = rowClass(document); } - + return ( - handleRowClick(event, document))} - hover - > - - { - _.sortBy(columns, column => column.order).map( - (column, index) => - ) - } - - { - (showEdit || editComponent) && - - - { - EditComponent && - - - } - { - showEdit && - - - } - - } - - + handleRowClick(event, document))} + hover + > + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + ) + } + + { + (showEdit || editComponent) && + + + { + EditComponent && + + + } + { + showEdit && + + + } + + } + + ); }; @@ -597,26 +602,26 @@ DatatableCell Component */ const DatatableCell = ({ column, document, currentUser, classes }) => { const Component = column.component || - Components[column.componentName] || - Components.DatatableDefaultCell; - + Components[column.componentName] || + Components.DatatableDefaultCell; + const columnName = typeof column === 'string' ? column : column.name; const className = typeof columnName === 'string' ? - `datatable-item-${columnName.toLowerCase()}` : - ''; + `datatable-item-${columnName.toLowerCase()}` : + ''; const cellClass = typeof column.cellClass === 'function' ? - column.cellClass({ column, document, currentUser }) : - typeof column.cellClass === 'string' ? - column.cellClass : - null; - + column.cellClass({ column, document, currentUser }) : + typeof column.cellClass === 'string' ? + column.cellClass : + null; + return ( - - - + + + ); }; @@ -630,15 +635,15 @@ DatatableDefaultCell Component */ const DatatableDefaultCell = ({ column, document }) => -
    - { - typeof column === 'string' - ? - getFieldValue(document[column]) - : - getFieldValue(document[column.name]) - } -
    ; +
    + { + typeof column === 'string' + ? + getFieldValue(document[column]) + : + getFieldValue(document[column.name]) + } +
    ; replaceComponent('DatatableDefaultCell', DatatableDefaultCell); diff --git a/packages/vulcan-ui-material/lib/components/index.js b/packages/vulcan-ui-material/lib/components/index.js index ebfdc6fcd..cbee0bccb 100644 --- a/packages/vulcan-ui-material/lib/components/index.js +++ b/packages/vulcan-ui-material/lib/components/index.js @@ -7,7 +7,7 @@ import './accounts/AccountsPasswordOrService'; import './accounts/AccountsSocialButtons'; import './bonus/LoadMore'; -// import './bonus/SearchInput'; +import './bonus/SearchInput'; import './bonus/TooltipIntl'; import './bonus/TooltipIconButton'; From 1025e6871d471fbc763a22132559864e1335ae0f Mon Sep 17 00:00:00 2001 From: enzo - Eduardo Garcia Date: Fri, 22 Mar 2019 15:51:28 +1100 Subject: [PATCH 26/35] fix indentation material-ui search input --- .../lib/components/bonus/SearchInput.jsx | 87 ++-- .../lib/components/core/Datatable.jsx | 438 +++++++++--------- 2 files changed, 262 insertions(+), 263 deletions(-) diff --git a/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx b/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx index 8ec9df601..bcfb10a27 100644 --- a/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx +++ b/packages/vulcan-ui-material/lib/components/bonus/SearchInput.jsx @@ -4,7 +4,6 @@ import { Components, registerComponent } from 'meteor/vulcan:core'; import withStyles from '@material-ui/core/styles/withStyles'; import SearchIcon from 'mdi-material-ui/Magnify'; import ClearIcon from 'mdi-material-ui/CloseCircle'; -import Input from '@material-ui/core/Input'; import TextField from '@material-ui/core/TextField'; import NoSsr from '@material-ui/core/NoSsr'; import classNames from 'classnames'; @@ -15,15 +14,15 @@ const styles = theme => ({ '@global': { 'input[type=text]::-ms-clear, input[type=text]::-ms-reveal': - { - display: 'none', - width: 0, - height: 0, - }, + { + display: 'none', + width: 0, + height: 0, + }, 'input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button': - { display: 'none' }, + { display: 'none' }, 'input[type="search"]::-webkit-search-results-button, input[type="search"]::-webkit-search-results-decoration': - { display: 'none' }, + { display: 'none' }, }, root: { @@ -162,46 +161,46 @@ class SearchInput extends PureComponent { const searchIcon = ; const clearButton = } - onClick={this.clearSearch} - classes={{ - root: classNames(!this.state.value && classes.clearDisabled), - button: classNames('clear-button', classes.clear, dense && classes.clearDense), - }} - disabled={!this.state.value} + titleId="search.clear" + icon={} + onClick={this.clearSearch} + classes={{ + root: classNames(!this.state.value && classes.clearDisabled), + button: classNames('clear-button', classes.clear, dense && classes.clearDense), + }} + disabled={!this.state.value} />; return ( - - this.input = input} - fullWidth - className={classNames('search-input', `search-input-${name}`, classes.root, dense && classes.inputTypeSearch, className, classes.textField)} - margin="normal" - variant="outlined" - onChange={this.updateSearch} - onFocus={this.handleFocus} - InputProps={{ - startAdornment: searchIcon, - endAdornment: clearButton - }} - /> - - { - // KeyboardEventHandler is not valid on the server, where its name is undefined - typeof window !== 'undefined' && KeyboardEventHandler.name && !noShortcuts && + + this.input = input} + fullWidth + className={classNames('search-input', `search-input-${name}`, classes.root, dense && classes.inputTypeSearch, className, classes.textField)} + margin="normal" + variant="outlined" + onChange={this.updateSearch} + onFocus={this.handleFocus} + InputProps={{ + startAdornment: searchIcon, + endAdornment: clearButton + }} + /> + + { + // KeyboardEventHandler is not valid on the server, where its name is undefined + typeof window !== 'undefined' && KeyboardEventHandler.name && !noShortcuts && - - } - - + + } + + ); } diff --git a/packages/vulcan-ui-material/lib/components/core/Datatable.jsx b/packages/vulcan-ui-material/lib/components/core/Datatable.jsx index 2c5fd274f..d2561b1b4 100644 --- a/packages/vulcan-ui-material/lib/components/core/Datatable.jsx +++ b/packages/vulcan-ui-material/lib/components/core/Datatable.jsx @@ -110,13 +110,13 @@ class Datatable extends PureComponent { if (this.props.data) { return ; } else { @@ -146,36 +146,36 @@ class Datatable extends PureComponent { { ...this.state.currentSort, _id: -1 }; return ( -
    - {/* DatatableAbove Component part*/} - { - showSearch && +
    + {/* DatatableAbove Component part*/} + { + showSearch && - - } - { - showNew && - - - } - - -
    + } + { + showNew && + + + } + + +
    ); } } @@ -297,119 +297,119 @@ const DatatableContents = ({ }; return ( - + + { + (title)? + + + title + + + :null + } + + { + columns && + + + { + _.sortBy(columns, column => column.order).map( + (column, index) => + + ) + } + { + (showEdit || editComponent) && + + + } + + + } + { - (title)? - - - title - - - :null + results && + + + { + results.map( + (document, index) => + ) + } + } -
    - { - columns && - - - { - _.sortBy(columns, column => column.order).map( - (column, index) => - - ) - } - { - (showEdit || editComponent) && - - } - - - } + { + footerData && - { - results && - - + + { - results.map( - (document, index) => - ) + _.sortBy(columns, column => column.order).map( + (column, index) => + + {footerData[index]} + + ) } - - } + { + (showEdit || editComponent) && - { - footerData && + + } + + - - - { - _.sortBy(columns, column => column.order).map( - (column, index) => - - {footerData[index]} - - ) - } - { - (showEdit || editComponent) && - - - } - - - - } - -
    - { - paginate && - - } - { - !paginate && loadMore && - - } -
    + + { + paginate && + + + } + { + !paginate && loadMore && + + + } +
    ); }; @@ -438,11 +438,11 @@ const DatatableHeader = ({ collection, intlNamespace, column, classes, toggleSor */ const defaultMessage = schema[columnName] ? schema[columnName].label : Utils.camelToSpaces(columnName); formattedLabel = typeof columnName === 'string' ? - intl.formatMessage({ - id: `${collection._name}.${columnName}`, - defaultMessage: defaultMessage - }) : - ''; + intl.formatMessage({ + id: `${collection._name}.${columnName}`, + defaultMessage: defaultMessage + }) : + ''; // if sortable is a string, use it as the name of the property to sort by. If it's just `true`, use // column.name @@ -458,17 +458,17 @@ const DatatableHeader = ({ collection, intlNamespace, column, classes, toggleSor } } else if (intlNamespace) { formattedLabel = typeof columnName === 'string' ? - intl.formatMessage({ - id: `${intlNamespace}.${columnName}`, - defaultMessage: columnName - }) : - ''; + intl.formatMessage({ + id: `${intlNamespace}.${columnName}`, + defaultMessage: columnName + }) : + ''; } else { formattedLabel = intl.formatMessage({ id: columnName, defaultMessage: columnName }); } return {formattedLabel}; + className={classNames(classes.tableHeadCell, column.headerClass)}>{formattedLabel}; }; @@ -487,23 +487,23 @@ DatatableSorter Component */ const DatatableSorter = ({ name, label, toggleSort, currentSort, sortable }) => - + - toggleSort(name)} > - toggleSort(name)} - > - {label} - - - ; + {label} + + + ; replaceComponent('DatatableSorter', DatatableSorter); @@ -545,44 +545,44 @@ const DatatableRow = ({ } return ( - handleRowClick(event, document))} - hover - > + handleRowClick(event, document))} + hover + > - { - _.sortBy(columns, column => column.order).map( - (column, index) => - ) - } + { + _.sortBy(columns, column => column.order).map( + (column, index) => + ) + } - { - (showEdit || editComponent) && + { + (showEdit || editComponent) && - - { - EditComponent && + + { + EditComponent && - - } - { - showEdit && + + } + { + showEdit && - - } - - } + + } + + } - + ); }; @@ -602,26 +602,26 @@ DatatableCell Component */ const DatatableCell = ({ column, document, currentUser, classes }) => { const Component = column.component || - Components[column.componentName] || - Components.DatatableDefaultCell; + Components[column.componentName] || + Components.DatatableDefaultCell; const columnName = typeof column === 'string' ? column : column.name; const className = typeof columnName === 'string' ? - `datatable-item-${columnName.toLowerCase()}` : - ''; + `datatable-item-${columnName.toLowerCase()}` : + ''; const cellClass = typeof column.cellClass === 'function' ? - column.cellClass({ column, document, currentUser }) : - typeof column.cellClass === 'string' ? - column.cellClass : - null; + column.cellClass({ column, document, currentUser }) : + typeof column.cellClass === 'string' ? + column.cellClass : + null; return ( - - - + + + ); }; @@ -635,15 +635,15 @@ DatatableDefaultCell Component */ const DatatableDefaultCell = ({ column, document }) => -
    - { - typeof column === 'string' - ? - getFieldValue(document[column]) - : - getFieldValue(document[column.name]) - } -
    ; +
    + { + typeof column === 'string' + ? + getFieldValue(document[column]) + : + getFieldValue(document[column.name]) + } +
    ; replaceComponent('DatatableDefaultCell', DatatableDefaultCell); From 0e79512b06a32b549df66dc7a4c593186aa4caf9 Mon Sep 17 00:00:00 2001 From: Harold Date: Wed, 20 Mar 2019 16:29:31 -0600 Subject: [PATCH 27/35] Create MuiPicker component to display dates --- .../forms/base-controls/MuiPicker.jsx | 84 +++++++++++++++++++ .../lib/components/forms/controls/Date.jsx | 6 +- 2 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx new file mode 100644 index 000000000..6e36bc87c --- /dev/null +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx @@ -0,0 +1,84 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import createReactClass from 'create-react-class'; +import withStyles from '@material-ui/core/styles/withStyles'; +import ComponentMixin from './mixins/component'; +import MuiFormControl from './MuiFormControl'; +import MuiFormHelper from './MuiFormHelper'; +import TextField from '@material-ui/core/TextField'; + +import moment from 'moment'; +import 'moment-timezone'; + +const dateFormat = 'YYYY-MM-DD'; + +export const styles = theme => ({ + inputRoot: { + '& .clear-enabled': { opacity: 0 }, + '&:hover .clear-enabled': { opacity: 0.54 }, + }, + inputFocused: { + '& .clear-enabled': { opacity: 0.54 } + }, +}); + +//noinspection JSUnusedGlobalSymbols +const MuiPicker = createReactClass({ + + mixins: [ComponentMixin], + + displayName: 'MuiPicker', + + propTypes: { + type: PropTypes.oneOf([ + 'date', + 'datetime', + 'datetime-local', + ]), + errors: PropTypes.array, + placeholder: PropTypes.string, + formatValue: PropTypes.func, + hideClear: PropTypes.bool, + }, + + getDefaultProps: function () { + return { + type: 'date', + }; + }, + + handleChange: function (event) { + let value = event.target.value; + if (this.props.scrubValue) { + value = this.props.scrubValue(value); + } + this.props.onChange(value); + }, + + render: function () { + const { classes, disabled, autoFocus } = this.props; + const value = moment(this.props.value, dateFormat, true).isValid() ? this.props.value : moment(this.props.value).format(dateFormat); + + const options = this.props.options || {}; + + return ( + + (this.element = c)} + {...this.cleanProps(this.props)} + id={this.getId()} + value={value} + autoFocus={options.autoFocus || autoFocus} + onChange={this.handleChange} + disabled={disabled} + placeholder={this.props.placeholder} + classes={{ root: classes.inputRoot }} + /> + + + ); + } +}); + + +export default withStyles(styles)(MuiPicker); diff --git a/packages/vulcan-ui-material/lib/components/forms/controls/Date.jsx b/packages/vulcan-ui-material/lib/components/forms/controls/Date.jsx index 190e08ced..cca995266 100644 --- a/packages/vulcan-ui-material/lib/components/forms/controls/Date.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/controls/Date.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import MuiInput from '../base-controls/MuiInput'; +import MuiPicker from '../base-controls/MuiPicker'; import { registerComponent } from 'meteor/vulcan:core'; import withStyles from '@material-ui/core/styles/withStyles'; @@ -29,9 +29,7 @@ export const styles = theme => ({ }); - const DateComponent = ({ refFunction, classes, ...properties }) => - ; - + ; registerComponent('FormComponentDate', DateComponent, [withStyles, styles]); From 9b9459cf6d2fb32e0323233ab2e80d5193458a78 Mon Sep 17 00:00:00 2001 From: enzo - Eduardo Garcia Date: Fri, 22 Mar 2019 15:30:50 +1100 Subject: [PATCH 28/35] Add marginTop to MuiPicker --- .../lib/components/forms/base-controls/MuiPicker.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx index 6e36bc87c..062b1a85e 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx @@ -14,6 +14,7 @@ const dateFormat = 'YYYY-MM-DD'; export const styles = theme => ({ inputRoot: { + 'marginTop': '16px', '& .clear-enabled': { opacity: 0 }, '&:hover .clear-enabled': { opacity: 0.54 }, }, @@ -40,13 +41,13 @@ const MuiPicker = createReactClass({ formatValue: PropTypes.func, hideClear: PropTypes.bool, }, - + getDefaultProps: function () { return { type: 'date', }; }, - + handleChange: function (event) { let value = event.target.value; if (this.props.scrubValue) { @@ -54,7 +55,7 @@ const MuiPicker = createReactClass({ } this.props.onChange(value); }, - + render: function () { const { classes, disabled, autoFocus } = this.props; const value = moment(this.props.value, dateFormat, true).isValid() ? this.props.value : moment(this.props.value).format(dateFormat); From 05c8f5f09c19a5196c93057bf29e5484d3c6385e Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 22 Mar 2019 10:12:34 +0100 Subject: [PATCH 29/35] remove timezone --- .../lib/components/forms/base-controls/MuiPicker.jsx | 2 +- packages/vulcan-ui-material/readme.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx index 062b1a85e..4b4576c27 100644 --- a/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx +++ b/packages/vulcan-ui-material/lib/components/forms/base-controls/MuiPicker.jsx @@ -8,7 +8,7 @@ import MuiFormHelper from './MuiFormHelper'; import TextField from '@material-ui/core/TextField'; import moment from 'moment'; -import 'moment-timezone'; +//import 'moment-timezone'; const dateFormat = 'YYYY-MM-DD'; diff --git a/packages/vulcan-ui-material/readme.md b/packages/vulcan-ui-material/readme.md index c644b6807..5e2d912f9 100644 --- a/packages/vulcan-ui-material/readme.md +++ b/packages/vulcan-ui-material/readme.md @@ -24,7 +24,8 @@ meteor npm install --save react-autosuggest meteor npm install --save autosuggest-highlight meteor npm install --save react-isolated-scroll meteor npm install --save-exact react-keyboard-event-handler@1.3.2 -meteor npm install --save autosize-input +#meteor npm install --save autosize-input +meteor npm install --save moment-timezone ``` > NOTE: If you want to avoid deprecation warnings added in MUI versions after 3.1.0, you can lock MUI to the currently supported version using `meteor npm install --save @material-ui/core@3.1.0`. Don't for get to remove or update the version number when you update this package in the future. From 9864424d89c0c3ea3c4b428bce233bb69e30009f Mon Sep 17 00:00:00 2001 From: eric-burel Date: Fri, 22 Mar 2019 11:30:26 +0100 Subject: [PATCH 30/35] Fix failure when document is invalid in the watchedMutations, small refactor --- package.json | 7 +- .../lib/modules/default_mutations.js | 268 ++++++++++-------- packages/vulcan-core/package.js | 1 + packages/vulcan-core/test/client/index.js | 1 + .../vulcan-core/test/client/mutations.test.js | 19 ++ packages/vulcan-core/test/index.js | 1 + packages/vulcan-core/test/mutations.test.js | 29 ++ .../lib/client/apollo-client/links/error.js | 3 +- 8 files changed, 209 insertions(+), 120 deletions(-) create mode 100644 packages/vulcan-core/test/client/index.js create mode 100644 packages/vulcan-core/test/client/mutations.test.js create mode 100644 packages/vulcan-core/test/mutations.test.js diff --git a/package.json b/package.json index db7b08586..5782ced0d 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ } }, "dependencies": { - "@babel/runtime": "7.0.0-beta.55", + "@babel/runtime": "7.1.2", "analytics-node": "^2.1.1", "apollo-cache-inmemory": "^1.4.2", "apollo-client": "2.4.12", @@ -30,6 +30,7 @@ "apollo-link-error": "^1.1.5", "apollo-link-schema": "^1.1.1", "apollo-link-state": "^0.4.2", + "apollo-link-watched-mutation": "^0.1.0", "apollo-server": "2.3.3", "apollo-server-express": "2.3.3", "babel-runtime": "^6.26.0", @@ -70,14 +71,14 @@ "moment": "^2.13.0", "prop-types": "^15.6.0", "qs": "^6.6.0", - "react": "^16.2.0", + "react": "^16.4.1", "react-addons-pure-render-mixin": "^15.4.1", "react-apollo": "^2.4.1", "react-bootstrap": "^1.0.0-beta.5", "react-bootstrap-datetimepicker": "0.0.22", "react-cookie": "^2.1.4", "react-datetime": "^2.11.1", - "react-dom": "^16.2.0", + "react-dom": "^16.4.1", "react-dropzone": "^8.0.3", "react-helmet": "^5.1.3", "react-intl": "^2.1.3", diff --git a/packages/vulcan-core/lib/modules/default_mutations.js b/packages/vulcan-core/lib/modules/default_mutations.js index f1eecfb6f..d8059f2d6 100644 --- a/packages/vulcan-core/lib/modules/default_mutations.js +++ b/packages/vulcan-core/lib/modules/default_mutations.js @@ -27,6 +27,135 @@ import isEmpty from 'lodash/isEmpty'; const defaultOptions = { create: true, update: true, upsert: true, delete: true }; +/** + * Safe getter + * Must returns null if the document is absent (eg in case of validation failure) + * @param {*} mutation + * @param {*} mutationName + */ +const getDocumentFromMutation = (mutation, mutationName) => { + const mutationData = (mutation.result.data[mutationName] || {}); + const document = mutationData.data; + return document; +}; + +const getCreateMutationName = (typeName) => `create${typeName}`; +const getUpdateMutationName = (typeName) => `update${typeName}`; +const getDeleteMutationName = (typeName) => `delete${typeName}`; +const getUpsertMutationName = (typeName) => `upsert${typeName}`; +const getMultiResolverName = (typeName) => Utils.camelCaseify(Utils.pluralize(typeName)); +const getMultiQueryName = (typeName) => `multi${typeName}Query`; + +/* + +Handle post-mutation updates of the client cache +TODO: this is a client only function +it should be called by a callback on collection create? +*/ +export const registerWatchedMutations = (mutations, typeName) => { + if (Meteor.isClient) { + const multiQueryName = getMultiQueryName(typeName); + const multiResolverName = getMultiResolverName(typeName); + // create + if (mutations.create) { + const mutationName = mutations.create.name; + registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => { + // get mongo selector and options objects based on current terms + const terms = query.variables.input.terms; + const collection = Collections.find(c => c.typeName === typeName); + const parameters = collection.getParameters(terms /* apolloClient */); + const { selector, options } = parameters; + let results = query.result; + const document = getDocumentFromMutation(mutation, mutationName); + // nothing to add + if (!document) return results; + + if (belongsToSet(document, selector)) { + if (!isInSet(results[multiResolverName], document)) { + // make sure document hasn't been already added as this may be called several times + results[multiResolverName] = addToSet(results[multiResolverName], document); + } + results[multiResolverName] = reorderSet(results[multiResolverName], options.sort); + } + + results[multiResolverName].__typename = `Multi${typeName}Output`; + + // console.log('// create'); + // console.log(mutation); + // console.log(query); + // console.log(collection); + // console.log(parameters); + // console.log(results); + + return results; + }); + } + //update + if (mutations.update) { + const mutationName = mutations.update.name; + registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => { + // get mongo selector and options objects based on current terms + const terms = query.variables.input.terms; + const collection = Collections.find(c => c.typeName === typeName); + const parameters = collection.getParameters(terms /* apolloClient */); + const { selector, options } = parameters; + let results = query.result; + const document = getDocumentFromMutation(mutation, mutationName); + // nothing to update + if (!document) return results; + + if (belongsToSet(document, selector)) { + // edited document belongs to the list + if (!isInSet(results[multiResolverName], document)) { + // if document wasn't already in list, add it + results[multiResolverName] = addToSet(results[multiResolverName], document); + } else { + // if document was already in the list, update it + results[multiResolverName] = updateInSet(results[multiResolverName], document); + } + results[multiResolverName] = reorderSet( + results[multiResolverName], + options.sort, + selector + ); + } else { + // if edited doesn't belong to current list anymore (based on view selector), remove it + results[multiResolverName] = removeFromSet(results[multiResolverName], document); + } + + results[multiResolverName].__typename = `Multi${typeName}Output`; + + // console.log('// update'); + // console.log(mutation); + // console.log(query); + // console.log(parameters); + // console.log(results); + + return results; + }); + } + //delete + if (mutations.delete) { + const mutationName = mutations.delete.name; + registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => { + let results = query.result; + const document = getDocumentFromMutation(mutation, mutationName); + // nothing to delete + if (!document) return results; + results[multiResolverName] = removeFromSet(results[multiResolverName], document); + results[multiResolverName].__typename = `Multi${typeName}Output`; + // console.log('// delete') + // console.log(mutation); + // console.log(query); + // console.log(parameters); + // console.log(results); + return results; + }); + } + } +}; + + export function getDefaultMutations(options) { let typeName, collectionName, mutationOptions; @@ -45,17 +174,16 @@ export function getDefaultMutations(options) { // register callbacks for documentation purposes registerCollectionCallbacks(typeName, mutationOptions); - const multiResolverName = Utils.camelCaseify(Utils.pluralize(typeName)); - const multiQueryName = `multi${typeName}Query`; const mutations = {}; if (mutationOptions.create) { // mutation for inserting a new document - const mutationName = `create${typeName}`; + const mutationName = getCreateMutationName(typeName); const createMutation = { description: `Mutation for creating new ${typeName} documents`, + name: mutationName, // check function called on a user to see if they can perform the operation check(user, document) { @@ -98,51 +226,16 @@ export function getDefaultMutations(options) { mutations.create = createMutation; // OpenCRUD backwards compatibility mutations.new = createMutation; - - /* - - Handle post-mutation updates of the client cache - - */ - if (Meteor.isClient) { - registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => { - // get mongo selector and options objects based on current terms - const terms = query.variables.input.terms; - const collection = Collections.find(c => c.typeName === typeName); - const parameters = collection.getParameters(terms /* apolloClient */); - const { selector, options } = parameters; - let results = query.result; - const document = mutation.result.data[mutationName].data; - - if (belongsToSet(document, selector)) { - if (!isInSet(results[multiResolverName], document)) { - // make sure document hasn't been already added as this may be called several times - results[multiResolverName] = addToSet(results[multiResolverName], document); - } - results[multiResolverName] = reorderSet(results[multiResolverName], options.sort); - } - - results[multiResolverName].__typename = `Multi${typeName}Output`; - - // console.log('// create'); - // console.log(mutation); - // console.log(query); - // console.log(collection); - // console.log(parameters); - // console.log(results); - - return results; - }); - } } if (mutationOptions.update) { // mutation for editing a specific document - const mutationName = `update${typeName}`; + const mutationName = getUpdateMutationName(typeName); const updateMutation = { description: `Mutation for updating a ${typeName} document`, + name: mutationName, // check function called on a user and document to see if they can perform the operation check(user, document) { @@ -159,13 +252,13 @@ export function getDefaultMutations(options) { // OpenCRUD backwards compatibility return Users.owns(user, document) ? Users.canDo(user, [ - `${typeName.toLowerCase()}.update.own`, - `${collectionName.toLowerCase()}.edit.own`, - ]) + `${typeName.toLowerCase()}.update.own`, + `${collectionName.toLowerCase()}.edit.own`, + ]) : Users.canDo(user, [ - `${typeName.toLowerCase()}.update.all`, - `${collectionName.toLowerCase()}.edit.all`, - ]); + `${typeName.toLowerCase()}.update.all`, + `${collectionName.toLowerCase()}.edit.all`, + ]); }, async mutation(root, { selector, data }, context) { @@ -211,56 +304,13 @@ export function getDefaultMutations(options) { // OpenCRUD backwards compatibility mutations.edit = updateMutation; - /* - - Handle post-mutation updates of the client cache - - */ - if (Meteor.isClient) { - registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => { - // get mongo selector and options objects based on current terms - const terms = query.variables.input.terms; - const collection = Collections.find(c => c.typeName === typeName); - const parameters = collection.getParameters(terms /* apolloClient */); - const { selector, options } = parameters; - let results = query.result; - const document = mutation.result.data[mutationName].data; - - if (belongsToSet(document, selector)) { - // edited document belongs to the list - if (!isInSet(results[multiResolverName], document)) { - // if document wasn't already in list, add it - results[multiResolverName] = addToSet(results[multiResolverName], document); - } else { - // if document was already in the list, update it - results[multiResolverName] = updateInSet(results[multiResolverName], document); - } - results[multiResolverName] = reorderSet( - results[multiResolverName], - options.sort, - selector - ); - } else { - // if edited doesn't belong to current list anymore (based on view selector), remove it - results[multiResolverName] = removeFromSet(results[multiResolverName], document); - } - - results[multiResolverName].__typename = `Multi${typeName}Output`; - - // console.log('// update'); - // console.log(mutation); - // console.log(query); - // console.log(parameters); - // console.log(results); - - return results; - }); - } } if (mutationOptions.upsert) { // mutation for upserting a specific document + const mutationName = getUpsertMutationName(typeName); mutations.upsert = { description: `Mutation for upserting a ${typeName} document`, + name: mutationName, async mutation(root, { selector, data }, context) { const collection = context[collectionName]; @@ -285,10 +335,11 @@ export function getDefaultMutations(options) { if (mutationOptions.delete) { // mutation for removing a specific document (same checks as edit mutation) - const mutationName = `delete${typeName}`; + const mutationName = getDeleteMutationName(typeName); const deleteMutation = { description: `Mutation for deleting a ${typeName} document`, + name: mutationName, check(user, document) { // OpenCRUD backwards compatibility @@ -301,13 +352,13 @@ export function getDefaultMutations(options) { // OpenCRUD backwards compatibility return Users.owns(user, document) ? Users.canDo(user, [ - `${typeName.toLowerCase()}.delete.own`, - `${collectionName.toLowerCase()}.remove.own`, - ]) + `${typeName.toLowerCase()}.delete.own`, + `${collectionName.toLowerCase()}.remove.own`, + ]) : Users.canDo(user, [ - `${typeName.toLowerCase()}.delete.all`, - `${collectionName.toLowerCase()}.remove.all`, - ]); + `${typeName.toLowerCase()}.delete.all`, + `${collectionName.toLowerCase()}.remove.all`, + ]); }, async mutation(root, { selector }, context) { @@ -350,25 +401,10 @@ export function getDefaultMutations(options) { // OpenCRUD backwards compatibility mutations.remove = deleteMutation; - /* + } - Handle post-mutation updates of the client cache - - */ - if (Meteor.isClient) { - registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => { - let results = query.result; - const document = mutation.result.data[mutationName].data; - results[multiResolverName] = removeFromSet(results[multiResolverName], document); - results[multiResolverName].__typename = `Multi${typeName}Output`; - // console.log('// delete') - // console.log(mutation); - // console.log(query); - // console.log(parameters); - // console.log(results); - return results; - }); - } + if (Meteor.isClient) { + registerWatchedMutations(mutations, typeName); } return mutations; diff --git a/packages/vulcan-core/package.js b/packages/vulcan-core/package.js index 3868867d6..bafb4bba8 100644 --- a/packages/vulcan-core/package.js +++ b/packages/vulcan-core/package.js @@ -25,4 +25,5 @@ Package.onUse(function(api) { Package.onTest(function(api) { api.use(['ecmascript', 'meteortesting:mocha', 'vulcan:test', 'vulcan:core']); api.mainModule('./test/index.js'); + api.mainModule('./test/client/index.js', ['client']); }); diff --git a/packages/vulcan-core/test/client/index.js b/packages/vulcan-core/test/client/index.js new file mode 100644 index 000000000..c49a4a470 --- /dev/null +++ b/packages/vulcan-core/test/client/index.js @@ -0,0 +1 @@ +import './mutations.test'; \ No newline at end of file diff --git a/packages/vulcan-core/test/client/mutations.test.js b/packages/vulcan-core/test/client/mutations.test.js new file mode 100644 index 000000000..344cef32b --- /dev/null +++ b/packages/vulcan-core/test/client/mutations.test.js @@ -0,0 +1,19 @@ +import { registerWatchedMutations } from '../../lib/modules/default_mutations'; +import expect from 'expect'; + +describe('vulcan:core/registerWatchedMutations', function(){ + it('should registerWatchedMutations without failing', function(){ + registerWatchedMutations({ + create:{ + name:'createFoo' + }, + update:{ + name:'updateFoo' + }, + delete:{ + name: 'deleteFoo' + } + }, 'Foo'); + expect(true).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/vulcan-core/test/index.js b/packages/vulcan-core/test/index.js index 1fe7eec6d..d6ee0c346 100644 --- a/packages/vulcan-core/test/index.js +++ b/packages/vulcan-core/test/index.js @@ -1,3 +1,4 @@ +import './mutations.test'; import './resolvers.test'; import './components.test'; import './containers.test'; diff --git a/packages/vulcan-core/test/mutations.test.js b/packages/vulcan-core/test/mutations.test.js new file mode 100644 index 000000000..d9c0f2687 --- /dev/null +++ b/packages/vulcan-core/test/mutations.test.js @@ -0,0 +1,29 @@ +import { getDefaultMutations } from '../lib/modules/default_mutations'; + +import expect from 'expect'; + + +describe('vulcan:core/default_mutations', function() { + + it('returns mutations', function(){ + const mutations = getDefaultMutations({ + typeName:'Foo', + collectionName:'Foos', + options: {} + }); + expect(mutations.create).toBeDefined(); + expect(mutations.update).toBeDefined(); + expect(mutations.delete).toBeDefined(); + }); + it('preserves openCRUD backward compatibility', function(){ + const mutations = getDefaultMutations({ + typeName:'Foo', + collectionName:'Foos', + options: {} + }); + expect(mutations.new).toBeDefined(); + expect(mutations.edit).toBeDefined(); + expect(mutations.remove).toBeDefined(); + }); + +}); \ No newline at end of file diff --git a/packages/vulcan-lib/lib/client/apollo-client/links/error.js b/packages/vulcan-lib/lib/client/apollo-client/links/error.js index cbe60daaf..57b6b670c 100644 --- a/packages/vulcan-lib/lib/client/apollo-client/links/error.js +++ b/packages/vulcan-lib/lib/client/apollo-client/links/error.js @@ -1,10 +1,11 @@ import { onError } from 'apollo-link-error'; +const locationsToStr = (locations=[]) => locations.map(({column, line}) => `line ${line}, col ${column}`).join(';'); const errorLink = onError(({ graphQLErrors, networkError }) => { if (graphQLErrors) graphQLErrors.map(({ message, locations, path }) => { // eslint-disable-next-line no-console - console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`); + console.log(`[GraphQL error]: Message: ${message}, Location: ${locationsToStr(locations)}, Path: ${path}`); }); if (networkError) { // eslint-disable-next-line no-console From 94552b20fdefa3c81b6a45601a3c5a45534a3df1 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Thu, 28 Mar 2019 18:26:56 +0900 Subject: [PATCH 31/35] apollo_server2.js -> apollo_server.js --- .../{apollo_server2.js => apollo_server.js} | 0 .../lib/server/apollo-server/index.js | 2 +- .../lib/server/apollo-server/startup.js | 2 +- .../vulcan-lib/lib/server/apollo_server.js | 280 ------------------ .../test/server/apollo-server.test.js | 2 +- 5 files changed, 3 insertions(+), 283 deletions(-) rename packages/vulcan-lib/lib/server/apollo-server/{apollo_server2.js => apollo_server.js} (100%) delete mode 100644 packages/vulcan-lib/lib/server/apollo_server.js diff --git a/packages/vulcan-lib/lib/server/apollo-server/apollo_server2.js b/packages/vulcan-lib/lib/server/apollo-server/apollo_server.js similarity index 100% rename from packages/vulcan-lib/lib/server/apollo-server/apollo_server2.js rename to packages/vulcan-lib/lib/server/apollo-server/apollo_server.js diff --git a/packages/vulcan-lib/lib/server/apollo-server/index.js b/packages/vulcan-lib/lib/server/apollo-server/index.js index 502f69a5a..b7d314606 100644 --- a/packages/vulcan-lib/lib/server/apollo-server/index.js +++ b/packages/vulcan-lib/lib/server/apollo-server/index.js @@ -1,4 +1,4 @@ -export * from './apollo_server2'; +export * from './apollo_server'; export * from './settings'; export { default as initGraphQL } from './initGraphQL'; diff --git a/packages/vulcan-lib/lib/server/apollo-server/startup.js b/packages/vulcan-lib/lib/server/apollo-server/startup.js index bf4fcb44a..61f12d86c 100644 --- a/packages/vulcan-lib/lib/server/apollo-server/startup.js +++ b/packages/vulcan-lib/lib/server/apollo-server/startup.js @@ -1,3 +1,3 @@ -const { onStart } = require('./apollo_server2'); +const { onStart } = require('./apollo_server'); // createApolloServer when server startup Meteor.startup(onStart); diff --git a/packages/vulcan-lib/lib/server/apollo_server.js b/packages/vulcan-lib/lib/server/apollo_server.js deleted file mode 100644 index 03b5e8ef1..000000000 --- a/packages/vulcan-lib/lib/server/apollo_server.js +++ /dev/null @@ -1,280 +0,0 @@ -/* import { graphqlExpress, graphiqlExpress } from 'apollo-server-express'; -import bodyParser from 'body-parser'; -import express from 'express'; -import { makeExecutableSchema } from 'graphql-tools'; -import deepmerge from 'deepmerge'; -import DataLoader from 'dataloader'; -import { formatError } from 'apollo-errors'; -import compression from 'compression'; -import { Meteor } from 'meteor/meteor'; -import { check } from 'meteor/check'; -// import { Accounts } from 'meteor/accounts-base'; -import { Engine } from 'apollo-engine'; - -import { GraphQLSchema } from '../modules/graphql.js'; -import { Utils } from '../modules/utils.js'; -import { webAppConnectHandlersUse } from './meteor_patch.js'; - -import { getSetting, registerSetting } from '../modules/settings.js'; -import { Collections } from '../modules/collections.js'; -import findByIds from '../modules/findbyids.js'; -import { runCallbacks } from '../modules/callbacks.js'; -import cookiesMiddleware from 'universal-cookie-express'; -// import Cookies from 'universal-cookie'; -import { _hashLoginToken, _tokenExpiration } from './accounts_helpers'; -import { getHeaderLocale } from './intl.js'; - -export let executableSchema; - -registerSetting('apolloEngine.logLevel', 'INFO', 'Log level (one of INFO, DEBUG, WARN, ERROR'); -registerSetting('apolloServer.tracing', Meteor.isDevelopment, 'Tracing by Apollo. Default is true on development and false on prod', true); - -// see https://github.com/apollographql/apollo-cache-control -const engineApiKey = getSetting('apolloEngine.apiKey'); -const engineLogLevel = getSetting('apolloEngine.logLevel', 'INFO'); -const engineConfig = { - apiKey: engineApiKey, - // "origins": [ - // { - // "http": { - // "url": "http://localhost:3000/graphql" - // } - // } - // ], - 'stores': [ - { - 'name': 'vulcanCache', - 'inMemory': { - 'cacheSize': 20000000 - } - } - ], - // "sessionAuth": { - // "store": "embeddedCache", - // "header": "Authorization" - // }, - // "frontends": [ - // { - // "host": "127.0.0.1", - // "port": 3000, - // "endpoint": "/graphql", - // "extensions": { - // "strip": [] - // } - // } - // ], - 'queryCache': { - 'publicFullQueryStore': 'vulcanCache', - 'privateFullQueryStore': 'vulcanCache' - }, - // "reporting": { - // "endpointUrl": "https://engine-report.apollographql.com", - // "debugReports": true - // }, - 'logging': { - 'level': engineLogLevel - } -}; -let engine; -if (engineApiKey) { - engine = new Engine({ engineConfig }); - engine.start(); -} - -// defaults -const defaultConfig = { - path: '/graphql', - maxAccountsCacheSizeInMB: 1, - graphiql: Meteor.isDevelopment, - graphiqlPath: '/graphiql', - graphiqlOptions: { - passHeader: "'Authorization': localStorage['Meteor.loginToken']", // eslint-disable-line quotes - }, - configServer: (graphQLServer) => { }, -}; - -const defaultOptions = { - formatError: e => ({ - message: e.message, - locations: e.locations, - path: e.path, - }), -}; - -if (Meteor.isDevelopment) { - defaultOptions.debug = true; -} - -// createApolloServer -const createApolloServer = (givenOptions = {}, givenConfig = {}) => { - const graphiqlOptions = { ...defaultConfig.graphiqlOptions, ...givenConfig.graphiqlOptions }; - const config = { ...defaultConfig, ...givenConfig }; - config.graphiqlOptions = graphiqlOptions; - - const graphQLServer = express(); - - config.configServer(graphQLServer); - - // Use Engine middleware - if (engineApiKey) { - graphQLServer.use(engine.expressMiddleware()); - } - - // cookies - graphQLServer.use(cookiesMiddleware()); - - // compression - graphQLServer.use(compression()); - - // GraphQL endpoint - graphQLServer.use(config.path, bodyParser.json({ limit: getSetting('apolloServer.jsonParserOptions.limit') }), graphqlExpress(async (req) => { - let options; - let user = null; - - if (typeof givenOptions === 'function') { - options = givenOptions(req); - } else { - options = givenOptions; - } - - // Merge in the defaults - options = { ...defaultOptions, ...options }; - if (options.context) { - // don't mutate the context provided in options - options.context = { ...options.context }; - } else { - options.context = {}; - } - - // enable tracing and caching - options.tracing = getSetting('apolloServer.tracing', Meteor.isDevelopment); - options.cacheControl = true; - - // note: custom default resolver doesn't currently work - // see https://github.com/apollographql/apollo-server/issues/716 - // options.fieldResolver = (source, args, context, info) => { - // return source[info.fieldName]; - // } - - // console.log('// apollo_server.js req.renderContext'); - // console.log(req.renderContext); - // console.log('\n\n'); - - // Get the token from the header - if (req.headers.authorization) { - const token = req.headers.authorization; - check(token, String); - const hashedToken = _hashLoginToken(token); - - // Get the user from the database - user = await Meteor.users.findOne( - { 'services.resume.loginTokens.hashedToken': hashedToken }, - ); - - if (user) { - - // identify user to any server-side analytics providers - runCallbacks('events.identify', user); - - const loginToken = Utils.findWhere(user.services.resume.loginTokens, { hashedToken }); - const expiresAt = _tokenExpiration(loginToken.when); - const isExpired = expiresAt < new Date(); - - if (!isExpired) { - options.context.userId = user._id; - options.context.currentUser = user; - } - } - } - - //add the headers to the context - options.context.headers = req.headers; - - - // merge with custom context - options.context = deepmerge(options.context, GraphQLSchema.context); - - // go over context and add Dataloader to each collection - Collections.forEach(collection => { - options.context[collection.options.collectionName].loader = new DataLoader(ids => findByIds(collection, ids, options.context), { cache: true }); - }); - - // look for headers either in renderContext (SSR) or req (normal request to the endpoint) - const headers = req.renderContext.originalHeaders || req.headers; - - options.context.locale = getHeaderLocale(headers, user && user.locale); - - if (headers.apikey && (headers.apikey === getSetting('vulcan.apiKey'))) { - options.context.currentUser = { isAdmin: true, isApiUser: true }; - } - // console.log('// apollo_server.js isSSR?', !!req.renderContext.originalHeaders ? 'yes' : 'no'); - // console.log('// apollo_server.js headers:'); - // console.log(headers); - // console.log('// apollo_server.js final locale: ', options.context.locale); - // console.log('\n\n'); - - // add error formatting from apollo-errors - options.formatError = formatError; - - return options; - })); - - // Start GraphiQL if enabled - if (config.graphiql) { - graphQLServer.use(config.graphiqlPath, graphiqlExpress({ ...config.graphiqlOptions, endpointURL: config.path })); - } - - // This binds the specified paths to the Express server running Apollo + GraphiQL - webAppConnectHandlersUse(Meteor.bindEnvironment(graphQLServer), { - name: 'graphQLServerMiddleware_bindEnvironment', - order: 30, - }); -}; - -// createApolloServer when server startup -Meteor.startup(() => { - - runCallbacks('graphql.init.before'); - - // typeDefs - const generateTypeDefs = () => [` -scalar JSON -scalar Date - -${GraphQLSchema.getAdditionalSchemas()} - -${GraphQLSchema.getCollectionsSchemas()} - -type Query { - -${GraphQLSchema.queries.map(q => ( - `${q.description ? ` # ${q.description} -` : ''} ${q.query} - `)).join('\n')} -} - -${GraphQLSchema.mutations.length > 0 ? `type Mutation { - -${GraphQLSchema.mutations.map(m => ( -`${m.description ? ` # ${m.description} -` : ''} ${m.mutation} -`)).join('\n')} -} -` : ''} -`]; - - const typeDefs = generateTypeDefs(); - - GraphQLSchema.finalSchema = typeDefs; - - executableSchema = makeExecutableSchema({ - typeDefs, - resolvers: GraphQLSchema.resolvers, - schemaDirectives: GraphQLSchema.directives, - }); - - createApolloServer({ - schema: executableSchema, - }); -}); - */ diff --git a/packages/vulcan-lib/test/server/apollo-server.test.js b/packages/vulcan-lib/test/server/apollo-server.test.js index 53fe7bd2c..e6e1d41d7 100644 --- a/packages/vulcan-lib/test/server/apollo-server.test.js +++ b/packages/vulcan-lib/test/server/apollo-server.test.js @@ -1,4 +1,4 @@ -import { createApolloServer } from '../../lib/server/apollo-server/apollo_server2'; +import { createApolloServer } from '../../lib/server/apollo-server/apollo_server'; //import initGraphQL from '../../lib/server/apollo-server/initGraphQL'; //import { GraphQLSchema } from '../../lib/modules/graphql'; import expect from 'expect'; From 88256624318e6d72ac82f373c5fca53424ae7665 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Thu, 28 Mar 2019 18:27:48 +0900 Subject: [PATCH 32/35] v1.13.0 --- package.json | 2 +- packages/vulcan-accounts/package.js | 4 ++-- packages/vulcan-admin/package.js | 4 ++-- packages/vulcan-cloudinary/package.js | 4 ++-- packages/vulcan-core/package.js | 14 +++++++------- packages/vulcan-debug/package.js | 6 +++--- packages/vulcan-email/package.js | 4 ++-- packages/vulcan-embed/package.js | 4 ++-- packages/vulcan-errors-sentry/package.js | 4 ++-- packages/vulcan-errors/package.js | 4 ++-- packages/vulcan-events-ga/package.js | 4 ++-- packages/vulcan-events-intercom/package.js | 4 ++-- packages/vulcan-events-internal/package.js | 4 ++-- packages/vulcan-events-segment/package.js | 4 ++-- packages/vulcan-events/package.js | 4 ++-- packages/vulcan-forms-tags/package.js | 4 ++-- packages/vulcan-forms-upload/package.js | 4 ++-- packages/vulcan-forms/package.js | 4 ++-- packages/vulcan-i18n-en-us/package.js | 4 ++-- packages/vulcan-i18n-es-es/package.js | 4 ++-- packages/vulcan-i18n-fr-fr/package.js | 4 ++-- packages/vulcan-i18n/package.js | 4 ++-- packages/vulcan-lib/lib/modules/config.js | 2 +- packages/vulcan-lib/package.js | 2 +- packages/vulcan-newsletter/package.js | 4 ++-- packages/vulcan-payments/package.js | 4 ++-- packages/vulcan-routing/package.js | 4 ++-- packages/vulcan-subscribe/package.js | 6 +++--- packages/vulcan-ui-bootstrap/package.js | 4 ++-- packages/vulcan-ui-material/package.js | 2 +- packages/vulcan-users/package.js | 4 ++-- packages/vulcan-voting/package.js | 4 ++-- 32 files changed, 67 insertions(+), 67 deletions(-) diff --git a/package.json b/package.json index 5782ced0d..62eaf8b77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Vulcan", - "version": "1.12.17", + "version": "1.13.0", "engines": { "npm": "^3.0" }, diff --git a/packages/vulcan-accounts/package.js b/packages/vulcan-accounts/package.js index fe2016336..28d45d1f5 100755 --- a/packages/vulcan-accounts/package.js +++ b/packages/vulcan-accounts/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'vulcan:accounts', - version: '1.12.17', + version: '1.13.0', summary: 'Accounts UI for React in Meteor 1.3+', git: 'https://github.com/studiointeract/accounts-ui', documentation: 'README.md', @@ -9,7 +9,7 @@ Package.describe({ Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use('vulcan:core@1.12.17'); + api.use('vulcan:core@1.13.0'); api.use('ecmascript'); api.use('tracker'); diff --git a/packages/vulcan-admin/package.js b/packages/vulcan-admin/package.js index 6a4849c03..160a26f0f 100644 --- a/packages/vulcan-admin/package.js +++ b/packages/vulcan-admin/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:admin', summary: 'Vulcan components package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); @@ -12,7 +12,7 @@ Package.onUse(function(api) { 'fourseven:scss@4.10.0', 'dynamic-import@0.1.1', // Vulcan packages - 'vulcan:core@1.12.17', + 'vulcan:core@1.13.0', ]); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-cloudinary/package.js b/packages/vulcan-cloudinary/package.js index 26e2599d2..bdba9e2ac 100644 --- a/packages/vulcan-cloudinary/package.js +++ b/packages/vulcan-cloudinary/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:cloudinary', summary: 'Vulcan file upload package.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17']); + api.use(['vulcan:core@1.13.0']); api.mainModule('lib/client/main.js', 'client'); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-core/package.js b/packages/vulcan-core/package.js index bafb4bba8..1bb3b84ad 100644 --- a/packages/vulcan-core/package.js +++ b/packages/vulcan-core/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:core', summary: 'Vulcan core package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); @@ -9,14 +9,14 @@ Package.onUse(function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:lib@1.12.17', - 'vulcan:i18n@1.12.17', - 'vulcan:users@1.12.17', - 'vulcan:routing@1.12.17', - 'vulcan:debug@1.12.17', + 'vulcan:lib@1.13.0', + 'vulcan:i18n@1.13.0', + 'vulcan:users@1.13.0', + 'vulcan:routing@1.13.0', + 'vulcan:debug@1.13.0', ]); - api.imply(['vulcan:lib@1.12.17']); + api.imply(['vulcan:lib@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-debug/package.js b/packages/vulcan-debug/package.js index 99a5f29c4..1e6153d27 100644 --- a/packages/vulcan-debug/package.js +++ b/packages/vulcan-debug/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:debug', summary: 'Vulcan debug package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', debugOnly: true, }); @@ -15,8 +15,8 @@ Package.onUse(function(api) { // Vulcan packages - 'vulcan:lib@1.12.17', - 'vulcan:email@1.12.17', + 'vulcan:lib@1.13.0', + 'vulcan:email@1.13.0', ]); api.addFiles(['lib/stylesheets/debug.scss'], ['client']); diff --git a/packages/vulcan-email/package.js b/packages/vulcan-email/package.js index a8f1f0eee..0b583e12c 100644 --- a/packages/vulcan-email/package.js +++ b/packages/vulcan-email/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:email', summary: 'Vulcan email package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:lib@1.12.17']); + api.use(['vulcan:lib@1.13.0']); api.mainModule('lib/server.js', 'server'); api.mainModule('lib/client.js', 'client'); diff --git a/packages/vulcan-embed/package.js b/packages/vulcan-embed/package.js index 7ff649f27..6d42f14e7 100644 --- a/packages/vulcan-embed/package.js +++ b/packages/vulcan-embed/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:embed', summary: 'Vulcan Embed package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['http', 'vulcan:core@1.12.17', 'fourseven:scss@4.10.0']); + api.use(['http', 'vulcan:core@1.13.0', 'fourseven:scss@4.10.0']); api.addFiles(['lib/stylesheets/embedly.scss'], ['client']); diff --git a/packages/vulcan-errors-sentry/package.js b/packages/vulcan-errors-sentry/package.js index 4a74bd211..f8a669c41 100755 --- a/packages/vulcan-errors-sentry/package.js +++ b/packages/vulcan-errors-sentry/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:errors-sentry', summary: 'Vulcan Sentry error tracking package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['ecmascript', 'vulcan:core@1.12.17', 'vulcan:users@1.12.17', 'vulcan:errors@1.12.17']); + api.use(['ecmascript', 'vulcan:core@1.13.0', 'vulcan:users@1.13.0', 'vulcan:errors@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-errors/package.js b/packages/vulcan-errors/package.js index ece2e44c5..550c34a54 100644 --- a/packages/vulcan-errors/package.js +++ b/packages/vulcan-errors/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:errors', summary: 'Vulcan error tracking package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['ecmascript', 'vulcan:core@1.12.17']); + api.use(['ecmascript', 'vulcan:core@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-events-ga/package.js b/packages/vulcan-events-ga/package.js index e220d3194..13dbf655d 100644 --- a/packages/vulcan-events-ga/package.js +++ b/packages/vulcan-events-ga/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:events-ga', summary: 'Vulcan Google Analytics event tracking package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:events@1.12.17']); + api.use(['vulcan:core@1.13.0', 'vulcan:events@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-events-intercom/package.js b/packages/vulcan-events-intercom/package.js index 8d8a7cdcb..eaf6af7e4 100644 --- a/packages/vulcan-events-intercom/package.js +++ b/packages/vulcan-events-intercom/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:events-intercom', summary: 'Vulcan Intercom integration package.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:events@1.12.17']); + api.use(['vulcan:core@1.13.0', 'vulcan:events@1.13.0']); api.mainModule('lib/client/main.js', 'client'); api.mainModule('lib/server/main.js', 'server'); diff --git a/packages/vulcan-events-internal/package.js b/packages/vulcan-events-internal/package.js index 68fedb9e9..1d8d8f6e1 100644 --- a/packages/vulcan-events-internal/package.js +++ b/packages/vulcan-events-internal/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:events-internal', summary: 'Vulcan internal event tracking package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:events@1.12.17']); + api.use(['vulcan:core@1.13.0', 'vulcan:events@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-events-segment/package.js b/packages/vulcan-events-segment/package.js index 06e413d9b..a455202ee 100644 --- a/packages/vulcan-events-segment/package.js +++ b/packages/vulcan-events-segment/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:events-segment', summary: 'Vulcan Segment', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:events@1.12.17']); + api.use(['vulcan:core@1.13.0', 'vulcan:events@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-events/package.js b/packages/vulcan-events/package.js index 5c59a1e5a..469c906b2 100644 --- a/packages/vulcan-events/package.js +++ b/packages/vulcan-events/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:events', summary: 'Vulcan event tracking package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17']); + api.use(['vulcan:core@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-forms-tags/package.js b/packages/vulcan-forms-tags/package.js index 6da54d2f8..4cf501499 100644 --- a/packages/vulcan-forms-tags/package.js +++ b/packages/vulcan-forms-tags/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:forms-tags', summary: 'Vulcan tag input package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:forms@1.12.17']); + api.use(['vulcan:core@1.13.0', 'vulcan:forms@1.13.0']); api.mainModule('lib/export.js', ['client', 'server']); }); diff --git a/packages/vulcan-forms-upload/package.js b/packages/vulcan-forms-upload/package.js index 2cafe0aa8..3269975de 100755 --- a/packages/vulcan-forms-upload/package.js +++ b/packages/vulcan-forms-upload/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:forms-upload', summary: 'Vulcan package extending vulcan:forms to upload images to Cloudinary from a drop zone.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/xavcz/nova-forms-upload.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:forms@1.12.17', 'fourseven:scss@4.10.0']); + api.use(['vulcan:core@1.13.0', 'vulcan:forms@1.13.0', 'fourseven:scss@4.10.0']); api.addFiles(['lib/Upload.scss'], 'client'); diff --git a/packages/vulcan-forms/package.js b/packages/vulcan-forms/package.js index b42014aca..5fdfc9e36 100644 --- a/packages/vulcan-forms/package.js +++ b/packages/vulcan-forms/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:forms', summary: 'Form containers for React', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/meteor-utilities/react-form-containers.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17']); + api.use(['vulcan:core@1.13.0']); api.mainModule('lib/client/main.js', ['client']); api.mainModule('lib/server/main.js', ['server']); diff --git a/packages/vulcan-i18n-en-us/package.js b/packages/vulcan-i18n-en-us/package.js index 202171609..cc863b098 100644 --- a/packages/vulcan-i18n-en-us/package.js +++ b/packages/vulcan-i18n-en-us/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:i18n-en-us', summary: 'Vulcan i18n package (en_US)', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17']); + api.use(['vulcan:core@1.13.0']); api.addFiles(['lib/en_US.js'], ['client', 'server']); }); diff --git a/packages/vulcan-i18n-es-es/package.js b/packages/vulcan-i18n-es-es/package.js index a742182d6..816480e2e 100644 --- a/packages/vulcan-i18n-es-es/package.js +++ b/packages/vulcan-i18n-es-es/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:i18n-es-es', summary: 'Vulcan i18n package (es_ES)', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17']); + api.use(['vulcan:core@1.13.0']); api.addFiles(['lib/es_ES.js'], ['client', 'server']); }); diff --git a/packages/vulcan-i18n-fr-fr/package.js b/packages/vulcan-i18n-fr-fr/package.js index 2da96b969..253847c73 100644 --- a/packages/vulcan-i18n-fr-fr/package.js +++ b/packages/vulcan-i18n-fr-fr/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:i18n-fr-fr', summary: 'Vulcan i18n package (fr_FR)', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17']); + api.use(['vulcan:core@1.13.0']); api.addFiles(['lib/fr_FR.js'], ['client', 'server']); }); diff --git a/packages/vulcan-i18n/package.js b/packages/vulcan-i18n/package.js index d9e3ae27f..a57538aa8 100644 --- a/packages/vulcan-i18n/package.js +++ b/packages/vulcan-i18n/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:i18n', summary: 'i18n client polyfill', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:lib@1.12.17']); + api.use(['vulcan:lib@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-lib/lib/modules/config.js b/packages/vulcan-lib/lib/modules/config.js index 4ac9c276b..67c8e8a63 100644 --- a/packages/vulcan-lib/lib/modules/config.js +++ b/packages/vulcan-lib/lib/modules/config.js @@ -9,7 +9,7 @@ import SimpleSchema from 'simpl-schema'; Vulcan = {}; // eslint-disable-next-line no-undef -Vulcan.VERSION = '1.12.17'; +Vulcan.VERSION = '1.13.0'; // ------------------------------------- Schemas -------------------------------- // diff --git a/packages/vulcan-lib/package.js b/packages/vulcan-lib/package.js index f2feff9e2..edcd4a486 100644 --- a/packages/vulcan-lib/package.js +++ b/packages/vulcan-lib/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:lib', summary: 'Vulcan libraries.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); diff --git a/packages/vulcan-newsletter/package.js b/packages/vulcan-newsletter/package.js index 08cf8b80a..4a0a59052 100644 --- a/packages/vulcan-newsletter/package.js +++ b/packages/vulcan-newsletter/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:newsletter', summary: 'Vulcan email newsletter package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:core@1.12.17', 'vulcan:email@1.12.17']); + api.use(['vulcan:core@1.13.0', 'vulcan:email@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-payments/package.js b/packages/vulcan-payments/package.js index 6c4c1adac..4e05f2f90 100644 --- a/packages/vulcan-payments/package.js +++ b/packages/vulcan-payments/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:payments', summary: 'Vulcan payments package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['promise', 'vulcan:core@1.12.17', 'fourseven:scss@4.5.4']); + api.use(['promise', 'vulcan:core@1.13.0', 'fourseven:scss@4.5.4']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-routing/package.js b/packages/vulcan-routing/package.js index dfc708ef5..06f65d56a 100644 --- a/packages/vulcan-routing/package.js +++ b/packages/vulcan-routing/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:routing', summary: 'Vulcan router package', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:lib@1.12.17']); + api.use(['vulcan:lib@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-subscribe/package.js b/packages/vulcan-subscribe/package.js index 0951d7e07..1c21c1b8b 100644 --- a/packages/vulcan-subscribe/package.js +++ b/packages/vulcan-subscribe/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:subscribe', summary: 'Subscribe to posts, users, etc. to be notified of new activity', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); @@ -9,11 +9,11 @@ Package.onUse(function(api) { api.versionsFrom('1.6.1'); api.use([ - 'vulcan:core@1.12.17', + 'vulcan:core@1.13.0', // dependencies on posts, categories are done with nested imports to reduce explicit dependencies ]); - api.use(['vulcan:posts@1.12.17', 'vulcan:comments@1.12.17', 'vulcan:categories@1.12.17'], { + api.use(['vulcan:posts@1.13.0', 'vulcan:comments@1.13.0', 'vulcan:categories@1.13.0'], { weak: true, }); diff --git a/packages/vulcan-ui-bootstrap/package.js b/packages/vulcan-ui-bootstrap/package.js index dc984387f..69114840b 100644 --- a/packages/vulcan-ui-bootstrap/package.js +++ b/packages/vulcan-ui-bootstrap/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:ui-bootstrap', summary: 'Vulcan Bootstrap UI components.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:lib@1.12.17', 'fourseven:scss@4.10.0']); + api.use(['vulcan:lib@1.13.0', 'fourseven:scss@4.10.0']); api.addFiles(['lib/stylesheets/style.scss', 'lib/stylesheets/datetime.scss'], 'client'); diff --git a/packages/vulcan-ui-material/package.js b/packages/vulcan-ui-material/package.js index 4759b53f1..5b4caf370 100644 --- a/packages/vulcan-ui-material/package.js +++ b/packages/vulcan-ui-material/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'vulcan:ui-material', - version: '1.12.17', + version: '1.13.0', summary: 'Replacement for Vulcan (http://vulcanjs.org/) components using material-ui', documentation: 'README.md' }); diff --git a/packages/vulcan-users/package.js b/packages/vulcan-users/package.js index a5b953789..d0c05f902 100644 --- a/packages/vulcan-users/package.js +++ b/packages/vulcan-users/package.js @@ -1,14 +1,14 @@ Package.describe({ name: 'vulcan:users', summary: 'Vulcan permissions.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); Package.onUse(function(api) { api.versionsFrom('1.6.1'); - api.use(['vulcan:lib@1.12.17']); + api.use(['vulcan:lib@1.13.0']); api.mainModule('lib/server/main.js', 'server'); api.mainModule('lib/client/main.js', 'client'); diff --git a/packages/vulcan-voting/package.js b/packages/vulcan-voting/package.js index 6430b8ee3..42ca87419 100644 --- a/packages/vulcan-voting/package.js +++ b/packages/vulcan-voting/package.js @@ -1,7 +1,7 @@ Package.describe({ name: 'vulcan:voting', summary: 'Vulcan scoring package.', - version: '1.12.17', + version: '1.13.0', git: 'https://github.com/VulcanJS/Vulcan.git', }); @@ -9,7 +9,7 @@ Package.onUse(function(api) { api.versionsFrom('1.6.1'); api.use( - ['fourseven:scss@4.10.0', 'vulcan:core@1.12.17', 'vulcan:i18n@1.12.17'], + ['fourseven:scss@4.10.0', 'vulcan:core@1.13.0', 'vulcan:i18n@1.13.0'], ['client', 'server'], ); From 1c114cb22c8006001975985c93b48143a3b5908f Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Fri, 29 Mar 2019 10:32:13 +0900 Subject: [PATCH 33/35] Add readme --- packages/vulcan-redux/README.md | 1 + packages/vulcan-styled-components/README.md | 1 + packages/vulcan-test/README.md | 1 + 3 files changed, 3 insertions(+) create mode 100644 packages/vulcan-redux/README.md create mode 100644 packages/vulcan-styled-components/README.md create mode 100644 packages/vulcan-test/README.md diff --git a/packages/vulcan-redux/README.md b/packages/vulcan-redux/README.md new file mode 100644 index 000000000..a6b3fd7ef --- /dev/null +++ b/packages/vulcan-redux/README.md @@ -0,0 +1 @@ +Redux package. \ No newline at end of file diff --git a/packages/vulcan-styled-components/README.md b/packages/vulcan-styled-components/README.md new file mode 100644 index 000000000..95409a7dc --- /dev/null +++ b/packages/vulcan-styled-components/README.md @@ -0,0 +1 @@ +Styled components package. \ No newline at end of file diff --git a/packages/vulcan-test/README.md b/packages/vulcan-test/README.md new file mode 100644 index 000000000..05740f98f --- /dev/null +++ b/packages/vulcan-test/README.md @@ -0,0 +1 @@ +Test package. \ No newline at end of file From 6a8a83234fb4daaddbb2eedbf7af96a668f9ba70 Mon Sep 17 00:00:00 2001 From: juliensl <43228020+juliensl@users.noreply.github.com> Date: Wed, 3 Apr 2019 11:39:26 +0200 Subject: [PATCH 34/35] Update mutators.js add field to properties --- packages/vulcan-lib/lib/server/mutators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vulcan-lib/lib/server/mutators.js b/packages/vulcan-lib/lib/server/mutators.js index a64441583..202644404 100644 --- a/packages/vulcan-lib/lib/server/mutators.js +++ b/packages/vulcan-lib/lib/server/mutators.js @@ -77,7 +77,7 @@ export const createMutator = async ({ Note: keep newDocument for backwards compatibility */ - const properties = { data, currentUser, collection, context, document, newDocument: document, schema }; + const properties = { data, originalData: clone(data), currentUser, collection, context, document, newDocument: document, schema }; /* From ee5907535392e75f8df3b13827b8e2ccb36456d9 Mon Sep 17 00:00:00 2001 From: SachaG <358832+SachaG@users.noreply.github.com> Date: Thu, 4 Apr 2019 09:51:55 +0900 Subject: [PATCH 35/35] use registerComponent instead of replaceComponent for FormElement --- .../vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx b/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx index c88b238d1..73eaf4527 100644 --- a/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx +++ b/packages/vulcan-ui-bootstrap/lib/components/forms/FormElement.jsx @@ -1,5 +1,5 @@ // import React from 'react'; import Form from 'react-bootstrap/Form'; -import { replaceComponent } from 'meteor/vulcan:core'; +import { registerComponent } from 'meteor/vulcan:core'; -replaceComponent('FormElement', Form); +registerComponent('FormElement', Form);