forked from ovh/manager
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add `@ovh-ux/ng-ovh-uirouter-layout` package
- Loading branch information
Showing
9 changed files
with
3,919 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
BSD 3-Clause License | ||
|
||
Copyright (c) 2013-present, OVH SAS | ||
All rights reserved. | ||
|
||
Redistribution and use in source and binary forms, with or without | ||
modification, are permitted provided that the following conditions are met: | ||
|
||
* Redistributions of source code must retain the above copyright notice, this | ||
list of conditions and the following disclaimer. | ||
|
||
* Redistributions in binary form must reproduce the above copyright notice, | ||
this list of conditions and the following disclaimer in the documentation | ||
and/or other materials provided with the distribution. | ||
|
||
* Neither the name of the copyright holder nor the names of its | ||
contributors may be used to endorse or promote products derived from | ||
this software without specific prior written permission. | ||
|
||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# ng-ovh-uirouter-layout | ||
|
||
> Support layout:modal when using ui-router | ||
[![Downloads](https://badgen.net/npm/dt/@ovh-ux/ng-ovh-uirouter-layout)](https://npmjs.com/package/@ovh-ux/ng-ovh-uirouter-layout) [![Dependencies](https://badgen.net/david/dep/ovh-ux/manager/packages/manager/modules/ng-ovh-uirouter-layout)](https://npmjs.com/package/@ovh-ux/ng-ovh-uirouter-layout?activeTab=dependencies) [![Dev Dependencies](https://badgen.net/david/dev/ovh-ux/manager/packages/manager/modules/ng-ovh-uirouter-layout)](https://npmjs.com/package/@ovh-ux/ng-ovh-uirouter-layout?activeTab=dependencies) [![Gitter](https://badgen.net/badge/gitter/ovh-ux/blue?icon=gitter)](https://gitter.im/ovh/ux) | ||
|
||
## Install | ||
|
||
```sh | ||
yarn add @ovh-ux/ng-ovh-uirouter-layout | ||
``` | ||
## Usage | ||
|
||
```js | ||
import angular from 'angular'; | ||
import ngOvhUiRouterLayout from '@ovh-ux/ng-ovh-uirouter-layout'; | ||
|
||
angular | ||
.module('myApp', [ | ||
ngOvhUiRouterLayout, | ||
]); | ||
``` | ||
|
||
## Test | ||
|
||
```sh | ||
yarn test | ||
``` | ||
|
||
## Contributing | ||
|
||
Always feel free to help out! Whether it's [filing bugs and feature requests](https://github.com/ovh-ux/manager/issues/new) or working on some of the [open issues](https://github.com/ovh-ux/manager/issues), our [contributing guide](CONTRIBUTING.md) will help get you started. | ||
|
||
## License | ||
|
||
[BSD-3-Clause](LICENSE) © OVH SAS |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"name": "@ovh-ux/ng-ovh-uirouter-layout", | ||
"version": "0.0.0", | ||
"description": "UiRouter Layout support", | ||
"keywords": [ | ||
"uirouter", | ||
"layout", | ||
"ovh" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/ovh-ux/manager.git", | ||
"directory": "packages/components/ng-ovh-uirouter-layout" | ||
}, | ||
"license": "BSD-3-Clause", | ||
"author": "OVH SAS", | ||
"files": [ | ||
"dist" | ||
], | ||
"main": "./dist/cjs/index.js", | ||
"module": "./dist/esm/index.js", | ||
"browser": "./dist/umd/ng-ovh-uirouter-layout.js", | ||
"scripts": { | ||
"build": "rollup -c --environment BUILD:production", | ||
"dev": "rollup -c --environment BUILD:development", | ||
"dev:watch": "yarn run dev --watch", | ||
"prepare": "yarn run build", | ||
"start": "lerna exec --stream --scope='@ovh-ux/ng-ovh-uirouter-layout' --include-filtered-dependencies -- yarn run build", | ||
"start:dev": "lerna exec --stream --scope='@ovh-ux/ng-ovh-uirouter-layout' --include-filtered-dependencies -- yarn run dev", | ||
"start:watch": "lerna exec --stream --parallel --scope='@ovh-ux/ng-ovh-uirouter-layout' --include-filtered-dependencies -- yarn run dev:watch" | ||
}, | ||
"dependencies": { | ||
"lodash": "^4.17.11" | ||
}, | ||
"devDependencies": { | ||
"@ovh-ux/component-rollup-config": "^5.0.0-beta.9" | ||
}, | ||
"peerDependencies": { | ||
"@uirouter/angularjs": "^1.0.22", | ||
"angular": "^1.5.0", | ||
"angular-ui-bootstrap": "~1.3.3" | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
packages/components/ng-ovh-uirouter-layout/rollup.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import path from 'path'; | ||
import rollupConfig from '@ovh-ux/component-rollup-config'; | ||
|
||
const config = rollupConfig({ | ||
input: './src/index.js', | ||
}, { | ||
lessTildeImporter: { | ||
paths: [ | ||
path.resolve(__dirname, 'node_modules'), | ||
path.resolve(__dirname, '../../../../node_modules'), | ||
], | ||
}, | ||
}); | ||
|
||
export default [ | ||
config.cjs(), | ||
config.es(), | ||
config.umd({ | ||
output: { | ||
globals: { | ||
angular: 'angular', | ||
}, | ||
}, | ||
}), | ||
]; |
196 changes: 196 additions & 0 deletions
196
packages/components/ng-ovh-uirouter-layout/src/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
import angular from 'angular'; | ||
import '@uirouter/angularjs'; | ||
import 'angular-ui-bootstrap'; | ||
|
||
import forEach from 'lodash/forEach'; | ||
import filter from 'lodash/filter'; | ||
import get from 'lodash/get'; | ||
import intersection from 'lodash/intersection'; | ||
import isString from 'lodash/isString'; | ||
import isObject from 'lodash/isObject'; | ||
import kebabCase from 'lodash/kebabCase'; | ||
import last from 'lodash/last'; | ||
import map from 'lodash/map'; | ||
import reduce from 'lodash/reduce'; | ||
import set from 'lodash/set'; | ||
import size from 'lodash/size'; | ||
import startsWith from 'lodash/startsWith'; | ||
import xor from 'lodash/xor'; | ||
|
||
const moduleName = 'ngOvhUiRouterLayout'; | ||
|
||
angular | ||
.module(moduleName, [ | ||
'ui.bootstrap', | ||
'ui.router', | ||
]) | ||
.config(/* @ngInject */($stateProvider, $transitionsProvider, $injector) => { | ||
let modalInstance = null; | ||
|
||
/** | ||
* Create a decorator for our new state attribute 'layout'. | ||
* For modal layout, the attribute can be a string or an object: | ||
* - if string - value must be 'modal' | ||
* - if object - avaiable attributes are: | ||
* - name: the value must be 'modal'. | ||
* - toChilds (default value: 'false'): | ||
* - can be a boolean: 'true' to declare the modal state to all direct parent childs. | ||
* - or an array of string that contains the childs of the direct parents where the modal | ||
* needs to be displayed. | ||
* - ignoreChilds: an array of string that contains states names where the modal state | ||
* doesn't need to be displayed. | ||
*/ | ||
$stateProvider.decorator('layout', (state) => { | ||
let modalLayout; | ||
const layout = get(state, 'self.layout'); | ||
if ((isString(layout) && layout === 'modal') | ||
|| (isObject(layout) && get(layout, 'name') === 'modal')) { | ||
modalLayout = { | ||
name: 'modal', | ||
toChilds: state.self.layout.toChilds || false, | ||
ignoreChilds: state.self.layout.ignoreChilds || [], | ||
redirectTo: state.self.layout.redirectTo || '^', | ||
}; | ||
} | ||
|
||
return modalLayout; | ||
}); | ||
|
||
/** | ||
* Use onSuccess hook to manage the modal display. | ||
*/ | ||
$transitionsProvider.onSuccess({}, (transition) => { | ||
transition.promise.finally(() => { | ||
const state = transition.$to(); | ||
|
||
// close previous modal | ||
if (modalInstance) { | ||
modalInstance.close(); | ||
} | ||
|
||
if (get(state, 'layout.name') === 'modal') { | ||
const $state = transition.injector().get('$state'); | ||
const $uibModal = transition.injector().get('$uibModal'); | ||
|
||
const componentName = get(state, state.views.modal ? 'views.modal.component' : 'component'); | ||
if (componentName && isString(componentName)) { | ||
const directives = $injector.get(`${componentName}DirectiveProvider`).$get(); | ||
// look for those directives that are components | ||
const candidateDirectives = directives.filter( | ||
directiveInfo => directiveInfo.controller | ||
&& directiveInfo.controllerAs | ||
&& directiveInfo.restrict === 'E', | ||
); | ||
|
||
if (candidateDirectives.length === 0) { | ||
throw new Error('No component found'); | ||
} | ||
if (candidateDirectives.length > 1) { | ||
throw new Error('Too many components found'); | ||
} | ||
// get the info of the component | ||
const [directiveInfo] = candidateDirectives; | ||
|
||
// create controller | ||
const resolves = reduce( | ||
transition.getResolveTokens(), | ||
(acc, resolveKey) => ({ | ||
...acc, | ||
[resolveKey]: transition.injector().get(resolveKey), | ||
}), | ||
{}, | ||
); | ||
const controller = () => resolves; | ||
|
||
// get resolveKeys compatibles with component bindings | ||
const resolveKeys = intersection( | ||
transition.getResolveTokens(), | ||
Object.keys(directiveInfo.bindToController), | ||
); | ||
|
||
// create template | ||
const div = document.createElement('div'); | ||
const elmt = document.createElement(kebabCase(directiveInfo.name)); | ||
forEach(resolveKeys, key => elmt.setAttribute(kebabCase(key), `$ctrl.${key}`)); | ||
div.appendChild(elmt); | ||
const template = div.innerHTML; | ||
|
||
modalInstance = $uibModal.open({ | ||
template, | ||
controller, | ||
controllerAs: '$ctrl', | ||
}); | ||
} else { | ||
modalInstance = $uibModal.open({ | ||
templateUrl: get(state, state.views.modal ? 'views.modal.templateUrl' : 'templateUrl'), | ||
template: get(state, state.views.modal ? 'views.modal.template' : 'template'), | ||
controller: get(state, state.views.modal ? 'views.modal.controller' : 'controller'), | ||
controllerAs: get(state, state.views.modal ? 'views.modal.controllerAs' : 'controllerAs', '$ctrl'), | ||
}); | ||
} | ||
// if backdrop is clicked - be sure to close the modal | ||
modalInstance.result.catch(() => $state.go(get(state, 'layout.redirectTo'))); | ||
} | ||
}); | ||
}); | ||
}) | ||
.run(/* @ngInject */($stateRegistry) => { | ||
/** | ||
* As initial URL synchronization is delayed we can check all modal layout states and check | ||
* if toChilds attribute is setted. | ||
* For these states we will need to create new states as follow: | ||
* We will take the direct parent of the modal layout state and create new states with the same | ||
* layout configuration to all of its child states. | ||
*/ | ||
const layoutStates = filter( | ||
$stateRegistry.states, | ||
({ layout }) => get(layout, 'toChilds') === true || size(get(layout, 'toChilds', [])), | ||
); | ||
|
||
const getChildStates = parentStateName => filter( | ||
$stateRegistry.states, | ||
({ name }) => startsWith(name, `${parentStateName}.`), | ||
); | ||
const getChildStatesNames = parentStateName => map(getChildStates(parentStateName), 'name'); | ||
|
||
layoutStates.forEach((layoutState) => { | ||
let childStates; | ||
|
||
// build child states that need modal layout applied | ||
if (angular.isArray(layoutState.layout.toChilds)) { | ||
childStates = layoutState.layout.toChilds; | ||
} else { | ||
childStates = getChildStatesNames(layoutState.parent.name); | ||
} | ||
|
||
// build child states that need to be ignored | ||
// 1st: all child states of each states that need to be ignored | ||
// 2nd: current layout state doesn't need to have itself as modal child | ||
layoutState.layout.ignoreChilds.forEach((childState) => { | ||
set(layoutState, 'layout.ignoreChilds', layoutState.layout.ignoreChilds.concat(getChildStatesNames(childState))); | ||
}); | ||
layoutState.layout.ignoreChilds.push(layoutState.name); | ||
|
||
// remove child states that need to be ignored | ||
childStates = xor(childStates, layoutState.layout.ignoreChilds); | ||
|
||
// create child state with layout settings applied | ||
const modalSateSuffix = last(layoutState.name.split('.')); | ||
childStates.forEach((childState) => { | ||
$stateRegistry.register({ | ||
name: `${childState}.${modalSateSuffix}`, | ||
url: layoutState.self.url, | ||
templateUrl: layoutState.self.templateUrl, | ||
controller: layoutState.self.controller, | ||
component: layoutState.self.component, | ||
layout: { // don't know why full config must be defined??? | ||
name: 'modal', | ||
toChilds: false, | ||
ignoreChilds: [], | ||
}, | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
export default moduleName; |
Oops, something went wrong.