mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
10 Commits
fix/transl
...
motion
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ede6289bc6 | ||
|
|
f077f0aac7 | ||
|
|
9c64308f0d | ||
|
|
795783311a | ||
|
|
f3e8d4c31d | ||
|
|
7fb38d1e12 | ||
|
|
9f9ba235ee | ||
|
|
28c5e14434 | ||
|
|
a4be67aeb8 | ||
|
|
94c3d481e9 |
366
.circleci/config.yml
Normal file
366
.circleci/config.yml
Normal file
@@ -0,0 +1,366 @@
|
||||
version: 2
|
||||
|
||||
aliases:
|
||||
- &restore-cache
|
||||
keys:
|
||||
- dependency-cache-{{ checksum "package.json" }}-3
|
||||
|
||||
- &save-cache
|
||||
key: dependency-cache-{{ checksum "package.json" }}-3
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
- &restore-cache-core
|
||||
keys:
|
||||
- dependency-cache-{{ checksum "core/package.json" }}-3
|
||||
|
||||
- &save-cache-core
|
||||
key: dependency-cache-{{ checksum "core/package.json" }}-3
|
||||
paths:
|
||||
- core/node_modules
|
||||
|
||||
- &restore-cache-core-stencil
|
||||
keys:
|
||||
- stencil-cache-2
|
||||
|
||||
- &save-cache-core-stencil
|
||||
key: stencil-cache-2
|
||||
paths:
|
||||
- core/.stencil
|
||||
- core/screenshot/images
|
||||
|
||||
defaults: &defaults
|
||||
docker:
|
||||
- image: circleci/node:10-browsers
|
||||
working_directory: /tmp/workspace
|
||||
|
||||
jobs:
|
||||
build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache: *restore-cache
|
||||
- run: npm install
|
||||
- save_cache: *save-cache
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build-core:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache: *restore-cache-core
|
||||
- restore_cache: *restore-cache-core-stencil
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/core
|
||||
- save_cache: *save-cache-core
|
||||
- run:
|
||||
command: npm run build -- --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
- save_cache: *save-cache-core-stencil
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build-angular:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/angular
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/core
|
||||
- run:
|
||||
command: sudo npm link @ionic/core
|
||||
working_directory: /tmp/workspace/angular
|
||||
- run:
|
||||
command: npm run build
|
||||
working_directory: /tmp/workspace/angular
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build-angular-server:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/packages/angular-server
|
||||
- run:
|
||||
command: npm run build.prod
|
||||
working_directory: /tmp/workspace/packages/angular-server
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build-react:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/core
|
||||
- run:
|
||||
command: sudo npm link @ionic/core
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- run:
|
||||
command: npm run build
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
build-react-router:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/core
|
||||
- run:
|
||||
command: sudo npm link @ionic/core
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- run:
|
||||
command: sudo npm link @ionic/react
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
- run:
|
||||
command: npm run build
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
paths:
|
||||
- "*"
|
||||
|
||||
test-core-clean-build:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Checking clean build
|
||||
command: git diff --exit-code
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-lint:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run lint
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-e2e:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run test.e2e --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-spec:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run test.spec --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-treeshake:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run test.treeshake --ci
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-screenshot:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Run Screenshot
|
||||
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci || true
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-core-screenshot-master:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Run Screenshot
|
||||
command: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --update-screenshot || true
|
||||
working_directory: /tmp/workspace/core
|
||||
|
||||
test-angular-lint:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run lint
|
||||
working_directory: /tmp/workspace/angular
|
||||
|
||||
test-react-lint:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run lint
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
|
||||
test-react-router-lint:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm run lint
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
|
||||
test-react-spec:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/core
|
||||
- run:
|
||||
command: sudo npm link @ionic/core
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- run:
|
||||
command: npm run test.spec
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
|
||||
test-react-router-spec:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/core
|
||||
- run:
|
||||
command: sudo npm link @ionic/core
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- run:
|
||||
command: sudo npm link
|
||||
working_directory: /tmp/workspace/packages/react
|
||||
- run:
|
||||
command: sudo npm link @ionic/react
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
- run:
|
||||
command: npm run test.spec
|
||||
working_directory: /tmp/workspace/packages/react-router
|
||||
|
||||
test-angular-e2e:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
command: npm install
|
||||
working_directory: /tmp/workspace/angular/test/test-app
|
||||
- run:
|
||||
command: npm test
|
||||
working_directory: /tmp/workspace/angular/test/test-app
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build:
|
||||
jobs:
|
||||
- build
|
||||
- build-core:
|
||||
requires: [build]
|
||||
- test-core-clean-build:
|
||||
requires: [build-core]
|
||||
- test-core-lint:
|
||||
requires: [build-core]
|
||||
- test-core-e2e:
|
||||
requires: [build-core]
|
||||
- test-core-spec:
|
||||
requires: [build-core]
|
||||
- test-core-treeshake:
|
||||
requires: [build-core]
|
||||
- test-core-screenshot:
|
||||
requires: [build-core]
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
- test-core-screenshot-master:
|
||||
requires: [build-core]
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
|
||||
- build-angular:
|
||||
requires: [build-core]
|
||||
- build-angular-server:
|
||||
requires: [build-angular]
|
||||
- build-react:
|
||||
requires: [build-core]
|
||||
- build-react-router:
|
||||
requires: [build-core, build-react]
|
||||
- test-react-lint:
|
||||
requires: [build-react]
|
||||
- test-react-router-lint:
|
||||
requires: [build-react-router]
|
||||
- test-react-spec:
|
||||
requires: [build-react]
|
||||
- test-react-router-spec:
|
||||
requires: [build-react-router]
|
||||
- test-angular-lint:
|
||||
requires: [build-angular]
|
||||
- test-angular-e2e:
|
||||
requires:
|
||||
- build-angular
|
||||
- build-angular-server
|
||||
15
.github/CODEOWNERS
vendored
15
.github/CODEOWNERS
vendored
@@ -1,15 +0,0 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# More details are here: https://help.github.com/articles/about-codeowners/
|
||||
|
||||
# The '*' pattern is global owners.
|
||||
|
||||
# Order is important. The last matching pattern has the most precedence.
|
||||
# The folders are ordered as follows:
|
||||
|
||||
# In each subsection folders are ordered first by depth, then alphabetically.
|
||||
# This should make it easy to add new rules without breaking existing ones.
|
||||
|
||||
# Global owners
|
||||
* @ionic-team/framework-core
|
||||
329
.github/COMPONENT-GUIDE.md
vendored
329
.github/COMPONENT-GUIDE.md
vendored
@@ -9,15 +9,10 @@
|
||||
* [Ripple Effect](#ripple-effect)
|
||||
* [Example Components](#example-components)
|
||||
* [References](#references)
|
||||
- [Accessibility](#accessibility)
|
||||
* [Checkbox](#checkbox)
|
||||
* [Switch](#switch)
|
||||
* [Accordion](#accordion)
|
||||
- [Rendering Anchor or Button](#rendering-anchor-or-button)
|
||||
* [Example Components](#example-components-1)
|
||||
* [Component Structure](#component-structure-1)
|
||||
- [Converting Scoped to Shadow](#converting-scoped-to-shadow)
|
||||
- [RTL](#rtl)
|
||||
|
||||
## Button States
|
||||
|
||||
@@ -357,9 +352,9 @@ ion-ripple-effect {
|
||||
|
||||
### Example Components
|
||||
|
||||
- [ion-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/button)
|
||||
- [ion-back-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/back-button)
|
||||
- [ion-menu-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/menu-button)
|
||||
- [ion-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/button)
|
||||
- [ion-back-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/back-button)
|
||||
- [ion-menu-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/menu-button)
|
||||
|
||||
### References
|
||||
|
||||
@@ -367,289 +362,17 @@ ion-ripple-effect {
|
||||
- [iOS Buttons](https://developer.apple.com/design/human-interface-guidelines/ios/controls/buttons/)
|
||||
|
||||
|
||||
## Accessibility
|
||||
|
||||
### Checkbox
|
||||
|
||||
#### Example Components
|
||||
|
||||
- [ion-checkbox](https://github.com/ionic-team/ionic/tree/main/core/src/components/checkbox)
|
||||
|
||||
#### VoiceOver
|
||||
|
||||
In order for VoiceOver to work properly with a checkbox component there must be a native `input` with `type="checkbox"`, and `aria-checked` and `role="checkbox"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the checkbox is disabled, preventing iOS users from selecting it:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### NVDA
|
||||
|
||||
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Labels
|
||||
|
||||
A helper function has been created to get the proper `aria-label` for the checkbox. This can be imported as `getAriaLabel` like the following:
|
||||
|
||||
```tsx
|
||||
const { label, labelId, labelText } = getAriaLabel(el, inputId);
|
||||
```
|
||||
|
||||
where `el` and `inputId` are the following:
|
||||
|
||||
```tsx
|
||||
export class Checkbox implements ComponentInterface {
|
||||
private inputId = `ion-cb-${checkboxIds++}`;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This can then be added to the `Host` like the following:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
```
|
||||
|
||||
In addition to that, the checkbox input should have a label added:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="checkbox"
|
||||
>
|
||||
<label htmlFor={inputId}>
|
||||
{labelText}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
id={inputId}
|
||||
/>
|
||||
```
|
||||
|
||||
#### Hidden Input
|
||||
|
||||
A helper function to render a hidden input has been added, it can be added in the `render`:
|
||||
|
||||
```tsx
|
||||
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
|
||||
```
|
||||
|
||||
> This is required for the checkbox to work with forms.
|
||||
|
||||
#### Known Issues
|
||||
|
||||
When using VoiceOver on macOS, Chrome will announce the following when you are focused on a checkbox:
|
||||
|
||||
```
|
||||
currently on a checkbox inside of a checkbox
|
||||
```
|
||||
|
||||
This is a compromise we have to make in order for it to work with the other screen readers & Safari.
|
||||
|
||||
|
||||
### Switch
|
||||
|
||||
#### Example Components
|
||||
|
||||
- [ion-toggle](https://github.com/ionic-team/ionic/tree/main/core/src/components/toggle)
|
||||
|
||||
#### Voiceover
|
||||
|
||||
In order for VoiceOver to work properly with a switch component there must be a native `input` with `type="checkbox"` and `role="switch"`, and `aria-checked` and `role="switch"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the switch is disabled, preventing iOS users from selecting it:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### NVDA
|
||||
|
||||
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
|
||||
|
||||
```tsx
|
||||
render() {
|
||||
const { checked, disabled } = this;
|
||||
|
||||
return (
|
||||
<Host
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
/>
|
||||
...
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### Labels
|
||||
|
||||
A helper function has been created to get the proper `aria-label` for the switch. This can be imported as `getAriaLabel` like the following:
|
||||
|
||||
```tsx
|
||||
const { label, labelId, labelText } = getAriaLabel(el, inputId);
|
||||
```
|
||||
|
||||
where `el` and `inputId` are the following:
|
||||
|
||||
```tsx
|
||||
export class Toggle implements ComponentInterface {
|
||||
private inputId = `ion-tg-${toggleIds++}`;
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
This can then be added to the `Host` like the following:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
```
|
||||
|
||||
In addition to that, the checkbox input should have a label added:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
aria-labelledby={label ? labelId : null}
|
||||
aria-checked={`${checked}`}
|
||||
aria-hidden={disabled ? 'true' : null}
|
||||
role="switch"
|
||||
>
|
||||
<label htmlFor={inputId}>
|
||||
{labelText}
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-checked={`${checked}`}
|
||||
disabled={disabled}
|
||||
id={inputId}
|
||||
/>
|
||||
```
|
||||
|
||||
|
||||
#### Hidden Input
|
||||
|
||||
A helper function to render a hidden input has been added, it can be added in the `render`:
|
||||
|
||||
```tsx
|
||||
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
|
||||
```
|
||||
|
||||
> This is required for the switch to work with forms.
|
||||
|
||||
|
||||
#### Known Issues
|
||||
|
||||
When using VoiceOver on macOS or iOS, Chrome will announce the switch as a checked or unchecked `checkbox`:
|
||||
|
||||
```
|
||||
You are currently on a switch. To select or deselect this checkbox, press Control-Option-Space.
|
||||
```
|
||||
|
||||
There is a WebKit bug open for this: https://bugs.webkit.org/show_bug.cgi?id=196354
|
||||
|
||||
### Accordion
|
||||
|
||||
#### Example Components
|
||||
|
||||
- [ion-accordion](https://github.com/ionic-team/ionic/tree/master/core/src/components/accordion)
|
||||
- [ion-accordion-group](https://github.com/ionic-team/ionic/tree/master/core/src/components/accordion-group)
|
||||
|
||||
#### NVDA
|
||||
|
||||
In order to use the arrow keys to navigate the accordions, users must be in "Focus Mode". Typically, NVDA automatically switches between Browse and Focus modes when inside of a form, but not every accordion needs a form.
|
||||
|
||||
You can either wrap your `ion-accordion-group` in a form, or manually toggle Focus Mode using NVDA's keyboard shortcut.
|
||||
|
||||
|
||||
## Rendering Anchor or Button
|
||||
|
||||
Certain components can render an `<a>` or a `<button>` depending on the presence of an `href` attribute.
|
||||
|
||||
### Example Components
|
||||
|
||||
- [ion-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/button)
|
||||
- [ion-card](https://github.com/ionic-team/ionic/tree/main/core/src/components/card)
|
||||
- [ion-fab-button](https://github.com/ionic-team/ionic/tree/main/core/src/components/fab-button)
|
||||
- [ion-item-option](https://github.com/ionic-team/ionic/tree/main/core/src/components/item-option)
|
||||
- [ion-item](https://github.com/ionic-team/ionic/tree/main/core/src/components/item)
|
||||
- [ion-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/button)
|
||||
- [ion-card](https://github.com/ionic-team/ionic/tree/master/core/src/components/card)
|
||||
- [ion-fab-button](https://github.com/ionic-team/ionic/tree/master/core/src/components/fab-button)
|
||||
- [ion-item-option](https://github.com/ionic-team/ionic/tree/master/core/src/components/item-option)
|
||||
- [ion-item](https://github.com/ionic-team/ionic/tree/master/core/src/components/item)
|
||||
|
||||
### Component Structure
|
||||
|
||||
@@ -718,39 +441,3 @@ There will be some CSS issues when converting to shadow. Below are some of the d
|
||||
/* IN SHADOW*/
|
||||
:host-context(ion-toolbar:not(.ion-color)):host(:not(.ion-color)) ::slotted(ion-segment-button) {
|
||||
```
|
||||
|
||||
## RTL
|
||||
|
||||
When you need to support both LTR and RTL modes, try to avoid using values such as `left` and `right`. For certain CSS properties, you can use the appropriate mixin to have this handled for you automatically.
|
||||
|
||||
For example, if you wanted `transform-origin` to be RTL-aware, you would use the `transform-origin` mixin:
|
||||
|
||||
```css
|
||||
@include transform-origin(start, center);
|
||||
```
|
||||
|
||||
This would output `transform-origin: left center` in LTR mode and `transform-origin: right center` in RTL mode.
|
||||
|
||||
These mixins depend on the `:host-context` pseudo-class when used inside of shadow components, which is not supported in WebKit. As a result, these mixins will not work in Safari for macOS and iOS when applied to shadow components.
|
||||
|
||||
To work around this, you should set an RTL class on the host of your component and set your RTL styles by targeting that class:
|
||||
|
||||
```tsx
|
||||
<Host
|
||||
class={{
|
||||
'my-cmp-rtl': document.dir === 'rtl'
|
||||
})
|
||||
>
|
||||
...
|
||||
</Host>
|
||||
```
|
||||
|
||||
```css
|
||||
:host {
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
:host(.my-cmp-rtl) {
|
||||
transform-origin: right center;
|
||||
}
|
||||
```
|
||||
77
.github/CONTRIBUTING.md
vendored
77
.github/CONTRIBUTING.md
vendored
@@ -6,7 +6,6 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
|
||||
- [Creating an Issue](#creating-an-issue)
|
||||
* [Creating a Good Code Reproduction](#creating-a-good-code-reproduction)
|
||||
- [Creating a Pull Request](#creating-a-pull-request)
|
||||
* [Requirements](#requirements)
|
||||
* [Setup](#setup)
|
||||
* [Core](#core)
|
||||
+ [Modifying Components](#modifying-components)
|
||||
@@ -16,12 +15,6 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
|
||||
+ [Modifying Tests](#modifying-tests)
|
||||
- [Screenshot Tests](#screenshot-tests)
|
||||
+ [Building Changes](#building-changes)
|
||||
* [Angular, React, and Vue](#angular-react-and-vue)
|
||||
+ [Modifying Files](#modifying-files)
|
||||
+ [Preview Changes](#preview-changes-1)
|
||||
+ [Lint Changes](#lint-changes-1)
|
||||
+ [Modifying Tests](#modifying-tests-1)
|
||||
+ [Building Changes](#building-changes-1)
|
||||
* [Submit Pull Request](#submit-pull-request)
|
||||
- [Commit Message Guidelines](#commit-message-guidelines)
|
||||
* [Commit Message Format](#commit-message-format)
|
||||
@@ -37,12 +30,12 @@ Thanks for your interest in contributing to the Ionic Framework! :tada:
|
||||
|
||||
## Contributing Etiquette
|
||||
|
||||
Please see our [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/main/CODE_OF_CONDUCT.md) for information on our rules of conduct.
|
||||
Please see our [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/master/CODE_OF_CONDUCT.md) for information on our rules of conduct.
|
||||
|
||||
|
||||
## Creating an Issue
|
||||
|
||||
* If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Discord](https://ionic.link/discord).
|
||||
* If you have a question about using the framework, please ask on the [Ionic Forum](http://forum.ionicframework.com/) or in the [Ionic Worldwide Slack](http://ionicworldwide.herokuapp.com/) group.
|
||||
|
||||
* It is required that you clearly describe the steps necessary to reproduce the issue you are running into. Although we would love to help our users as much as possible, diagnosing issues without clear reproduction steps is extremely time-consuming and simply not sustainable.
|
||||
|
||||
@@ -84,23 +77,17 @@ Without a reliable code reproduction, it is unlikely we will be able to resolve
|
||||
|
||||
## Creating a Pull Request
|
||||
|
||||
Before creating a pull request, please read our requirements that explains the minimal details to have your PR considered and merged into the codebase.
|
||||
* We appreciate you taking the time to contribute! Before submitting a pull request, we ask that you please [create an issue](#creating-an-issue) that explains the bug or feature request and let us know that you plan on creating a pull request for it. If an issue already exists, please comment on that issue letting us know you would like to submit a pull request for it. This helps us to keep track of the pull request and make sure there isn't duplicated effort.
|
||||
|
||||
### Requirements
|
||||
1. PRs must reference an existing issue that describes the issue or feature being submitted.
|
||||
2. PRs must have a reproduction app or the issue must include a reproduction app to verify changes against.
|
||||
3. PRs must include tests covering the changed behavior or a description of why tests cannot be written.
|
||||
* Looking for an issue to fix? Make sure to look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
|
||||
|
||||
> Note: We appreciate you taking the time to contribute! Before submitting a pull request, please take the time to comment on the issue you are wanting to resolve. This helps us prevent duplicate effort or advise if the team is already addressing the issue.
|
||||
|
||||
* Looking for an issue to fix? Look through our issues with the [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label!
|
||||
|
||||
### Setup
|
||||
|
||||
1. [Download the installer](https://nodejs.org/) for the LTS version of Node.js. This is the best way to also [install npm](https://blog.npmjs.org/post/85484771375/how-to-install-npm#_=_).
|
||||
2. Fork this repository.
|
||||
3. Clone your fork.
|
||||
4. Create a new branch from main for your change.
|
||||
4. Create a new branch from master for your change.
|
||||
5. Navigate into the directory of the package you wish to modify (core, angular, etc.).
|
||||
6. Run `npm install` to install dependencies for this package.
|
||||
7. Follow the steps for the specific package below.
|
||||
@@ -173,67 +160,17 @@ Before creating a pull request, please read our requirements that explains the m
|
||||
3. Make sure the build has finished before committing. If you made changes to the documentation, properties, methods, or anything else that requires an update to a generate file, this needs to be committed.
|
||||
4. After the changes have been pushed, publish the branch and [create a pull request](#creating-a-pull-request).
|
||||
|
||||
### Angular, React, and Vue
|
||||
|
||||
#### Modifying Files
|
||||
|
||||
1. Locate the files inside the relevant root directory:
|
||||
- Angular: `/angular/src`
|
||||
- React: `/packages/react/src`
|
||||
- Vue: `/packages/vue/src`
|
||||
2. Make your changes to the files. If the change is overly complex or out of the ordinary, add comments so we can understand the changes.
|
||||
3. Run lint on the directory and make sure there are no errors.
|
||||
4. Build the project.
|
||||
5. After the build is finished, commit the changes. Please follow the [commit message format](#commit-message-format) for every commit.
|
||||
6. [Submit a Pull Request](#submit-pull-request) of your changes.
|
||||
|
||||
|
||||
|
||||
#### Preview Changes
|
||||
|
||||
1. Run `npm run start` inside of the relevant test app directory. This will sync your previously built changes into a test Ionic app:
|
||||
- Angular: `/angular/test-app`
|
||||
- React: `/packages/react/test-app`
|
||||
- Vue: `/packages/vue/test-app`
|
||||
2. In a browser, navigate to the page you wish to test.
|
||||
3. Alternatively, create a new page if you need to test something that is not already there.
|
||||
|
||||
|
||||
#### Lint Changes
|
||||
|
||||
1. Run `npm run lint` to lint the TypeScript in the relevant directory:
|
||||
- Angular: `/angular/src`
|
||||
- React: `/packages/react/src`
|
||||
- Vue: `/packages/vue/src`
|
||||
2. If there are lint errors, run `npm run lint.fix` to automatically fix any errors. Repeat step 1 to ensure the errors have been fixed, and manually fix them if not.
|
||||
|
||||
#### Modifying Tests
|
||||
|
||||
1. Locate the test to modify inside the relevant test app directory:
|
||||
- Angular: `/angular/test-app/e2e/src`
|
||||
- React: `/packages/react/test-app/cypress/integration`
|
||||
- Vue: `/packages/vue/test-app/tests/e2e`
|
||||
2. If a test exists, modify the test by adding an example to reproduce the problem fixed or feature added.
|
||||
3. If a new test is needed, copy an existing test, rename it, and edit the content in the test file.
|
||||
4. Run `npm run test` to run your tests.
|
||||
|
||||
#### Building Changes
|
||||
|
||||
1. Once all changes have been made, run `npm run build` inside of the root directory. This will add your changes to any auto-generated files, if necessary.
|
||||
2. Review the changes and, if everything looks correct, [commit](#commit-message-format) the changes.
|
||||
3. Make sure the build has finished before committing. If you made changes to the documentation, properties, methods, or anything else that requires an update to a generate file, this needs to be committed.
|
||||
4. After the changes have been pushed, publish the branch and [create a pull request](#creating-a-pull-request).
|
||||
|
||||
### Submit Pull Request
|
||||
|
||||
1. [Create a new pull request](https://github.com/ionic-team/ionic/compare) with the `main` branch as the `base`. You may need to click on `compare across forks` to find your changes.
|
||||
1. [Create a new pull request](https://github.com/ionic-team/ionic/compare) with the `master` branch as the `base`. You may need to click on `compare across forks` to find your changes.
|
||||
2. See the [Creating a pull request from a fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) GitHub help article for more information.
|
||||
3. Please fill out the provided Pull Request template to the best of your ability and include any issues that are related.
|
||||
|
||||
|
||||
## Commit Message Guidelines
|
||||
|
||||
We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our [changelog](https://github.com/ionic-team/ionic/blob/main/CHANGELOG.md). Our format closely resembles Angular's [commit message guidelines](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit).
|
||||
We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our [changelog](https://github.com/ionic-team/ionic/blob/master/CHANGELOG.md). Our format closely resembles Angular's [commit message guidelines](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit).
|
||||
|
||||
### Commit Message Format
|
||||
|
||||
|
||||
58
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
58
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us improve
|
||||
title: 'bug: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!-- Before submitting an issue, please consult our docs (https://ionicframework.com/docs/). -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
# Bug Report
|
||||
|
||||
**Ionic version:**
|
||||
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
|
||||
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
|
||||
[x] **4.x**
|
||||
|
||||
**Current behavior:**
|
||||
<!-- Describe how the bug manifests. -->
|
||||
|
||||
**Expected behavior:**
|
||||
<!-- Describe what the behavior would be without the bug. -->
|
||||
|
||||
**Steps to reproduce:**
|
||||
<!-- Please explain the steps required to duplicate the issue, especially if you are able to provide a sample application. -->
|
||||
|
||||
**Related code:**
|
||||
|
||||
<!-- If you are able to illustrate the bug or feature request with an example, please provide a sample application via one of the following means:
|
||||
|
||||
A sample application via GitHub
|
||||
|
||||
StackBlitz (https://stackblitz.com)
|
||||
Ionic Angular StackBlitz: https://stackblitz.com/edit/ionic-v4-angular-tabs
|
||||
|
||||
Plunker (http://plnkr.co/edit/cpeRJs?p=preview)
|
||||
|
||||
-->
|
||||
|
||||
```
|
||||
insert short code snippets here
|
||||
```
|
||||
|
||||
**Other information:**
|
||||
<!-- List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to fix, Stack Overflow links, forum links, etc. -->
|
||||
|
||||
**Ionic info:**
|
||||
<!-- (run `ionic info` from a terminal/cmd prompt and paste output below): -->
|
||||
|
||||
```
|
||||
insert the output from ionic info here
|
||||
```
|
||||
56
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
56
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,56 +0,0 @@
|
||||
name: 🐛 Bug Report
|
||||
description: Create a report to help us improve Ionic Framework
|
||||
title: 'bug: '
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
description: Please ensure you have completed all of the following.
|
||||
options:
|
||||
- label: I have read the [Contributing Guidelines](https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#creating-an-issue).
|
||||
required: true
|
||||
- label: I agree to follow the [Code of Conduct](https://ionicframework.com/code-of-conduct).
|
||||
required: true
|
||||
- label: I have searched for [existing issues](https://github.com/ionic-team/ionic-framework/issues) that already report this problem, without success.
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Ionic Framework Version
|
||||
description: Please select which versions of Ionic Framework this issue impacts. For Ionic Framework 1.x issues, please use https://github.com/ionic-team/ionic-v1. For Ionic Framework 2.x and 3.x issues, please use https://github.com/ionic-team/ionic-v3.
|
||||
options:
|
||||
- label: v4.x
|
||||
- label: v5.x
|
||||
- label: v6.x
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A clear description of what the bug is and how it manifests.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: Please explain the steps required to duplicate this issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Code Reproduction URL
|
||||
description: Please reproduce this issue in a blank Ionic Framework starter application and provide a link to the repo. Try out our [Getting Started Wizard](https://ionicframework.com/start#basics) to quickly spin up an Ionic Framework starter app. This is the best way to ensure this issue is triaged quickly. Issues without a code reproduction may be closed if the Ionic Team cannot reproduce the issue you are reporting.
|
||||
placeholder: https://github.com/...
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Ionic Info
|
||||
description: Please run `ionic info` from within your Ionic Framework project directory and paste the output below.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to fix, Stack Overflow links, forum links, etc.
|
||||
11
.github/ISSUE_TEMPLATE/cli.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/cli.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: CLI
|
||||
about: Suggest an improvement for the CLI
|
||||
title: ''
|
||||
labels: 'ionitron: cli'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# CLI
|
||||
|
||||
Please do not submit bug reports or feature requests related to the Ionic CLI. Instead, please submit an issue to the [Ionic CLI Repository](https://github.com/ionic-team/ionic-cli/issues/new/choose).
|
||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
10
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,10 +0,0 @@
|
||||
contact_links:
|
||||
- name: 📚 Documentation
|
||||
url: https://github.com/ionic-team/ionic-docs/issues/new/choose
|
||||
about: This issue tracker is not for documentation issues. Please file documentation issues on the Ionic Docs repo.
|
||||
- name: 💻 CLI
|
||||
url: https://github.com/ionic-team/ionic-cli/issues/new/choose
|
||||
about: This issue tracker is not for CLI issues. Please file CLI issues on the Ionic CLI repo.
|
||||
- name: 🤔 Support Question
|
||||
url: https://forum.ionicframework.com/
|
||||
about: This issue tracker is not for support questions. Please post your question on the Ionic Forums.
|
||||
11
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Documentation
|
||||
about: Suggest an improvement for the documentation of this project
|
||||
title: ''
|
||||
labels: 'ionitron: docs'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Documentation
|
||||
|
||||
Please do not submit issues on how to improve or fix the documentation. Instead, please submit an issue to the [Ionic Docs Repository](https://github.com/ionic-team/ionic-docs/issues/new/choose).
|
||||
37
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea for this project
|
||||
title: 'feat: '
|
||||
labels: ''
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
<!-- Before submitting an issue, please consult our docs (https://ionicframework.com/docs/). -->
|
||||
|
||||
<!-- Please make sure you are posting an issue pertaining to the Ionic Framework. If you are having an issue with the Ionic Appflow services (Ionic View, Ionic Deploy, etc.) please consult the Ionic Appflow support portal (https://ionic.zendesk.com/hc/en-us) -->
|
||||
|
||||
<!-- Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/ -->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
# Feature Request
|
||||
|
||||
**Ionic version:**
|
||||
<!-- (For Ionic 1.x issues, please use https://github.com/ionic-team/ionic-v1) -->
|
||||
<!-- (For Ionic 2.x & 3.x issues, please use https://github.com/ionic-team/ionic-v3) -->
|
||||
[x] **4.x**
|
||||
|
||||
**Describe the Feature Request**
|
||||
<!-- A clear and concise description of what the feature request is. Please include if your feature request is related to a problem. -->
|
||||
|
||||
**Describe Preferred Solution**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
**Describe Alternatives**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
**Related Code**
|
||||
<!-- If you are able to illustrate the feature request with an example, please provide a sample application via an online code collaborator such as [StackBlitz](https://stackblitz.com), or [GitHub](https://github.com). -->
|
||||
|
||||
**Additional Context**
|
||||
<!-- List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to add, use case, Stack Overflow links, forum links, screenshots, OS if applicable, etc. -->
|
||||
43
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
43
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: 💡 Feature Request
|
||||
description: Suggest an idea for Ionic Framework
|
||||
title: 'feat: '
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Prerequisites
|
||||
description: Please ensure you have completed all of the following.
|
||||
options:
|
||||
- label: I have read the [Contributing Guidelines](https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#creating-an-issue).
|
||||
required: true
|
||||
- label: I agree to follow the [Code of Conduct](https://ionicframework.com/code-of-conduct).
|
||||
required: true
|
||||
- label: I have searched for [existing issues](https://github.com/ionic-team/ionic-framework/issues) that already include this feature request, without success.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Feature Request
|
||||
description: A clear and concise description of what the feature does.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Use Case
|
||||
description: A clear and concise use case for what problem this feature would solve.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe Preferred Solution
|
||||
description: A clear and concise description of what you how you want this feature to be added to Ionic Framework.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe Alternatives
|
||||
description: A clear and concise description of any alternative solutions or features you have considered.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Related Code
|
||||
description: If you are able to illustrate the feature request with an example, please provide a sample Ionic Framework application. Try out our [Getting Started Wizard](https://ionicframework.com/start#basics) to quickly spin up an Ionic Framework starter app.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: List any other information that is relevant to your issue. Stack traces, related issues, suggestions on how to implement, Stack Overflow links, forum links, etc.
|
||||
11
.github/ISSUE_TEMPLATE/support_question.md
vendored
Normal file
11
.github/ISSUE_TEMPLATE/support_question.md
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: Support Question
|
||||
about: Question on how to use this project
|
||||
title: 'support: '
|
||||
labels: 'ionitron: support'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
# Support Question
|
||||
|
||||
Please do not submit support requests or "How to" questions here. Instead, please use one of these channels: https://forum.ionicframework.com/ or http://ionicworldwide.herokuapp.com/
|
||||
74
.github/PROCESS.md
vendored
74
.github/PROCESS.md
vendored
@@ -60,7 +60,7 @@ If the issue is associated with Ionic Appflow the submitter should be told to us
|
||||
|
||||
### Support Questions
|
||||
|
||||
If the issue is a support question, the submitter should be redirected to our [forum](https://forum.ionicframework.com). The issue should be closed and locked. Use the `ionitron: support` label to accomplish this.
|
||||
If the issue is a support question, the submitter should be redirected to our [forum](https://forum.ionicframework.com) or [slack channel](https://ionicworldwide.herokuapp.com/). The issue should be closed and locked. Use the `ionitron: support` label to accomplish this.
|
||||
|
||||
### Incomplete Template
|
||||
|
||||
@@ -77,15 +77,7 @@ NOTE: be sure to perform those actions in the order stated. If you add the comme
|
||||
|
||||
If there is a response to the question, the bot will remove the `needs: reply` and apply the `triage` label. The issue will then go through the triage handling again.
|
||||
|
||||
If there is no response within 14 days, the issue will be closed and locked.
|
||||
|
||||
### Missing Code Reproduction
|
||||
|
||||
If the information the submitter has supplied is not enough for you to reproduce the issue, add the `ionitron: needs reproduction` label. An automated comment will be added to the thread asking the submitter to provide a code reproduction of the issue.
|
||||
|
||||
This label can also be added when the submitter has supplied some code, but not enough for you to reproduce the issue (i.e. code snippets).
|
||||
|
||||
Issues with this label are not automatically closed and locked, so we manually close and lock them if there is no response within 14 days.
|
||||
If there is no response within 30 days, the issue will be closed and locked.
|
||||
|
||||
## Workflow
|
||||
|
||||
@@ -95,81 +87,81 @@ Issues with this label are not automatically closed and locked, so we manually c
|
||||
|
||||
We have two long-living branches:
|
||||
|
||||
- `main`: completed features, bug fixes, refactors, chores
|
||||
- `master`: completed features, bug fixes, refactors, chores
|
||||
- `stable`: the latest release
|
||||
|
||||
The overall flow:
|
||||
|
||||
1. Feature, refactor, and bug fix branches are created from `main`
|
||||
1. When a feature, refactor, or fix is complete it is merged into `main`
|
||||
1. A release branch is created from `main`
|
||||
1. When the release branch is done it is merged into `main` and `stable`
|
||||
1. Feature, refactor, and bug fix branches are created from `master`
|
||||
1. When a feature, refactor, or fix is complete it is merged into `master`
|
||||
1. A release branch is created from `master`
|
||||
1. When the release branch is done it is merged into `master` and `stable`
|
||||
1. If an issue in `stable` is detected a hotfix branch is created from `stable`
|
||||
1. Once the hotfix is complete it is merged to both `main` and `stable`
|
||||
1. All branches should follow the syntax of `{type}-{details}` where `{type}` is the type of branch (`hotfix`, `release`, or one of the [commit types](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format)) and `{details}` is a few hyphen separated words explaining the branch
|
||||
1. Once the hotfix is complete it is merged to both `master` and `stable`
|
||||
1. All branches should follow the syntax of `{type}-{details}` where `{type}` is the type of branch (`hotfix`, `release`, or one of the [commit types](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format)) and `{details}` is a few hyphen separated words explaining the branch
|
||||
|
||||
### Stable and Main Branches
|
||||
### Stable and Master Branches
|
||||
|
||||
#### Stable Branch
|
||||
|
||||
Branches created from `stable`:
|
||||
|
||||
The following branch should be merged back to **both** `main` and `stable`:
|
||||
The following branch should be merged back to **both** `master` and `stable`:
|
||||
|
||||
- A `hotfix` branch (e.g. `hotfix-missing-export`): a bug fix that is fixing a regression or issue with a published release
|
||||
|
||||
A `hotfix` branch should be the **only** branch that is created from stable.
|
||||
|
||||
#### Main Branch
|
||||
#### Master Branch
|
||||
|
||||
Branches created from `main`:
|
||||
Branches created from `master`:
|
||||
|
||||
The following branches should be merged back to `main` via a pull request:
|
||||
The following branches should be merged back to `master` via a pull request:
|
||||
|
||||
1. A feature branch (e.g. `feat-desktop-support`): an addition to the API that is not a bug fix or regression fix
|
||||
1. A bug fix branch (e.g. `fix-tab-color`): a bug fix that is not fixing a regression or issue with a published release
|
||||
1. All other types listed in the [commit message types](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format): `docs`, `style`, `refactor`, `perf`, `test`, `chore`
|
||||
1. All other types listed in the [commit message types](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format): `docs`, `style`, `refactor`, `perf`, `test`, `chore`
|
||||
|
||||
The following branch should be merged back to **both** `main` and `stable`:
|
||||
The following branch should be merged back to **both** `master` and `stable`:
|
||||
|
||||
1. A `release` branch (e.g. `release-4.1.x`): contains all fixes and (optionally) features that are tested and should go into the release
|
||||
|
||||
|
||||
### Feature Branches
|
||||
|
||||
Each new feature should reside in its own branch, based on the `main` branch. When a feature is complete, it should go into a pull request that gets merged back into `main`. A pull request adding a feature should be approved by two team members. Features should never interact directly with `stable`.
|
||||
Each new feature should reside in its own branch, based on the `master` branch. When a feature is complete, it should go into a pull request that gets merged back into `master`. A pull request adding a feature should be approved by two team members. Features should never interact directly with `stable`.
|
||||
|
||||
|
||||
### Release Branches
|
||||
|
||||
Once `main` has acquired enough features for a release (or a predetermined release date is approaching), fork a release branch off of `main`. Creating this branch starts the next release cycle, so no new features can be added after this point - only bug fixes, documentation generation, and other release-oriented tasks should go in this branch.
|
||||
Once `master` has acquired enough features for a release (or a predetermined release date is approaching), fork a release branch off of `master`. Creating this branch starts the next release cycle, so no new features can be added after this point - only bug fixes, documentation generation, and other release-oriented tasks should go in this branch.
|
||||
|
||||
Once the release is ready to ship, it will get merged into `stable` and `main`, then the release branch will be deleted. It’s important to merge back into `main` because critical updates may have been added to the release branch and they need to be accessible to new features. This should be done in a pull request after review.
|
||||
Once the release is ready to ship, it will get merged into `stable` and `master`, then the release branch will be deleted. It’s important to merge back into `master` because critical updates may have been added to the release branch and they need to be accessible to new features. This should be done in a pull request after review.
|
||||
|
||||
See the [steps for releasing](#releasing) below for detailed information on how to publish a release.
|
||||
|
||||
### Version Branches
|
||||
|
||||
Once a release has shipped and the release branch has been merged into `stable` and `main` it should also be merged into its corresponding version branch. These version branches allow us to ship updates for specific versions of the framework (i.e. Lets us ship a bug fix that only affects 4.2.x).
|
||||
Once a release has shipped and the release branch has been merged into `stable` and `master` it should also be merged into its corrsponding version branch. These version branches allow us to ship updates for specific versions of the framework (i.e. Lets us ship a bug fix that only affects 4.2.x).
|
||||
|
||||
Patch releases should be merged into their corresponding version branches. For example, a `release-4.1.1` branch should be merged into the `4.1.x` version branch and a `release-5.0.1` branch should be merged into the `5.0.x` version branch.
|
||||
|
||||
When releasing a major version such as `5.0.0 ` or a minor version such as `4.1.0` , the version branch will not exist. The version branch should be created once the release branch has been merged into `stable` and `main`. For example, when releasing `4.1.0`, the `release-4.1.0` release branch should be merged into `stable` and `main` and then the `4.1.x` version branch should be created off the latest `stable`.
|
||||
When releasing a major version such as `5.0.0 ` or a minor version such as `4.1.0` , the version branch will not exist. The version branch should be created once the release branch has been merged into `stable` and `master`. For example, when releasing `4.1.0`, the `release-4.1.0` release branch should be merged into `stable` and `master` and then the `4.1.x` version branch should be created off the latest `stable`.
|
||||
|
||||
|
||||
### Hotfix Branches
|
||||
|
||||
Maintenance or “hotfix” branches are used to quickly patch production releases. This is the only branch that should fork directly off of `stable`. As soon as the fix is complete, it should be merged into both `stable` and `main` (or the current release branch).
|
||||
Maintenance or “hotfix” branches are used to quickly patch production releases. This is the only branch that should fork directly off of `stable`. As soon as the fix is complete, it should be merged into both `stable` and `master` (or the current release branch).
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
#### Making a Change
|
||||
|
||||
1. Create a branch from `main`.
|
||||
1. Create a branch from `master`.
|
||||
1. Make changes. Limit your changes to a "unit of work", meaning don't include irrelevant changes that may confuse and delay the change.
|
||||
1. Push changes.
|
||||
1. Create a PR with the base of `main`.
|
||||
1. Create a PR with the base of `master`.
|
||||
1. Have someone approve your change (optional right now--at your discretion).
|
||||
|
||||
<img width="236" alt="image" src="https://user-images.githubusercontent.com/236501/47031893-913e0480-d136-11e8-9d9a-4b6297a4d7ba.png">
|
||||
@@ -182,13 +174,13 @@ Maintenance or “hotfix” branches are used to quickly patch production releas
|
||||
|
||||
<img width="192" alt="Squash and merge button" src="https://user-images.githubusercontent.com/236501/47031620-da418900-d135-11e8-91ff-e84f2478b2b3.png">
|
||||
|
||||
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `main` become permanent.
|
||||
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `master` become permanent.
|
||||
|
||||
<img width="672" alt="Squash and merge confirmation" src="https://user-images.githubusercontent.com/236501/47031753-31dff480-d136-11e8-9116-03934961bdc2.png">
|
||||
|
||||
1. Confirm squash and merge into `main`.
|
||||
1. Confirm squash and merge into `master`.
|
||||
|
||||
#### Updating from `main`
|
||||
#### Updating from `master`
|
||||
|
||||
1. Pull the latest changes locally.
|
||||
1. Merge the changes, fixing any conflicts.
|
||||
@@ -204,7 +196,7 @@ OR
|
||||
|
||||
#### Hotfixes
|
||||
|
||||
Hotfixes bypass `main` and should only be used for urgent fixes that can't wait for the next release to be ready.
|
||||
Hotfixes bypass `master` and should only be used for urgent fixes that can't wait for the next release to be ready.
|
||||
|
||||
1. Create a branch from `stable`.
|
||||
1. Make changes.
|
||||
@@ -215,13 +207,13 @@ Hotfixes bypass `main` and should only be used for urgent fixes that can't wait
|
||||
|
||||
<img width="192" alt="Squash and merge button" src="https://user-images.githubusercontent.com/236501/47031620-da418900-d135-11e8-91ff-e84f2478b2b3.png">
|
||||
|
||||
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `main` become permanent.
|
||||
1. During confirmation, rewrite the commit message using our [Commit Message Format guidelines](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format). Keep the `(#1234)` at the end; it will create a link to the PR in the commit history and `CHANGELOG.md`. This is where commits on `master` become permanent.
|
||||
|
||||
<img width="672" alt="Squash and merge confirmation" src="https://user-images.githubusercontent.com/236501/47031753-31dff480-d136-11e8-9116-03934961bdc2.png">
|
||||
|
||||
1. Confirm squash and merge into `stable`.
|
||||
1. CI builds `stable`, performing the release.
|
||||
1. Create a PR to merge `stable` into `main`.
|
||||
1. Create a PR to merge `stable` into `master`.
|
||||
1. Click **Merge pull request**. Use the dropdown to select this option if necessary.
|
||||
|
||||
<img width="191" alt="Merge pull request button" src="https://user-images.githubusercontent.com/236501/47032669-8be1b980-d138-11e8-9a90-d1518c223184.png">
|
||||
@@ -229,7 +221,7 @@ Hotfixes bypass `main` and should only be used for urgent fixes that can't wait
|
||||
|
||||
## Releasing
|
||||
|
||||
1. Create the release branch from `main`, for example: `release-4.5.0`.
|
||||
1. Create the release branch from `master`, for example: `release-4.5.0`.
|
||||
|
||||
1. For major or minor releases, create a version branch based off the latest version branch. For example, if releasing 4.5.0, create a branch called `4.5.x` based off `4.4.x`.
|
||||
|
||||
@@ -244,7 +236,7 @@ Hotfixes bypass `main` and should only be used for urgent fixes that can't wait
|
||||
1. Run `npm run release.prepare`
|
||||
- Select the version based on the type of commits and the [Ionic Versioning](https://ionicframework.com/docs/intro/versioning)
|
||||
- After the process completes, verify the version number in all packages (`core`, `docs`, `angular`)
|
||||
- Verify the changelog commits are accurate and follow the [proper format]((https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md#commit-message-format))
|
||||
- Verify the changelog commits are accurate and follow the [proper format]((https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md#commit-message-format))
|
||||
- For major or minor releases, ensure that the version number has an associated title (for example: `4.5.0 Boron`)
|
||||
- Commit these changes with the version number as the message, e.g. `git commit -m "4.5.0"`
|
||||
|
||||
@@ -258,4 +250,4 @@ Hotfixes bypass `main` and should only be used for urgent fixes that can't wait
|
||||
|
||||
1. Rewrite the commit message to `merge release-[VERSION]` with the proper release branch. For example, if this release is for `4.5.0`, the message would be `merge release-4.5.0`.
|
||||
|
||||
1. Submit a pull request from the release branch into `main`. Merge this pull request using the same commit format in the last step, to ensure any changes made on the release branch get added to future releases.
|
||||
1. Submit a pull request from the release branch into `master`. Merge this pull request using the same commit format in the last step, to ensure any changes made on the release branch get added to future releases.
|
||||
|
||||
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
6
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -26,11 +26,9 @@ Please check the type of change your PR introduces:
|
||||
|
||||
|
||||
## What is the current behavior?
|
||||
<!-- Please describe the current behavior that you are modifying. -->
|
||||
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
|
||||
|
||||
|
||||
<!-- Issues are required for both bug fixes and features. -->
|
||||
Issue URL:
|
||||
Issue Number: N/A
|
||||
|
||||
|
||||
## What is the new behavior?
|
||||
|
||||
BIN
.github/assets/logo.png
vendored
BIN
.github/assets/logo.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB |
24
.github/ionic-issue-bot.yml
vendored
24
.github/ionic-issue-bot.yml
vendored
@@ -27,10 +27,7 @@ comment:
|
||||
is added to issues that need a code reproduction.
|
||||
|
||||
|
||||
Please reproduce this issue in an Ionic starter application and provide a way for us to access it (GitHub repo, StackBlitz, etc). Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
|
||||
|
||||
|
||||
If you have already provided a code snippet and are seeing this message, it is likely that the code snippet was not enough for our team to reproduce the issue.
|
||||
Please provide a reproduction with the minimum amount of code required to reproduce the issue. Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
|
||||
|
||||
|
||||
For a guide on how to create a good reproduction, see our [Contributing Guide](https://ionicframework.com/docs/contributing/how-to-contribute#creating-a-good-code-reproduction).
|
||||
@@ -41,7 +38,8 @@ closeAndLock:
|
||||
- label: "ionitron: support"
|
||||
message: >
|
||||
Thanks for the issue! This issue appears to be a support request. We use this issue tracker exclusively for
|
||||
bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) for questions about the framework.
|
||||
bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) or our
|
||||
[slack channel](https://ionicworldwide.herokuapp.com/) for questions about the framework.
|
||||
|
||||
|
||||
Thank you for using Ionic!
|
||||
@@ -132,6 +130,22 @@ noReproduction:
|
||||
lock: true
|
||||
dryRun: false
|
||||
|
||||
labelPullRequest:
|
||||
labels:
|
||||
- label: "package: angular"
|
||||
branch: master
|
||||
path: ^angular
|
||||
- label: "package: core"
|
||||
branch: master
|
||||
path: ^core
|
||||
- label: "package: react"
|
||||
branch: master
|
||||
path: ^react
|
||||
- label: "package: vue"
|
||||
branch: master
|
||||
path: ^vue
|
||||
dryRun: false
|
||||
|
||||
wrongRepo:
|
||||
repos:
|
||||
- label: "ionitron: capacitor"
|
||||
|
||||
21
.github/labeler.yml
vendored
21
.github/labeler.yml
vendored
@@ -1,21 +0,0 @@
|
||||
# This is used with the label workflow which
|
||||
# will triage pull requests and apply a label based on the
|
||||
# paths that are modified in the pull request.
|
||||
#
|
||||
# For more information, see:
|
||||
# https://github.com/actions/labeler
|
||||
|
||||
'package: core':
|
||||
- core/**/*
|
||||
|
||||
'package: angular':
|
||||
- angular/**/*
|
||||
- packages/angular-*/**/*
|
||||
|
||||
'package: react':
|
||||
- packages/react/**/*
|
||||
- packages/react-*/**/*
|
||||
|
||||
'package: vue':
|
||||
- packages/vue/**/*
|
||||
- packages/vue-*/**/*
|
||||
@@ -1,22 +0,0 @@
|
||||
name: 'Build Ionic Angular Server'
|
||||
description: 'Build Ionic Angular Server'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Install Angular Server Dependencies
|
||||
run: npm install --legacy-peer-deps
|
||||
shell: bash
|
||||
working-directory: ./packages/angular-server
|
||||
- name: Build
|
||||
run: npm run build.prod
|
||||
shell: bash
|
||||
working-directory: ./packages/angular-server
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-angular-server
|
||||
output: packages/angular-server/AngularServerBuild.zip
|
||||
paths: packages/angular-server/dist
|
||||
@@ -1,53 +0,0 @@
|
||||
name: 'Build Ionic Angular'
|
||||
description: 'Build Ionic Angular'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json')}}-v2
|
||||
- name: Cache Angular Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: angular-node-modules
|
||||
with:
|
||||
path: ./angular/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./angular/package-lock.json')}}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Install Angular Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./angular
|
||||
- name: Link @ionic/core
|
||||
run: npm link
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
- name: Link @ionic/core in @ionic/angular
|
||||
run: npm link @ionic/core
|
||||
shell: bash
|
||||
working-directory: ./angular
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./angular
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./angular
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-angular
|
||||
output: ./angular/AngularBuild.zip
|
||||
paths: ./angular/dist
|
||||
30
.github/workflows/actions/build-core/action.yml
vendored
30
.github/workflows/actions/build-core/action.yml
vendored
@@ -1,30 +0,0 @@
|
||||
name: 'Build Ionic Core'
|
||||
description: 'Build Ionic Core'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
working-directory: ./core
|
||||
shell: bash
|
||||
- name: Build Core
|
||||
run: npm run build -- --ci
|
||||
working-directory: ./core
|
||||
shell: bash
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
output: core/CoreBuild.zip
|
||||
paths: core/dist core/components core/css core/hydrate core/loader
|
||||
@@ -1,56 +0,0 @@
|
||||
name: 'Build Ionic React Router'
|
||||
description: 'Build Ionic React Router'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- name: Install Dependencies
|
||||
run: npm install --legacy-peer-deps
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-react
|
||||
path: ./packages/react
|
||||
filename: ReactBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-react
|
||||
path: ./packages/react
|
||||
filename: ReactBuild.zip
|
||||
- name: Install Dependencies
|
||||
run: npm install --legacy-peer-deps
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-react-router
|
||||
output: packages/react-router/ReactRouterBuild.zip
|
||||
paths: packages/react-router/dist
|
||||
46
.github/workflows/actions/build-react/action.yml
vendored
46
.github/workflows/actions/build-react/action.yml
vendored
@@ -1,46 +0,0 @@
|
||||
name: 'Build Ionic React'
|
||||
description: 'Build Ionic React'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Install React Dependencies
|
||||
run: npm install --legacy-peer-deps
|
||||
shell: bash
|
||||
working-directory: ./packages/react
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/react
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./packages/react
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/react
|
||||
- name: Test Spec
|
||||
run: npm run test.spec
|
||||
shell: bash
|
||||
working-directory: ./packages/react
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-react
|
||||
output: packages/react/ReactBuild.zip
|
||||
paths: packages/react/dist packages/react/css
|
||||
@@ -1,51 +0,0 @@
|
||||
name: 'Build Ionic Vue Router'
|
||||
description: 'Builds Ionic Vue Router'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-vue
|
||||
path: ./packages/vue
|
||||
filename: VueBuild.zip
|
||||
- name: Install Vue Router Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/vue-router
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/vue-router
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./packages/vue-router
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/vue-router
|
||||
- name: Test Spec
|
||||
run: npm run test.spec
|
||||
shell: bash
|
||||
working-directory: ./packages/vue-router
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-vue-router
|
||||
output: ./packages/vue-router/VueRouterBuild.zip
|
||||
paths: packages/vue-router/dist
|
||||
42
.github/workflows/actions/build-vue/action.yml
vendored
42
.github/workflows/actions/build-vue/action.yml
vendored
@@ -1,42 +0,0 @@
|
||||
name: 'Build Ionic Vue'
|
||||
description: 'Build Ionic Vue'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Install Vue Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/vue
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/vue
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./packages/vue
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/vue
|
||||
- uses: ./.github/workflows/actions/upload-archive
|
||||
with:
|
||||
name: ionic-vue
|
||||
output: packages/vue/VueBuild.zip
|
||||
paths: packages/vue/dist packages/vue/css
|
||||
@@ -1,19 +0,0 @@
|
||||
name: 'Ionic Framework Archive Download'
|
||||
description: 'Downloads and decompresses an archive from a previous job'
|
||||
inputs:
|
||||
path:
|
||||
description: 'Input archive name'
|
||||
filename:
|
||||
description: 'Input file name'
|
||||
name:
|
||||
description: 'Archive name'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.name }}
|
||||
path: ${{ inputs.path }}
|
||||
- name: Exract Archive
|
||||
run: unzip -q -o ${{ inputs.path }}/${{ inputs.filename }}
|
||||
shell: bash
|
||||
@@ -1,43 +0,0 @@
|
||||
name: 'Test Angular E2E'
|
||||
description: 'Test Angular E2E'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-angular
|
||||
path: ./angular
|
||||
filename: AngularBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-angular-server
|
||||
path: ./packages/angular-server
|
||||
filename: AngularServerBuild.zip
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./angular/test/test-app
|
||||
- name: Sync Built Changes
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./angular/test/test-app
|
||||
- name: Run Tests
|
||||
run: npm run test
|
||||
shell: bash
|
||||
working-directory: ./angular/test/test-app
|
||||
@@ -1,18 +0,0 @@
|
||||
name: 'Test Core Clean Build'
|
||||
description: 'Test Core Clean Build'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Check Diff
|
||||
run: git diff --exit-code
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
@@ -1,25 +0,0 @@
|
||||
name: 'Test Core E2E'
|
||||
description: 'Test Core E2E'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Test
|
||||
run: npm run test.e2e -- --ci --no-build
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
@@ -1,20 +0,0 @@
|
||||
name: 'Test Core Lint'
|
||||
description: 'Test Core Lint'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- name: Lint
|
||||
run: npm run lint
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
@@ -1,33 +0,0 @@
|
||||
name: 'Test Core Screenshot Main'
|
||||
description: 'Test Core Screenshot Main'
|
||||
inputs:
|
||||
access-key-id:
|
||||
description: 'AWS_ACCESS_KEY_ID'
|
||||
secret-access-key:
|
||||
description: 'AWS_SECRET_ACCESS_KEY'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Test
|
||||
run: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --update-screenshot --no-build || true
|
||||
shell: bash
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ inputs.access-key-id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ inputs.secret-access-key }}
|
||||
working-directory: ./core
|
||||
@@ -1,33 +0,0 @@
|
||||
name: 'Test Core Screenshot'
|
||||
description: 'Test Core Screenshot'
|
||||
inputs:
|
||||
access-key-id:
|
||||
description: 'AWS_ACCESS_KEY_ID'
|
||||
secret-access-key:
|
||||
description: 'AWS_SECRET_ACCESS_KEY'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Test
|
||||
run: npx stencil test --e2e --screenshot --screenshot-connector=scripts/screenshot/ci.js --ci --no-build || true
|
||||
shell: bash
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ inputs.access-key-id }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ inputs.secret-access-key }}
|
||||
working-directory: ./core
|
||||
@@ -1,25 +0,0 @@
|
||||
name: 'Test Core Spec'
|
||||
description: 'Test Core Spec'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- name: Test
|
||||
run: npm run test.spec -- --ci
|
||||
shell: bash
|
||||
working-directory: ./core
|
||||
@@ -1,47 +0,0 @@
|
||||
name: 'Test React E2E'
|
||||
description: 'Test React E2E'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-react
|
||||
path: ./packages/react
|
||||
filename: ReactBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-react-router
|
||||
path: ./packages/react-router
|
||||
filename: ReactRouterBuild.zip
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
- name: Run E2E Tests
|
||||
run: npm run e2e
|
||||
shell: bash
|
||||
working-directory: ./packages/react/test-app
|
||||
@@ -1,47 +0,0 @@
|
||||
name: 'Test React Router E2E'
|
||||
description: 'Test React Router '
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-react
|
||||
path: ./packages/react
|
||||
filename: ReactBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-react-router
|
||||
path: ./packages/react-router
|
||||
filename: ReactRouterBuild.zip
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Build
|
||||
run: npm run build
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
- name: Run E2E Tests
|
||||
run: npm run e2e
|
||||
shell: bash
|
||||
working-directory: ./packages/react-router/test-app
|
||||
@@ -1,47 +0,0 @@
|
||||
name: 'Test Vue E2E'
|
||||
description: 'Test Vue E2E'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 15.x
|
||||
|
||||
- name: Cache Core Node Modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: core-node-modules
|
||||
with:
|
||||
path: ./core/node_modules
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('./core/package-lock.json') }}-v2
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-core
|
||||
path: ./core
|
||||
filename: CoreBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-vue
|
||||
path: ./packages/vue
|
||||
filename: VueBuild.zip
|
||||
- uses: ./.github/workflows/actions/download-archive
|
||||
with:
|
||||
name: ionic-vue-router
|
||||
path: ./packages/vue-router
|
||||
filename: VueRouterBuild.zip
|
||||
- name: Install Dependencies
|
||||
run: npm install
|
||||
shell: bash
|
||||
working-directory: ./packages/vue/test-app
|
||||
- name: Sync
|
||||
run: npm run sync
|
||||
shell: bash
|
||||
working-directory: ./packages/vue/test-app
|
||||
- name: Run Spec Tests
|
||||
run: npm run test:unit
|
||||
shell: bash
|
||||
working-directory: ./packages/vue/test-app
|
||||
- name: Run E2E Tests
|
||||
run: npm run test:e2e
|
||||
shell: bash
|
||||
working-directory: ./packages/vue/test-app
|
||||
@@ -1,19 +0,0 @@
|
||||
name: 'Ionic Framework Archive Upload'
|
||||
description: 'Compresses and uploads an archive to be reused across jobs'
|
||||
inputs:
|
||||
paths:
|
||||
description: 'Paths to files or directories to archive'
|
||||
output:
|
||||
description: 'Output file name'
|
||||
name:
|
||||
description: 'Archive name'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Create Archive
|
||||
run: zip -q -r ${{ inputs.output }} ${{ inputs.paths }}
|
||||
shell: bash
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ inputs.name }}
|
||||
path: ${{ inputs.output }}
|
||||
132
.github/workflows/build.yml
vendored
132
.github/workflows/build.yml
vendored
@@ -1,132 +0,0 @@
|
||||
name: 'Ionic Framework Build'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
|
||||
jobs:
|
||||
build-core:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-core
|
||||
|
||||
test-core-clean-build:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-core-clean-build
|
||||
|
||||
test-core-lint:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-core-lint
|
||||
|
||||
test-core-spec:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-core-spec
|
||||
|
||||
test-core-e2e:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-core-e2e
|
||||
|
||||
test-core-screenshot:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref != 'refs/heads/main' && !github.event.pull_request.head.repo.fork
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-core-screenshot
|
||||
with:
|
||||
access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
test-core-screenshot-main:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-core-screenshot-main
|
||||
with:
|
||||
access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
|
||||
build-vue:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-vue
|
||||
|
||||
build-vue-router:
|
||||
needs: [build-vue]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-vue-router
|
||||
|
||||
test-vue-e2e:
|
||||
needs: [build-vue, build-vue-router]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-vue-e2e
|
||||
|
||||
build-angular:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-angular
|
||||
|
||||
build-angular-server:
|
||||
needs: [build-angular]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-angular-server
|
||||
|
||||
test-angular-e2e:
|
||||
needs: [build-angular, build-angular-server]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-angular-e2e
|
||||
|
||||
build-react:
|
||||
needs: [build-core]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-react
|
||||
|
||||
build-react-router:
|
||||
needs: [build-react]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/build-react-router
|
||||
|
||||
test-react-router-e2e:
|
||||
needs: [build-react, build-react-router]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-react-router-e2e
|
||||
|
||||
test-react-e2e:
|
||||
needs: [build-react, build-react-router]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/workflows/actions/test-react-e2e
|
||||
45
.github/workflows/dev-build.yml
vendored
45
.github/workflows/dev-build.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: 'Ionic Dev Build'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
dev-build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.dev-build.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
- name: Prepare NPM Token
|
||||
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
|
||||
shell: bash
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
# A 1 is required before the timestamp
|
||||
# as lerna will fail when there is a leading 0
|
||||
# See https://github.com/lerna/lerna/issues/2840
|
||||
- name: Create Dev Hash
|
||||
run: |
|
||||
echo "HASH=1$(git log -1 --format=%H | cut -c 1-7)" >> $GITHUB_ENV
|
||||
echo "TIMESTAMP=1$(date +%s)" >> $GITHUB_ENV
|
||||
echo "CURRENT_VERSION=$(node ./.scripts/bump-version.js)" >> $GITHUB_ENV
|
||||
shell: bash
|
||||
- name: Create Dev Build
|
||||
run: |
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}") --no-verify-access --yes --force-publish='*' --dist-tag dev --no-git-tag-version --no-push --exact
|
||||
shell: bash
|
||||
- id: dev-build
|
||||
run: echo "::set-output name=version::$(echo "${{ env.CURRENT_VERSION }}")-dev.$(echo "${{ env.TIMESTAMP }}").$(echo "${{ env.HASH }}")"
|
||||
get-build:
|
||||
name: Get your dev build!
|
||||
runs-on: ubuntu-latest
|
||||
needs: dev-build
|
||||
steps:
|
||||
- run: echo ${{ needs.dev-build.outputs.version }}
|
||||
|
||||
19
.github/workflows/label.yml
vendored
19
.github/workflows/label.yml
vendored
@@ -1,19 +0,0 @@
|
||||
# This workflow will triage pull requests and apply a label based on the
|
||||
# paths that are modified in the pull request.
|
||||
#
|
||||
# To use this workflow, you will need to set up a .github/labeler.yml
|
||||
# file with configuration. For more information, see:
|
||||
# https://github.com/actions/labeler
|
||||
|
||||
name: "Pull Request Labeler"
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@main
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
48
.github/workflows/pre-release.yml
vendored
48
.github/workflows/pre-release.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: 'Ionic Pre-Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: choice
|
||||
description: Which version should be published?
|
||||
options:
|
||||
- prepatch
|
||||
- preminor
|
||||
- premajor
|
||||
prefix:
|
||||
required: true
|
||||
type: choice
|
||||
description: What kind of pre-release is this?
|
||||
default: beta
|
||||
options:
|
||||
- alpha
|
||||
- beta
|
||||
- rc
|
||||
|
||||
jobs:
|
||||
build-ionic:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Configure Identity
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
shell: bash
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
- name: Prepare NPM Token
|
||||
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
|
||||
shell: bash
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Release
|
||||
run: |
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag next --no-git-tag-version --no-push --skip-npm --preid $(echo "${{ github.events.inputs.prefix }}")
|
||||
shell: bash
|
||||
51
.github/workflows/release.yml
vendored
51
.github/workflows/release.yml
vendored
@@ -1,51 +0,0 @@
|
||||
name: 'Ionic Production Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
required: true
|
||||
type: choice
|
||||
description: Which version should be published?
|
||||
options:
|
||||
- patch
|
||||
- minor
|
||||
- major
|
||||
tag:
|
||||
required: true
|
||||
type: choice
|
||||
description: Which npm tag should this be published to?
|
||||
options:
|
||||
- latest
|
||||
- v5-lts
|
||||
- v4-lts
|
||||
|
||||
jobs:
|
||||
build-ionic:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16
|
||||
- name: Configure Identity
|
||||
run: |
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
shell: bash
|
||||
- name: Install Dependencies
|
||||
run: npm ci --no-package-lock && lerna bootstrap --ignore-scripts -- --legacy-peer-deps
|
||||
shell: bash
|
||||
- name: Prepare NPM Token
|
||||
run: echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} > .npmrc
|
||||
shell: bash
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
- name: Release
|
||||
run: |
|
||||
HUSKY_SKIP_HOOKS=1 lerna publish $(echo "${{ github.event.inputs.version }}") --no-verify-access --yes --force-publish='*' --dist-tag $(echo "${{ github.event.inputs.tag }}") --conventional-commits --create-release github
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
11
.github/workflows/update-screenshots.yml
vendored
11
.github/workflows/update-screenshots.yml
vendored
@@ -1,11 +0,0 @@
|
||||
name: 'Update Screenshot References'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stub:
|
||||
steps:
|
||||
- name: Stub
|
||||
run: echo 'This is a stub'
|
||||
shell: bash
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -58,13 +58,9 @@ prerender-static.html
|
||||
# stencil
|
||||
angular/css/
|
||||
packages/react/css/
|
||||
packages/vue/css/
|
||||
core/components/
|
||||
core/css/
|
||||
core/hydrate/
|
||||
core/loader/
|
||||
core/www/
|
||||
.stencil/
|
||||
angular/build/
|
||||
|
||||
.npmrc
|
||||
|
||||
26
.scripts/README.md
Normal file
26
.scripts/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Build Scripts
|
||||
|
||||
## Release
|
||||
|
||||
The deploy scripts at the root, make a new release of all the packages in this monorepo.
|
||||
All packages will be released with the same version.
|
||||
|
||||
In order to make a new release:
|
||||
|
||||
1. `npm run release.prepare`
|
||||
2. Review/update changelog
|
||||
3. Commit updates using the package name and version number as the commit message.
|
||||
4. `npm run release`
|
||||
5. :tada:
|
||||
|
||||
|
||||
## Prerelease
|
||||
|
||||
It's also possible to make prereleases of individual packages (@ionic/core, @ionic/angular).
|
||||
In order to do so, move to the package you want to make a new release and execute:
|
||||
```
|
||||
npm run prerelease
|
||||
```
|
||||
|
||||
It will publish a new prerelease in NPM, but it will not create any new git tag
|
||||
or update the CHANGELOG.
|
||||
15
.scripts/build.js
Normal file
15
.scripts/build.js
Normal file
@@ -0,0 +1,15 @@
|
||||
const common = require('./common');
|
||||
const Listr = require('listr');
|
||||
|
||||
|
||||
async function main() {
|
||||
const tasks = [];
|
||||
common.packages.forEach(package => {
|
||||
common.preparePackage(tasks, package, false, false);
|
||||
});
|
||||
|
||||
const listr = new Listr(tasks, { showSubtasks: true });
|
||||
await listr.run();
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -1,10 +0,0 @@
|
||||
const semver = require('semver');
|
||||
|
||||
const getDevVersion = () => {
|
||||
const originalVersion = require('../lerna.json').version;
|
||||
const baseVersion = semver.inc(originalVersion, 'patch');
|
||||
|
||||
return baseVersion;
|
||||
}
|
||||
|
||||
console.log(getDevVersion());
|
||||
395
.scripts/common.js
Normal file
395
.scripts/common.js
Normal file
@@ -0,0 +1,395 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const semver = require('semver');
|
||||
const { bold, cyan, dim } = require('colorette');
|
||||
|
||||
const rootDir = path.join(__dirname, '../');
|
||||
|
||||
const packages = [
|
||||
'core',
|
||||
'docs',
|
||||
'angular',
|
||||
'packages/react',
|
||||
'packages/react-router',
|
||||
'packages/angular-server'
|
||||
];
|
||||
|
||||
function readPkg(project) {
|
||||
const packageJsonPath = packagePath(project);
|
||||
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
||||
}
|
||||
|
||||
function writePkg(project, pkg) {
|
||||
const packageJsonPath = packagePath(project);
|
||||
const text = JSON.stringify(pkg, null, 2);
|
||||
return fs.writeFileSync(packageJsonPath, `${text}\n`);
|
||||
}
|
||||
|
||||
function packagePath(project) {
|
||||
return path.join(rootDir, project, 'package.json');
|
||||
}
|
||||
|
||||
function projectPath(project) {
|
||||
return path.join(rootDir, project);
|
||||
}
|
||||
|
||||
async function askNpmTag(version) {
|
||||
const prompts = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'npmTag',
|
||||
message: 'Select npm tag or specify a new tag',
|
||||
choices: ['latest', 'next', 'v4-lts']
|
||||
.concat([
|
||||
new inquirer.Separator(),
|
||||
{
|
||||
name: 'Other (specify)',
|
||||
value: null
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: answers => {
|
||||
return `Will publish ${cyan(version)} to ${cyan(answers.npmTag)}. Continue?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { npmTag, confirm } = await inquirer.prompt(prompts);
|
||||
return { npmTag, confirm };
|
||||
}
|
||||
|
||||
function checkGit(tasks) {
|
||||
tasks.push(
|
||||
{
|
||||
title: 'Check current branch',
|
||||
task: () =>
|
||||
execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
|
||||
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
|
||||
throw new Error(`Must be on a "release" or "hotfix" branch.`);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Check local working tree',
|
||||
task: () =>
|
||||
execa.stdout('git', ['status', '--porcelain']).then(status => {
|
||||
if (status !== '') {
|
||||
throw new Error(`Unclean working tree. Commit or stash changes first.`);
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
title: 'Check remote history',
|
||||
task: () =>
|
||||
execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
|
||||
if (result !== '0') {
|
||||
throw new Error(`Remote history differs. Please pull changes.`);
|
||||
}
|
||||
})
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function checkTestDist(tasks) {
|
||||
tasks.push({
|
||||
title: 'Check dist folders for required files',
|
||||
task: () =>
|
||||
execa.stdout('node', ['.scripts/test-dist.js']).then(status => {
|
||||
if (status.indexOf('✅ test.dist') === -1) {
|
||||
throw new Error(`Test Dist did not find some required files`);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const isValidVersion = input => Boolean(semver.valid(input));
|
||||
|
||||
function preparePackage(tasks, package, version, install) {
|
||||
const projectRoot = projectPath(package);
|
||||
const pkg = readPkg(package);
|
||||
|
||||
const projectTasks = [];
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: validate new version`,
|
||||
task: () => {
|
||||
if (!isVersionGreater(pkg.version, version)) {
|
||||
throw new Error(
|
||||
`New version \`${version}\` should be higher than current version \`${pkg.version}\``
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (install) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: install npm dependencies`,
|
||||
task: async () => {
|
||||
await fs.remove(path.join(projectRoot, 'node_modules'));
|
||||
await execa('npm', ['i'], { cwd: projectRoot });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (package !== 'docs') {
|
||||
if (package !== 'core') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link @ionic/core`,
|
||||
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
|
||||
});
|
||||
|
||||
if (package === 'packages/react-router') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link @ionic/react`,
|
||||
task: () => execa('npm', ['link', '@ionic/react'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Lint, Test, Bump Core dependency
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: lint`,
|
||||
task: () => execa('npm', ['run', 'lint'], { cwd: projectRoot })
|
||||
});
|
||||
// TODO will not work due to https://github.com/ionic-team/ionic/issues/20136
|
||||
// projectTasks.push({
|
||||
// title: `${pkg.name}: test`,
|
||||
// task: async () => await execa('npm', ['test'], { cwd: projectRoot })
|
||||
// });
|
||||
}
|
||||
|
||||
// Build
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: build`,
|
||||
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
|
||||
});
|
||||
|
||||
// Link core or react for sub projects
|
||||
if (package === 'core' || package === 'packages/react') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link`,
|
||||
task: () => execa('npm', ['link'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
|
||||
if (version) {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||
task: () => {
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add project tasks
|
||||
tasks.push({
|
||||
title: `Prepare ${bold(pkg.name)}`,
|
||||
task: () => new Listr(projectTasks)
|
||||
});
|
||||
}
|
||||
|
||||
function prepareDevPackage(tasks, package, version) {
|
||||
const projectRoot = projectPath(package);
|
||||
const pkg = readPkg(package);
|
||||
|
||||
const projectTasks = [];
|
||||
|
||||
if (package !== 'docs') {
|
||||
if (package !== 'core') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link @ionic/core`,
|
||||
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: update ionic/core dep to ${version}`,
|
||||
task: () => {
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: build`,
|
||||
task: () => execa('npm', ['run', 'build'], { cwd: projectRoot })
|
||||
});
|
||||
|
||||
if (package === 'core' || package === 'packages/react') {
|
||||
projectTasks.push({
|
||||
title: `${pkg.name}: npm link`,
|
||||
task: () => execa('npm', ['link'], { cwd: projectRoot })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add project tasks
|
||||
tasks.push({
|
||||
title: `Prepare dev build: ${bold(pkg.name)}`,
|
||||
task: () => new Listr(projectTasks)
|
||||
});
|
||||
}
|
||||
|
||||
function updatePackageVersions(tasks, packages, version) {
|
||||
packages.forEach(package => {
|
||||
updatePackageVersion(tasks, package, version);
|
||||
|
||||
tasks.push({
|
||||
title: `${package} update @ionic/core dependency, if present ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
if (package !== 'core') {
|
||||
const pkg = readPkg(package);
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// angular & angular-server need to update their dist versions
|
||||
if (package === 'angular' || package === 'packages/angular-server') {
|
||||
const distPackage = path.join(package, 'dist');
|
||||
|
||||
updatePackageVersion(tasks, distPackage, version);
|
||||
|
||||
tasks.push({
|
||||
title: `${package} update @ionic/core dependency, if present ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
const pkg = readPkg(distPackage);
|
||||
updateDependency(pkg, '@ionic/core', version);
|
||||
writePkg(distPackage, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (package === 'packages/react-router') {
|
||||
tasks.push({
|
||||
title: `${package} update @ionic/react dependency, if present ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
const pkg = readPkg(package);
|
||||
updateDependency(pkg, '@ionic/react', version);
|
||||
writePkg(package, pkg);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updatePackageVersion(tasks, package, version) {
|
||||
const projectRoot = projectPath(package);
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: update package.json ${dim(`(${version})`)}`,
|
||||
task: async () => {
|
||||
await execa('npm', ['version', version], { cwd: projectRoot });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function copyPackageToDist(tasks, packages) {
|
||||
packages.forEach(package => {
|
||||
const projectRoot = projectPath(package);
|
||||
|
||||
// angular and angular-server are the only packages that publish dist
|
||||
if (package !== 'angular' && package !== 'packages/angular-server') {
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: Copy package.json to dist`,
|
||||
task: () => execa('node', ['copy-package.js', package], { cwd: path.join(rootDir, '.scripts') })
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function publishPackages(tasks, packages, version, npmTag = 'latest') {
|
||||
// first verify version
|
||||
packages.forEach(package => {
|
||||
if (package === 'core') {
|
||||
return;
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: check version (must match: ${version})`,
|
||||
task: () => {
|
||||
const pkg = readPkg(package);
|
||||
|
||||
if (version !== pkg.version) {
|
||||
throw new Error(`${pkg.name} version ${pkg.version} must match ${version}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Publish
|
||||
packages.forEach(package => {
|
||||
let projectRoot = projectPath(package);
|
||||
|
||||
if (package === 'packages/angular-server' || package === 'angular') {
|
||||
projectRoot = path.join(projectRoot, 'dist')
|
||||
}
|
||||
|
||||
tasks.push({
|
||||
title: `${package}: publish to ${npmTag} tag`,
|
||||
task: async () => {
|
||||
await execa('npm', ['publish', '--tag', npmTag], { cwd: projectRoot });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updateDependency(pkg, dependency, version) {
|
||||
if (pkg.dependencies && pkg.dependencies[dependency]) {
|
||||
pkg.dependencies[dependency] = version;
|
||||
}
|
||||
if (pkg.devDependencies && pkg.devDependencies[dependency]) {
|
||||
pkg.devDependencies[dependency] = version;
|
||||
}
|
||||
if (pkg.peerDependencies && pkg.peerDependencies[dependency]) {
|
||||
pkg.peerDependencies[dependency] = version;
|
||||
}
|
||||
}
|
||||
|
||||
function isVersionGreater(oldVersion, newVersion) {
|
||||
if (!isValidVersion(newVersion)) {
|
||||
throw new Error('Version should be a valid semver version.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function copyCDNLoader(tasks, version) {
|
||||
tasks.push({
|
||||
title: `Copy CDN loader`,
|
||||
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') })
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
checkTestDist,
|
||||
checkGit,
|
||||
askNpmTag,
|
||||
isValidVersion,
|
||||
isVersionGreater,
|
||||
copyCDNLoader,
|
||||
copyPackageToDist,
|
||||
packages,
|
||||
packagePath,
|
||||
prepareDevPackage,
|
||||
preparePackage,
|
||||
projectPath,
|
||||
publishPackages,
|
||||
readPkg,
|
||||
rootDir,
|
||||
updateDependency,
|
||||
updatePackageVersion,
|
||||
updatePackageVersions,
|
||||
writePkg
|
||||
};
|
||||
214
.scripts/prepare.js
Normal file
214
.scripts/prepare.js
Normal file
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
* Deploy script adopted from https://github.com/sindresorhus/np
|
||||
* MIT License (c) Sindre Sorhus (sindresorhus.com)
|
||||
*/
|
||||
const { cyan, dim, red, reset } = require('colorette');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const semver = require('semver');
|
||||
const common = require('./common');
|
||||
const path = require('path');
|
||||
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
if (!process.env.GH_TOKEN) {
|
||||
throw new Error('env.GH_TOKEN is undefined');
|
||||
}
|
||||
|
||||
const { version, confirm } = await askVersion();
|
||||
const install = process.argv.indexOf('--no-install') < 0;
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
// compile and verify packages
|
||||
await preparePackages(common.packages, version, install);
|
||||
|
||||
console.log(`\nionic ${version} prepared 🤖\n`);
|
||||
console.log(`Next steps:`);
|
||||
console.log(` Verify CHANGELOG.md`);
|
||||
console.log(` git commit -m "${version}"`);
|
||||
console.log(` npm run release\n`);
|
||||
|
||||
} catch(err) {
|
||||
console.log('\n', red(err), '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function askVersion() {
|
||||
const pkg = common.readPkg('core');
|
||||
const oldVersion = pkg.version;
|
||||
|
||||
const prompts = [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'version',
|
||||
message: 'Select semver increment or specify new version',
|
||||
pageSize: SEMVER_INCREMENTS.length + 2,
|
||||
choices: SEMVER_INCREMENTS
|
||||
.map(inc => ({
|
||||
name: `${inc} ${prettyVersionDiff(oldVersion, inc)}`,
|
||||
value: inc
|
||||
}))
|
||||
.concat([
|
||||
new inquirer.Separator(),
|
||||
{
|
||||
name: 'Other (specify)',
|
||||
value: null
|
||||
}
|
||||
]),
|
||||
filter: input => isValidVersionInput(input) ? getNewVersion(oldVersion, input) : input
|
||||
},
|
||||
{
|
||||
type: 'input',
|
||||
name: 'version',
|
||||
message: 'Version',
|
||||
when: answers => !answers.version,
|
||||
filter: input => isValidVersionInput(input) ? getNewVersion(pkg.version, input) : input,
|
||||
validate: input => {
|
||||
if (!isValidVersionInput(input)) {
|
||||
return 'Please specify a valid semver, for example, `1.2.3`. See http://semver.org';
|
||||
} else if (!common.isVersionGreater(oldVersion, input)) {
|
||||
return `Version must be greater than ${oldVersion}`;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: answers => {
|
||||
return `Will bump from ${cyan(oldVersion)} to ${cyan(answers.version)}. Continue?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { version, confirm } = await inquirer.prompt(prompts);
|
||||
return { version, confirm };
|
||||
}
|
||||
|
||||
|
||||
async function preparePackages(packages, version, install) {
|
||||
// execution order matters
|
||||
const tasks = [];
|
||||
|
||||
// check git is nice and clean local and remote
|
||||
common.checkGit(tasks);
|
||||
|
||||
// test we're good with git
|
||||
validateGit(tasks, version);
|
||||
|
||||
// add all the prepare scripts
|
||||
// run all these tasks before updating package.json version
|
||||
packages.forEach(package => {
|
||||
common.preparePackage(tasks, package, version, install);
|
||||
});
|
||||
|
||||
// add update package.json of each project
|
||||
common.updatePackageVersions(tasks, packages, version);
|
||||
|
||||
// generate changelog
|
||||
generateChangeLog(tasks);
|
||||
|
||||
// check dist folders
|
||||
common.checkTestDist(tasks);
|
||||
|
||||
// update core readme with version number
|
||||
updateCoreReadme(tasks, version);
|
||||
common.copyCDNLoader(tasks, version);
|
||||
|
||||
const listr = new Listr(tasks, { showSubtasks: true });
|
||||
await listr.run();
|
||||
}
|
||||
|
||||
|
||||
function validateGit(tasks, version) {
|
||||
tasks.push(
|
||||
{
|
||||
title: `Validate git tag ${dim(`(v${version})`)}`,
|
||||
task: () => execa('git', ['fetch'])
|
||||
.then(() => {
|
||||
return execa.stdout('npm', ['config', 'get', 'tag-version-prefix']);
|
||||
})
|
||||
.then(
|
||||
output => {
|
||||
tagPrefix = output;
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
.then(() => execa.stdout('git', ['rev-parse', '--quiet', '--verify', `refs/tags/${tagPrefix}${version}`]))
|
||||
.then(
|
||||
output => {
|
||||
if (output) {
|
||||
throw new Error(`Git tag \`${tagPrefix}${version}\` already exists.`);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
// Command fails with code 1 and no output if the tag does not exist, even though `--quiet` is provided
|
||||
// https://github.com/sindresorhus/np/pull/73#discussion_r72385685
|
||||
if (err.stdout !== '' || err.stderr !== '') {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function generateChangeLog(tasks) {
|
||||
tasks.push({
|
||||
title: `Generate CHANGELOG.md`,
|
||||
task: () => execa('npm', ['run', 'changelog'], { cwd: common.rootDir }),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateCoreReadme(tasks, version) {
|
||||
tasks.push({
|
||||
title: `Update core README.md`,
|
||||
task: () => execa('node', ['update-readme.js', version], { cwd: path.join(common.rootDir, 'core', 'scripts') }),
|
||||
});
|
||||
}
|
||||
|
||||
const SEMVER_INCREMENTS = ['patch', 'minor', 'major'];
|
||||
|
||||
const isValidVersionInput = input => SEMVER_INCREMENTS.indexOf(input) !== -1 || common.isValidVersion(input);
|
||||
|
||||
function getNewVersion(oldVersion, input) {
|
||||
if (!isValidVersionInput(input)) {
|
||||
throw new Error(`Version should be either ${SEMVER_INCREMENTS.join(', ')} or a valid semver version.`);
|
||||
}
|
||||
|
||||
return SEMVER_INCREMENTS.indexOf(input) === -1 ? input : semver.inc(oldVersion, input);
|
||||
};
|
||||
|
||||
|
||||
function prettyVersionDiff(oldVersion, inc) {
|
||||
const newVersion = getNewVersion(oldVersion, inc).split('.');
|
||||
oldVersion = oldVersion.split('.');
|
||||
let firstVersionChange = false;
|
||||
const output = [];
|
||||
|
||||
for (let i = 0; i < newVersion.length; i++) {
|
||||
if ((newVersion[i] !== oldVersion[i] && !firstVersionChange)) {
|
||||
output.push(`${dim(cyan(newVersion[i]))}`);
|
||||
firstVersionChange = true;
|
||||
} else if (newVersion[i].indexOf('-') >= 1) {
|
||||
let preVersion = [];
|
||||
preVersion = newVersion[i].split('-');
|
||||
output.push(`${dim(cyan(`${preVersion[0]}-${preVersion[1]}`))}`);
|
||||
} else {
|
||||
output.push(reset(dim(newVersion[i])));
|
||||
}
|
||||
}
|
||||
return output.join(reset(dim('.')));
|
||||
}
|
||||
|
||||
main();
|
||||
104
.scripts/release-dev.js
Normal file
104
.scripts/release-dev.js
Normal file
@@ -0,0 +1,104 @@
|
||||
const { cyan, red } = require('colorette');
|
||||
const semver = require('semver');
|
||||
const execa = require('execa');
|
||||
const inquirer = require('inquirer');
|
||||
const Listr = require('listr');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
const common = require('./common');
|
||||
|
||||
const DIST_NPM_TAG = 'dev';
|
||||
const DIST_TAG = 'dev';
|
||||
|
||||
async function main() {
|
||||
const { packages } = common;
|
||||
|
||||
const orgPkg = packages.map(package => {
|
||||
const packageJsonPath = common.packagePath(package);
|
||||
return {
|
||||
filePath: packageJsonPath,
|
||||
packageContent: fs.readFileSync(packageJsonPath, 'utf-8')
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
const originalVersion = common.readPkg('core').version;
|
||||
const devVersion = await getDevVersion(originalVersion);
|
||||
|
||||
const confirm = await askDevVersion(devVersion);
|
||||
if (!confirm) {
|
||||
console.log(``);
|
||||
return;
|
||||
}
|
||||
|
||||
const tasks = [];
|
||||
|
||||
await setPackageVersionChanges(packages, devVersion);
|
||||
|
||||
packages.forEach(package => {
|
||||
common.prepareDevPackage(tasks, package, devVersion);
|
||||
});
|
||||
common.copyCDNLoader(tasks, devVersion);
|
||||
common.publishPackages(tasks, packages, devVersion, DIST_NPM_TAG);
|
||||
|
||||
const listr = new Listr(tasks);
|
||||
await listr.run();
|
||||
|
||||
console.log(`\nionic ${devVersion} published!! 🎉\n`);
|
||||
|
||||
} catch (err) {
|
||||
console.log('\n', red(err), '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
orgPkg.forEach(pkg => {
|
||||
fs.writeFileSync(pkg.filePath, pkg.packageContent);
|
||||
});
|
||||
}
|
||||
|
||||
async function askDevVersion(devVersion) {
|
||||
|
||||
const prompts = [
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
value: true,
|
||||
message: () => {
|
||||
return `Publish the dev build ${cyan(devVersion)}?`;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { confirm } = await inquirer.prompt(prompts);
|
||||
return confirm;
|
||||
}
|
||||
|
||||
async function setPackageVersionChanges(packages, version) {
|
||||
await Promise.all(packages.map(async package => {
|
||||
if (package !== 'core') {
|
||||
const pkg = common.readPkg(package);
|
||||
common.updateDependency(pkg, '@ionic/core', version);
|
||||
common.writePkg(package, pkg);
|
||||
}
|
||||
const projectRoot = common.projectPath(package);
|
||||
await execa('npm', ['version', version], { cwd: projectRoot });
|
||||
}));
|
||||
}
|
||||
|
||||
async function getDevVersion(originalVersion) {
|
||||
const { stdout: sha } = await execa('git', ['log', '-1', '--format=%H']);
|
||||
const shortSha = sha.substring(0, 7);
|
||||
const baseVersion = semver.inc(originalVersion, 'minor');
|
||||
|
||||
const d = new Date();
|
||||
|
||||
let timestamp = d.getUTCFullYear().toString();
|
||||
timestamp += (d.getUTCMonth() + 1).toString().padStart(2, '0');
|
||||
timestamp += d.getUTCDate().toString().padStart(2, '0');
|
||||
timestamp += d.getUTCHours().toString().padStart(2, '0');
|
||||
timestamp += d.getUTCMinutes().toString().padStart(2, '0');
|
||||
|
||||
return `${baseVersion}-${DIST_TAG}.${timestamp}.${shortSha}`;
|
||||
}
|
||||
|
||||
main();
|
||||
144
.scripts/release.js
Normal file
144
.scripts/release.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* Deploy script adopted from https://github.com/sindresorhus/np
|
||||
* MIT License (c) Sindre Sorhus (sindresorhus.com)
|
||||
*/
|
||||
const { cyan, dim, green, red, yellow } = require('colorette');
|
||||
const execa = require('execa');
|
||||
const Listr = require('listr');
|
||||
const path = require('path');
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
const common = require('./common');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const dryRun = process.argv.indexOf('--dry-run') > -1;
|
||||
|
||||
if (!process.env.GH_TOKEN) {
|
||||
throw new Error('env.GH_TOKEN is undefined');
|
||||
}
|
||||
|
||||
checkProductionRelease();
|
||||
|
||||
const tasks = [];
|
||||
const { version } = common.readPkg('core');
|
||||
const changelog = findChangelog();
|
||||
|
||||
// repo must be clean
|
||||
common.checkGit(tasks);
|
||||
|
||||
const { npmTag, confirm } = await common.askNpmTag(version);
|
||||
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!dryRun) {
|
||||
// publish each package in NPM
|
||||
common.publishPackages(tasks, common.packages, version, npmTag);
|
||||
|
||||
// push tag to git remote
|
||||
publishGit(tasks, version, changelog, npmTag);
|
||||
}
|
||||
|
||||
const listr = new Listr(tasks);
|
||||
await listr.run();
|
||||
|
||||
// Dry run doesn't publish to npm or git
|
||||
if (dryRun) {
|
||||
console.log(`
|
||||
\n${yellow('Did not publish. Remove the "--dry-run" flag to publish:')}\n${green(version)} to ${cyan(npmTag)}\n
|
||||
`);
|
||||
} else {
|
||||
console.log(`\nionic ${version} published to ${npmTag}!! 🎉\n`);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log('\n', red(err), '\n');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function checkProductionRelease() {
|
||||
const corePath = common.projectPath('core');
|
||||
const hasEsm = fs.existsSync(path.join(corePath, 'dist', 'esm'));
|
||||
const hasEsmEs5 = fs.existsSync(path.join(corePath, 'dist', 'esm-es5'));
|
||||
const hasCjs = fs.existsSync(path.join(corePath, 'dist', 'cjs'));
|
||||
if (!hasEsm || !hasEsmEs5 || !hasCjs) {
|
||||
throw new Error('core build is not a production build');
|
||||
}
|
||||
}
|
||||
|
||||
function publishGit(tasks, version, changelog, npmTag) {
|
||||
const gitTag = `v${version}`;
|
||||
|
||||
tasks.push(
|
||||
{
|
||||
title: `Tag latest commit ${dim(`(${gitTag})`)}`,
|
||||
task: () => execa('git', ['tag', `${gitTag}`], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Push branches to remote',
|
||||
task: () => execa('git', ['push'], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Push tags to remove',
|
||||
task: () => execa('git', ['push', '--follow-tags'], { cwd: common.rootDir })
|
||||
},
|
||||
{
|
||||
title: 'Publish Github release',
|
||||
task: () => publishGithub(version, gitTag, changelog, npmTag)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function findChangelog() {
|
||||
const lines = fs.readFileSync('CHANGELOG.md', 'utf-8').toString().split('\n');
|
||||
let start = -1;
|
||||
let end = -1;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.startsWith('## [') || line.startsWith('# [')) {
|
||||
if (start === -1) {
|
||||
start = i + 1;
|
||||
} else {
|
||||
end = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(start === -1 || end === -1) {
|
||||
throw new Error('changelog diff was not found');
|
||||
}
|
||||
return lines.slice(start, end).join('\n').trim();
|
||||
}
|
||||
|
||||
async function publishGithub(version, gitTag, changelog, npmTag) {
|
||||
// If the npm tag is next then publish as a prerelease
|
||||
const prerelease = npmTag === 'next' ? true : false;
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GH_TOKEN
|
||||
});
|
||||
|
||||
let branch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']);
|
||||
|
||||
if (!branch) {
|
||||
branch = 'master';
|
||||
}
|
||||
|
||||
await octokit.repos.createRelease({
|
||||
owner: 'ionic-team',
|
||||
repo: 'ionic',
|
||||
target_commitish: branch,
|
||||
tag_name: gitTag,
|
||||
name: version,
|
||||
body: changelog,
|
||||
prerelease: prerelease
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
97
.scripts/test-dist.js
Normal file
97
.scripts/test-dist.js
Normal file
@@ -0,0 +1,97 @@
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// Test dist build:
|
||||
// Double-triple check all the packages
|
||||
// and files are good to go before publishing
|
||||
[
|
||||
// core
|
||||
{
|
||||
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
|
||||
},
|
||||
// angular
|
||||
{
|
||||
files: [
|
||||
'../angular/dist/schematics/collection.json',
|
||||
'../angular/dist/fesm5/ionic-angular.js',
|
||||
'../angular/dist/fesm2015/ionic-angular.js',
|
||||
'../angular/dist/ionic-angular.d.ts',
|
||||
'../angular/dist/ionic-angular.metadata.json'
|
||||
]
|
||||
},
|
||||
// angular-server
|
||||
{
|
||||
files: [
|
||||
'../packages/angular-server/dist/fesm5/ionic-angular-server.js',
|
||||
'../packages/angular-server/dist/fesm2015/ionic-angular-server.js',
|
||||
'../packages/angular-server/dist/ionic-angular-server.d.ts',
|
||||
'../packages/angular-server/dist/ionic-angular-server.metadata.json'
|
||||
]
|
||||
},
|
||||
// react
|
||||
{
|
||||
files: ['../packages/react/dist/index.js']
|
||||
},
|
||||
// react-router
|
||||
{
|
||||
files: ['../packages/react-router/dist/index.js']
|
||||
}
|
||||
].forEach(testPackage);
|
||||
|
||||
function testPackage(testPkg) {
|
||||
if (testPkg.packageJson) {
|
||||
const pkgDir = path.dirname(testPkg.packageJson);
|
||||
const pkgJson = require(testPkg.packageJson);
|
||||
|
||||
if (!pkgJson.name) {
|
||||
throw new Error('missing package.json name: ' + testPkg.packageJson);
|
||||
}
|
||||
|
||||
if (!pkgJson.main) {
|
||||
throw new Error('missing package.json main: ' + testPkg.packageJson);
|
||||
}
|
||||
|
||||
const pkgPath = path.join(pkgDir, pkgJson.main);
|
||||
const pkgImport = require(pkgPath);
|
||||
|
||||
if (testPkg.files) {
|
||||
if (!Array.isArray(pkgJson.files)) {
|
||||
throw new Error(testPkg.packageJson + ' missing "files" property');
|
||||
}
|
||||
testPkg.files.forEach(testPkgFile => {
|
||||
if (!pkgJson.files.includes(testPkgFile)) {
|
||||
throw new Error(testPkg.packageJson + ' missing file ' + testPkgFile);
|
||||
}
|
||||
|
||||
const filePath = path.join(__dirname, pkgDir, testPkgFile);
|
||||
fs.accessSync(filePath);
|
||||
});
|
||||
}
|
||||
|
||||
if (pkgJson.module) {
|
||||
const moduleIndex = path.join(__dirname, pkgDir, pkgJson.module);
|
||||
fs.accessSync(moduleIndex);
|
||||
}
|
||||
|
||||
if (pkgJson.types) {
|
||||
const pkgTypes = path.join(__dirname, pkgDir, pkgJson.types);
|
||||
fs.accessSync(pkgTypes);
|
||||
}
|
||||
|
||||
if (testPkg.exports) {
|
||||
testPkg.exports.forEach(exportName => {
|
||||
const m = pkgImport[exportName];
|
||||
if (!m) {
|
||||
throw new Error('export "' + exportName + '" not found in: ' + testPkg.packageJson);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (testPkg.files) {
|
||||
testPkg.files.forEach(file => {
|
||||
const filePath = path.join(__dirname, file);
|
||||
fs.statSync(filePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ test.dist`);
|
||||
107
BETA.md
107
BETA.md
@@ -1,107 +0,0 @@
|
||||
# Ionic Framework v6 Beta
|
||||
|
||||
Thanks for your interest in trying out the Framework v6 beta! We are looking for developers to help test our new changes and provide feedback so that we can make Framework v6 the best release yet! Follow this guide to get setup with the beta.
|
||||
|
||||
## Installation
|
||||
|
||||
We have worked to make the Framework v6 migration as easy as possible, so the upgrade process should be a breeze!
|
||||
|
||||
Developers can follow the guide below to begin updating their existing apps to Framework v6. If you want to try out Framework v6 in a new app, you can create a starter application using `ionic start` with the Ionic CLI and then follow the guide below. See https://ionicframework.com/docs/intro/cli for information on how to get started with a new Ionic Framework application.
|
||||
|
||||
> Note: Framework v6 is currently in beta, so do not push any apps running v6 to production!
|
||||
|
||||
### Ionic Vue
|
||||
|
||||
Ionic Vue developers should first begin by upgrading to the latest version of `vue` and `vue-router`. As of Framework v6, `vue@3.0.6+` is required.
|
||||
|
||||
```shell
|
||||
npm install vue@next vue-router@4
|
||||
```
|
||||
|
||||
Ionic Vue users have access to the new Custom Elements build of Framework v6. To make the most out of this improvement, we recommend using Webpack 5. To do this, developers should first install the latest version of the Vue CLI:
|
||||
|
||||
```shell
|
||||
npm install -g @vue/cli@next
|
||||
```
|
||||
|
||||
From there, they can upgrade all Vue CLI plugins which will automatically migrate them to Webpack 5:
|
||||
|
||||
```shell
|
||||
vue upgrade --next
|
||||
```
|
||||
|
||||
The new Vue CLI will automatically generate two different bundles based on your `browserslist` configuration: one for modern browsers and one for legacy browsers. New Ionic Vue starter apps will only generate the bundle for modern browsers, but some older starter apps may need to have their `.browserslistrc` file updated. You can ensure your app only builds for modern browsers by setting `.browserlistrc` to have the following content:
|
||||
|
||||
```
|
||||
> 1%, last 2 versions, not dead, not ie 11
|
||||
```
|
||||
|
||||
From there, developers can install the Framework v6 beta:
|
||||
|
||||
```shell
|
||||
npm install @ionic/vue@next @ionic/vue-router@next
|
||||
```
|
||||
|
||||
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
|
||||
|
||||
After that, you should be good to go! Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
|
||||
|
||||
### Ionic React
|
||||
|
||||
Ionic React developers should first begin by upgrading to the latest version of `react` and `react-dom`. As of Framework v6, `react@17+` is required:
|
||||
|
||||
```shell
|
||||
npm install react@latest react-dom@latest
|
||||
```
|
||||
|
||||
From there, developers can install the Framework v6 beta:
|
||||
|
||||
```shell
|
||||
npm install @ionic/react@next @ionic/react-router@next
|
||||
```
|
||||
|
||||
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
|
||||
|
||||
After that, you should be good to go! Be sure to review the other breaking changes: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
|
||||
|
||||
Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
|
||||
|
||||
### Ionic Angular
|
||||
|
||||
Ionic Angular developers should first begin by upgrading to the latest version of Angular. As of Framework v6, Angular 11+ is required.
|
||||
|
||||
Please see https://update.angular.io/ for a guide on how to update to the latest version of Angular.
|
||||
|
||||
From there, developers can install the Framework v6 beta:
|
||||
|
||||
```shell
|
||||
npm install @ionic/angular@next
|
||||
```
|
||||
|
||||
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
|
||||
|
||||
After that, you should be good to go! Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
|
||||
|
||||
### Ionic Core
|
||||
|
||||
Developers using `@ionic/core` directly should install the Framework v6 beta directly:
|
||||
|
||||
```shell
|
||||
npm install @ionic/core@next
|
||||
```
|
||||
|
||||
If you are using Ionic Framework in a Stencil app, be sure to update to the latest version of Stencil as well:
|
||||
|
||||
```shell
|
||||
npm install @stencil/core@latest
|
||||
```
|
||||
|
||||
Next, developers should review the breaking changes and make any changes necessary in their apps: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.md
|
||||
|
||||
After that, you should be good to go! Check out https://beta.ionicframework.com/docs for the Framework v6 documentation.
|
||||
|
||||
## Providing Feedback
|
||||
|
||||
Feedback should be provided on our GitHub repo by creating a new issue: https://github.com/ionic-team/ionic-framework/issues/new/choose
|
||||
|
||||
Please note in the issue title that you are using the Framework v6 beta!
|
||||
391
BREAKING.md
391
BREAKING.md
@@ -4,402 +4,11 @@ This is a comprehensive list of the breaking changes introduced in the major ver
|
||||
|
||||
## Versions
|
||||
|
||||
- [Version 6.x](#version-6x)
|
||||
- [Version 5.x](#version-5x)
|
||||
- [Version 4.x](#version-4x)
|
||||
- [Legacy](#legacy)
|
||||
|
||||
|
||||
## Version 6.x
|
||||
|
||||
- [Components](#components)
|
||||
* [Datetime](#datetime)
|
||||
* [Header](#header)
|
||||
* [Icons](#icons)
|
||||
* [Input](#input)
|
||||
* [Modal](#modal)
|
||||
* [Popover](#popover)
|
||||
* [Radio](#radio)
|
||||
* [Searchbar](#searchbar)
|
||||
* [Select](#select)
|
||||
* [Tab Bar](#tab-bar)
|
||||
* [Textarea](#textarea)
|
||||
* [Toast](#toast)
|
||||
* [Toolbar](#toolbar)
|
||||
- [Config](#config)
|
||||
* [Transition Shadow](#transition-shadow)
|
||||
- [Angular](#angular)
|
||||
* [Config](#config-1)
|
||||
- [Vue](#vue)
|
||||
* [Config](#config-2)
|
||||
* [Tabs Config](#tabs-config)
|
||||
* [Tabs Router Outlet](#tabs-router-outlet)
|
||||
* [Overlay Events](#overlay-events)
|
||||
* [Utility Function Types](#utility-function-types)
|
||||
- [React](#react)
|
||||
* [Config](#config-3)
|
||||
- [Browser and Platform Support](#browser-and-platform-support)
|
||||
|
||||
|
||||
### Components
|
||||
|
||||
#### Datetime
|
||||
|
||||
The `ion-datetime` component has undergone a complete rewrite and uses a new calendar style. As a result, some of the properties no longer apply and have been removed.
|
||||
|
||||
- `ion-datetime` now displays the calendar inline by default, allowing for more flexibility in presentation. As a result, the `placeholder` property has been removed. Additionally, the `text` and `placeholder` Shadow Parts have been removed.
|
||||
|
||||
- The `--padding-bottom`, `--padding-end`, `--padding-start`, `--padding-top`, and `--placeholder-color` CSS Variables have been removed since `ion-datetime` now displays inline by default.
|
||||
|
||||
- The `displayFormat` and `displayTimezone` properties have been removed since `ion-datetime` now displays inline with a calendar picker. To parse the UTC string provided in the payload of the `ionChange` event, we recommend using a 3rd-party date library like [date-fns](https://date-fns.org/). Here is an example of how you can take the UTC string from `ion-datetime` and format it to whatever style you prefer:
|
||||
|
||||
```typescript
|
||||
import { format, parseISO } from 'date-fns';
|
||||
|
||||
/**
|
||||
* This is provided in the event
|
||||
* payload from the `ionChange` event.
|
||||
*/
|
||||
const dateFromIonDatetime = '2021-06-04T14:23:00-04:00';
|
||||
const formattedString = format(parseISO(dateFromIonDatetime), 'MMM d, yyyy');
|
||||
|
||||
console.log(formattedString); // Jun 4, 2021
|
||||
```
|
||||
|
||||
- The `pickerOptions` and `pickerFormat` properties have been removed since `ion-datetime` now uses a calendar style rather than a wheel picker style.
|
||||
|
||||
- The `monthNames`, `monthShortNames`, `dayNames`, and `dayShortNames` properties have been removed. `ion-datetime` can now automatically format these values according to your devices locale thanks to the [Intl.DateTimeFormat API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat). If you wish to force a specific locale, you can use the new `locale` property:
|
||||
|
||||
```html
|
||||
<ion-datetime locale="fr-FR"></ion-datetime>
|
||||
```
|
||||
|
||||
- The `open` method has been removed. To present the datetime in an overlay, you can pass it into an `ion-modal` or `ion-popover` component and call the `present` method on the overlay instance. Alternatively, you can use the `trigger` property on `ion-modal` or `ion-popover` to present the overlay on a button click:
|
||||
|
||||
```html
|
||||
<ion-button id="open-modal">Open Datetime Modal</ion-button>
|
||||
<ion-modal trigger="open-modal">
|
||||
<ion-datetime></ion-datetime>
|
||||
</ion-modal>
|
||||
```
|
||||
|
||||
#### Header
|
||||
|
||||
When using a collapsible large title, the last toolbar in the header with `collapse="condense"` no longer has a border. This does not affect the toolbar when the large title is collapsed.
|
||||
|
||||
To get the old style back, add the following CSS to your global stylesheet:
|
||||
|
||||
```css
|
||||
ion-header.header-collapse-condense ion-toolbar:last-of-type {
|
||||
--border-width: 0 0 0.55px;
|
||||
}
|
||||
```
|
||||
|
||||
#### Icons
|
||||
|
||||
Ionic 6 now ships with Ionicons 6. Please be sure to review the [Ionicons 6.0.0 Changelog](https://github.com/ionic-team/ionicons/releases/tag/v6.0.0) and make any necessary changes.
|
||||
|
||||
#### Input
|
||||
|
||||
The `placeholder` property now has a type of `string | undefined` rather than `null | string | undefined`.
|
||||
|
||||
#### Modal
|
||||
|
||||
Converted `ion-modal` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
|
||||
|
||||
If you were targeting the internals of `ion-modal` in your CSS, you will need to target the `backdrop` or `content` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
|
||||
|
||||
Developers dynamically creating modals using `document.createElement('ion-modal')` will now need to call `modal.remove()` after the modal has been dismissed if they want the modal to be removed from the DOM.
|
||||
|
||||
#### Popover
|
||||
|
||||
Converted `ion-popover` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
|
||||
|
||||
If you were targeting the internals of `ion-popover` in your CSS, you will need to target the `backdrop`, `arrow`, or `content` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
|
||||
|
||||
Developers dynamically creating popovers using `document.createElement('ion-popover')` will now need to call `popover.remove()` after the popover has been dismissed if they want the popover to be removed from the DOM.
|
||||
|
||||
#### Radio
|
||||
|
||||
The `RadioChangeEventDetail` interface has been removed. Instead, listen for the `ionChange` event on `ion-radio-group` and use the `RadioGroupChangeEventDetail` interface.
|
||||
|
||||
#### Searchbar
|
||||
|
||||
The `showClearButton` property now defaults to `'always'` for improved usability with screen readers.
|
||||
|
||||
To get the old behavior, set `showClearButton` to `'focus'`.
|
||||
|
||||
#### Select
|
||||
|
||||
The `placeholder` property now has a type of `string | undefined` rather than `null | string | undefined`.
|
||||
|
||||
#### Tab Bar
|
||||
|
||||
The default iOS tab bar background color has been updated to better reflect the latest iOS styles. The new default value is:
|
||||
|
||||
```css
|
||||
var(--ion-tab-bar-background, var(--ion-color-step-50, #f7f7f7));
|
||||
```
|
||||
|
||||
#### Textarea
|
||||
|
||||
The `placeholder` property now has a type of `string | undefined` rather than `null | string | undefined`.
|
||||
|
||||
#### Toast
|
||||
|
||||
The `--white-space` CSS variable now defaults to `normal` instead of `pre-wrap`.
|
||||
|
||||
#### Toolbar
|
||||
|
||||
The default iOS toolbar background color has been updated to better reflect the latest iOS styles. The new default value is:
|
||||
|
||||
```css
|
||||
var(--ion-toolbar-background, var(--ion-color-step-50, #f7f7f7));
|
||||
```
|
||||
|
||||
|
||||
### Config
|
||||
|
||||
#### Transition Shadow
|
||||
|
||||
The `experimentalTransitionShadow` config option has been removed. The transition shadow is now enabled when running in `ios` mode.
|
||||
|
||||
|
||||
### Angular
|
||||
|
||||
#### Config
|
||||
|
||||
The `Config.set()` method has been removed. See https://ionicframework.com/docs/angular/config for examples on how to set config globally, per-component, and per-platform.
|
||||
|
||||
Additionally, the `setupConfig` function is no longer exported from `@ionic/angular`. Developers should use `IonicModule.forRoot` to set the config instead. See https://ionicframework.com/docs/angular/config for more information.
|
||||
|
||||
### React
|
||||
|
||||
#### Config
|
||||
|
||||
All Ionic React applications must now import `setupIonicReact` from `@ionic/react` and call it. If you are setting a custom config with `setupConfig`, pass your config directly to `setupIonicReact` instead:
|
||||
|
||||
**Old**
|
||||
```javascript
|
||||
import { setupConfig } from '@ionic/react';
|
||||
|
||||
setupConfig({
|
||||
mode: 'md'
|
||||
})
|
||||
```
|
||||
|
||||
**New**
|
||||
```javascript
|
||||
import { setupIonicReact } from '@ionic/react';
|
||||
|
||||
setupIonicReact({
|
||||
mode: 'md'
|
||||
})
|
||||
```
|
||||
|
||||
Note that all Ionic React applications must call `setupIonicReact` even if they are not setting custom configuration.
|
||||
|
||||
Additionally, the `setupConfig` function is no longer exported from `@ionic/react`.
|
||||
|
||||
### Vue
|
||||
|
||||
#### Config
|
||||
|
||||
The `setupConfig` function is no longer exported from `@ionic/vue`. Developers should pass their config into the `IonicVue` plugin. See https://ionicframework.com/docs/vue/config for more information.
|
||||
|
||||
#### Tabs Config
|
||||
|
||||
Support for child routes nested inside of tabs has been removed to better conform to Vue Router's best practices. Additional routes should be written as sibling routes with the parent tab as the path prefix:
|
||||
|
||||
**Old**
|
||||
```typescript
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/tabs/tab1'
|
||||
},
|
||||
{
|
||||
path: '/tabs/',
|
||||
component: Tabs,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirect: 'tab1'
|
||||
},
|
||||
{
|
||||
path: 'tab1',
|
||||
component: () => import('@/views/Tab1.vue'),
|
||||
children: {
|
||||
{
|
||||
path: 'view',
|
||||
component: () => import('@/views/Tab1View.vue')
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tab2',
|
||||
component: () => import('@/views/Tab2.vue')
|
||||
},
|
||||
{
|
||||
path: 'tab3',
|
||||
component: () => import('@/views/Tab3.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**New**
|
||||
```typescript
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/tabs/tab1'
|
||||
},
|
||||
{
|
||||
path: '/tabs/',
|
||||
component: Tabs,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
redirect: 'tab1'
|
||||
},
|
||||
{
|
||||
path: 'tab1',
|
||||
component: () => import('@/views/Tab1.vue')
|
||||
},
|
||||
{
|
||||
path: 'tab1/view',
|
||||
component: () => import('@/views/Tab1View.vue')
|
||||
},
|
||||
{
|
||||
path: 'tab2',
|
||||
component: () => import('@/views/Tab2.vue')
|
||||
},
|
||||
{
|
||||
path: 'tab3',
|
||||
component: () => import('@/views/Tab3.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
In the example above `tabs/tab1/view` has been rewritten has a sibling route to `tabs/tab1`. The `path` field now includes the `tab1` prefix.
|
||||
|
||||
#### Tabs Router Outlet
|
||||
|
||||
Developers must now provide an `ion-router-outlet` inside of `ion-tabs`. Previously one was generated automatically, but this made it difficult for developers to access the properties on the generated `ion-router-outlet`.
|
||||
|
||||
**Old**
|
||||
```html
|
||||
<ion-tabs>
|
||||
<ion-tab-bar slot="bottom">
|
||||
...
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
|
||||
<script>
|
||||
import { IonTabs, IonTabBar } from '@ionic/vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: { IonTabs, IonTabBar }
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
**New**
|
||||
```html
|
||||
<ion-tabs>
|
||||
<ion-router-outlet></ion-router-outlet>
|
||||
<ion-tab-bar slot="bottom">
|
||||
...
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
|
||||
<script>
|
||||
import { IonTabs, IonTabBar, IonRouterOutlet } from '@ionic/vue';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: { IonTabs, IonTabBar, IonRouterOutlet }
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
#### Overlay Events
|
||||
|
||||
Overlay events `onWillPresent`, `onDidPresent`, `onWillDismiss`, and `onDidDismiss` have been removed in favor of `willPresent`, `didPresent`, `willDismiss`, and `didDismiss`.
|
||||
|
||||
This applies to the following components: `ion-action-sheet`, `ion-alert`, `ion-loading`, `ion-modal`, `ion-picker`, `ion-popover`, and `ion-toast`.
|
||||
|
||||
**Old**
|
||||
```html
|
||||
<ion-modal
|
||||
:is-open="modalOpenRef"
|
||||
@onWillPresent="onModalWillPresentHandler"
|
||||
@onDidPresent="onModalDidPresentHandler"
|
||||
@onWillDismiss="onModalWillDismissHandler"
|
||||
@onDidDismiss="onModalDidDismissHandler"
|
||||
>
|
||||
...
|
||||
</ion-modal>
|
||||
```
|
||||
|
||||
**New**
|
||||
```html
|
||||
<ion-modal
|
||||
:is-open="modalOpenRef"
|
||||
@willPresent="onModalWillPresentHandler"
|
||||
@didPresent="onModalDidPresentHandler"
|
||||
@willDismiss="onModalWillDismissHandler"
|
||||
@didDismiss="onModalDidDismissHandler"
|
||||
>
|
||||
...
|
||||
</ion-modal>
|
||||
```
|
||||
|
||||
#### Utility Function Types
|
||||
|
||||
- The `IonRouter` type for `useIonRouter` has been renamed to `UseIonRouterResult`.
|
||||
|
||||
- The `IonKeyboardRef` type for `useKeyboard` has been renamed to `UseKeyboardResult`.
|
||||
|
||||
|
||||
### Browser and Platform Support
|
||||
|
||||
This section details the desktop browser, JavaScript framework, and mobile platform versions that are supported by Ionic Framework v6.
|
||||
|
||||
**Minimum Browser Versions**
|
||||
| Desktop Browser | Supported Versions |
|
||||
| --------------- | ----------------- |
|
||||
| Chrome | 60+ |
|
||||
| Safari | 13+ |
|
||||
| Firefox | 63+ |
|
||||
| Edge | 79+ |
|
||||
|
||||
**Minimum JavaScript Framework Versions**
|
||||
|
||||
| Framework | Supported Version |
|
||||
| --------- | --------------------- |
|
||||
| Angular | 12+ |
|
||||
| React | 17+ |
|
||||
| Vue | 3.0.6+ |
|
||||
|
||||
**Minimum Mobile Platform Versions**
|
||||
|
||||
| Platform | Supported Version |
|
||||
| -------- | --------------------------------------- |
|
||||
| iOS | 13+ |
|
||||
| Android | 5.0+ with Chromium 60+ (See note below) |
|
||||
|
||||
Starting with Android 5.0, the webview was moved to a separate application that can be updated independently of Android. This means that most Android 5.0+ devices are going to be running a modern version of Chromium. However, there are a still a subset of Android devices whose manufacturer has locked the webview version and does not allow the webview to update. These webviews are typically stuck at the version that was available when the device initially shipped.
|
||||
|
||||
As a result, Ionic Framework only supports Android devices and emulators running Android 5.0+ with a webview of Chromium 60 or newer. For context, this is the version that Stencil can support with no polyfills: https://stenciljs.com/docs/browser-support
|
||||
|
||||
|
||||
|
||||
## Version 5.x
|
||||
|
||||
- [CSS](#css)
|
||||
|
||||
1607
CHANGELOG.md
1607
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -1,129 +1,11 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
As contributors and maintainers of the Ionic project, we pledge to respect everyone who contributes by posting issues, updating documentation, submitting pull requests, providing feedback in comments, and any other activities.
|
||||
|
||||
## Our Pledge
|
||||
Communication through any of Ionic's channels (GitHub, Slack, Forum, IRC, mailing lists, Twitter, etc.) must be constructive and never resort to personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
We promise to extend courtesy and respect to everyone involved in this project regardless of gender, gender identity, sexual orientation, disability, age, race, ethnicity, religion, or level of experience. We expect anyone contributing to the Ionic project to do the same.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
If any member of the community violates this code of conduct, the maintainers of the Ionic project may take action, removing issues, comments, and PRs or blocking accounts as deemed appropriate.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include the ability to:
|
||||
|
||||
* Demonstrate empathy and kindness towards people
|
||||
* Be respectful of differing opinions, viewpoints, and experiences
|
||||
* Give and gracefully accept constructive feedback
|
||||
* Accept responsibility and apologize to those affected by our mistakes,
|
||||
and learn from the experience
|
||||
* Focus on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[contact@ionic.io](mailto:contact@ionic.io).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
If you are subject to or witness unacceptable behavior, or have any other concerns, please email us at [hi@ionicframework.com](mailto:hi@ionicframework.com).
|
||||
|
||||
85
README.md
85
README.md
@@ -1,63 +1,19 @@
|
||||
<p align="center">
|
||||
<a href="#">
|
||||
<img alt="Ionic" src="https://github.com/ionic-team/ionic-framework/blob/main/.github/assets/logo.png?raw=true" width="60" />
|
||||
</a>
|
||||
</p>
|
||||
# Ionic
|
||||
|
||||
<h1 align="center">
|
||||
Ionic
|
||||
</h1>
|
||||
[Ionic](https://ionicframework.com/) is the open-source mobile app development framework that makes it easy to
|
||||
build top quality native and progressive web apps with web technologies.
|
||||
|
||||
<p align="center">
|
||||
Ionic is an open source app development toolkit for building modern, fast, top-quality cross-platform native and Progressive Web Apps from a single codebase with JavaScript and the Web.
|
||||
</p>
|
||||
<p align="center">
|
||||
Ionic is based on <a href="https://www.webcomponents.org/introduction">Web Components</a>, which enables significant performance, usability, and feature improvements alongside support for popular web frameworks like <a href="https://angular.io/">Angular</a>, <a href="https://reactjs.com/">React</a>, and <a href="https://vuejs.org/">Vue</a>.
|
||||
Ionic is based on [Web Components](https://www.webcomponents.org/introduction) and comes with many significant performance, usability, and feature improvements over the past versions.
|
||||
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/ionic-team/ionic-framework/blob/main/LICENSE">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Ionic Framework is released under the MIT license." />
|
||||
</a>
|
||||
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">
|
||||
<img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome!" />
|
||||
</a>
|
||||
<a href="https://twitter.com/Ionicframework">
|
||||
<img src="https://img.shields.io/twitter/follow/ionicframework.svg?label=Follow%20@IonicFramework" alt="Follow @IonicFramework">
|
||||
</a>
|
||||
<a href="https://ionic.link/discord">
|
||||
<img src="https://img.shields.io/discord/520266681499779082?color=7289DA&label=%23ionic&logo=discord&logoColor=white" alt="Official Ionic Discord" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h2 align="center">
|
||||
<a href="https://ionicframework.com/getting-started/">Quickstart</a>
|
||||
<span> · </span>
|
||||
<a href="https://ionicframework.com/docs/">
|
||||
Documentation
|
||||
</a>
|
||||
<span> · </span>
|
||||
<a href="https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md">Contribute</a>
|
||||
<span> · </span>
|
||||
<a href="https://blog.ionicframework.com/">Blog</a>
|
||||
<br />
|
||||
Community:
|
||||
<a href="https://ionic.link/discord">Discord</a>
|
||||
<span> · </span>
|
||||
<a href="https://forum.ionicframework.com/">Forums</a>
|
||||
<span> · </span>
|
||||
<a href="https://twitter.com/Ionicframework">Twitter</a>
|
||||
</h2>
|
||||
|
||||
### Packages
|
||||
|
||||
| Project | Package | Version | Downloads| Links |
|
||||
| ------- | ------- | ------- | -------- |:-----:|
|
||||
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [](https://www.npmjs.com/package/@ionic/core) | <a href="https://www.npmjs.com/package/@ionic/core" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/core.svg" alt="NPM Downloads" /></a> | [`README.md`](core/README.md)
|
||||
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [](https://www.npmjs.com/package/@ionic/angular) | <a href="https://www.npmjs.com/package/@ionic/angular" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/angular.svg" alt="NPM Downloads" /></a> | [`README.md`](angular/README.md)
|
||||
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [](https://www.npmjs.com/package/@ionic/vue) | <a href="https://www.npmjs.com/package/@ionic/vue" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/vue.svg" alt="NPM Downloads" /></a> | [`README.md`](packages/vue/README.md)
|
||||
| **React** | [`@ionic/react`](https://www.npmjs.com/package/@ionic/react) | [](https://www.npmjs.com/package/@ionic/react) | <a href="https://www.npmjs.com/package/@ionic/react" target="_blank"><img src="https://img.shields.io/npm/dm/@ionic/react.svg" alt="NPM Downloads" /></a> |[`README.md`](packages/react/README.md)
|
||||
| Project | Package | Version | Links |
|
||||
| ------- | ------- | ------- |:-----:|
|
||||
| **Core** | [`@ionic/core`](https://www.npmjs.com/package/@ionic/core) | [](https://www.npmjs.com/package/@ionic/core) | [`README.md`](core/README.md)
|
||||
| **Angular** | [`@ionic/angular`](https://www.npmjs.com/package/@ionic/angular) | [](https://www.npmjs.com/package/@ionic/angular) | [`README.md`](angular/README.md)
|
||||
| **Vue** | [`@ionic/vue`](https://www.npmjs.com/package/@ionic/vue) | [](https://www.npmjs.com/package/@ionic/vue) | [`README.md`](vue/README.md)
|
||||
| **React** | [`@ionic/react`](https://www.npmjs.com/package/@ionic/react) | [](https://www.npmjs.com/package/@ionic/react) | [`README.md`](packages/react/README.md)
|
||||
|
||||
Looking for the `ionic-angular` package? Ionic 3 has been moved to the [`ionic-v3`](https://github.com/ionic-team/ionic-v3) repo. See [Earlier Versions](#earlier-versions).
|
||||
|
||||
@@ -67,31 +23,24 @@ Start a new project by following our quick [Getting Started guide](https://ionic
|
||||
We would love to hear from you! If you have any feedback or run into issues using our framework, please file
|
||||
an [issue](https://github.com/ionic-team/ionic/issues/new) on this repository.
|
||||
|
||||
### Migration Guides
|
||||
|
||||
Already have an Ionic app? These guides will help you migrate to the latest versions.
|
||||
### Contributing
|
||||
|
||||
Thanks for your interest in contributing! Read up on our guidelines for
|
||||
[contributing](https://github.com/ionic-team/ionic/blob/master/.github/CONTRIBUTING.md)
|
||||
and then look through our issues with a [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
||||
label.
|
||||
|
||||
* [Migrate from v5 to v6](https://ionicframework.com/docs/reference/migration#migrating-from-ionic-5x-to-ionic-6x)
|
||||
* [Migrate from v4 to v5](https://ionicframework.com/docs/reference/migration#migrating-from-ionic-4x-to-ionic-5x)
|
||||
* [Migrate from v3 to v4](https://ionicframework.com/docs/reference/migration#migrating-from-ionic-30-to-ionic-40)
|
||||
|
||||
### Examples
|
||||
|
||||
The [Ionic Conference App](https://github.com/ionic-team/ionic-conference-app) is a full featured Ionic app.
|
||||
It is the perfect starting point for learning and building your own app.
|
||||
|
||||
### Contributing
|
||||
|
||||
Thanks for your interest in contributing! Read up on our guidelines for
|
||||
[contributing](https://github.com/ionic-team/ionic/blob/main/.github/CONTRIBUTING.md)
|
||||
and then look through our issues with a [help wanted](https://github.com/ionic-team/ionic/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
|
||||
label.
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct](https://github.com/ionic-team/ionic/blob/main/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
||||
|
||||
### Future Goals
|
||||
|
||||
As Ionic Framework components migrate to the web component standard, a goal of ours is to have Ionic Framework easily work within all of the popular frameworks.
|
||||
As Ionic components migrate to the web component standard, a goal of ours is to have Ionic components easily work within all of the popular frameworks.
|
||||
|
||||
### Earlier Versions
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
dist
|
||||
virtual-scroll
|
||||
scripts
|
||||
proxies.ts
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["test/**/*", "src/directives/virtual-scroll/**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"parserOptions": {
|
||||
"project": ["tsconfig.json"],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"@ionic/eslint-config/recommended",
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-imports": "off",
|
||||
"@angular-eslint/component-class-suffix": "off",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "ion",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1
angular/.npmrc
Normal file
1
angular/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
package-lock=false
|
||||
@@ -1,6 +0,0 @@
|
||||
dist
|
||||
virtual-scroll
|
||||
scripts
|
||||
test
|
||||
src/directives/proxies.ts
|
||||
src/directives/angular-component-lib/utils.ts
|
||||
@@ -137,7 +137,7 @@ The back button is no longer added by default to a navigation bar. It should be
|
||||
</ion-toolbar>
|
||||
```
|
||||
|
||||
See the [back button documentation](https://github.com/ionic-team/ionic/blob/main/core/src/components/back-button) for more usage examples.
|
||||
See the [back button documentation](https://github.com/ionic-team/ionic/blob/master/core/src/components/back-button) for more usage examples.
|
||||
|
||||
## Button
|
||||
|
||||
@@ -507,7 +507,7 @@ _In the following examples, `{breakpoint}` refers to the optional screen breakpo
|
||||
- `push-{breakpoint}-{value}` attributes have been renamed to `push-{breakpoint}=“{value}”`
|
||||
- `pull-{breakpoint}-{value}` attributes have been renamed to `pull-{breakpoint}=“{value}”`
|
||||
|
||||
Customizing the padding and width of a grid should now be done with CSS variables. For more information, see [Grid Layout](https://github.com/ionic-team/ionic-docs/blob/main/src/pages/layout/grid.md).
|
||||
Customizing the padding and width of a grid should now be done with CSS variables. For more information, see [Grid Layout](https://github.com/ionic-team/ionic-docs/blob/master/src/pages/layout/grid.md).
|
||||
|
||||
## Icon
|
||||
|
||||
@@ -1689,7 +1689,7 @@ The tab attribute defines the route to be shown upon clicking on this tab.
|
||||
</ion-tabs>
|
||||
```
|
||||
|
||||
See more usage examples in the [Tabs](https://github.com/ionic-team/ionic/blob/main/core/src/components/tabs) documentation.
|
||||
See more usage examples in the [Tabs](https://github.com/ionic-team/ionic/blob/master/core/src/components/tabs) documentation.
|
||||
|
||||
|
||||
## Text / Typography
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [6.0.13](https://github.com/ionic-team/ionic/compare/v6.0.12...v6.0.13) (2022-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** ngOnDestroy runs inside angular zone ([#24949](https://github.com/ionic-team/ionic/issues/24949)) ([a8fd2d9](https://github.com/ionic-team/ionic/commit/a8fd2d9199ca92d62bce6abf8caccc7709fa5ca1)), closes [#22571](https://github.com/ionic-team/ionic/issues/22571)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.12](https://github.com/ionic-team/ionic/compare/v6.0.11...v6.0.12) (2022-03-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tabs:** angular, fire willChange event before selected tab changes ([#24910](https://github.com/ionic-team/ionic/issues/24910)) ([d5efa11](https://github.com/ionic-team/ionic/commit/d5efa113317eaf874712134dc9b8e4502aa4760f))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.11](https://github.com/ionic-team/ionic/compare/v6.0.10...v6.0.11) (2022-03-09)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.10](https://github.com/ionic-team/ionic/compare/v6.0.9...v6.0.10) (2022-03-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** .ion-page element is now correctly added ([#24811](https://github.com/ionic-team/ionic/issues/24811)) ([3d0f999](https://github.com/ionic-team/ionic/commit/3d0f99904fe192fcb5f529780858a0f25f076af7)), closes [#24809](https://github.com/ionic-team/ionic/issues/24809)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.9](https://github.com/ionic-team/ionic/compare/v6.0.8...v6.0.9) (2022-02-23)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.8](https://github.com/ionic-team/ionic/compare/v6.0.7...v6.0.8) (2022-02-15)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.7](https://github.com/ionic-team/ionic/compare/v6.0.6...v6.0.7) (2022-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** inline modals now add .ion-page class correctly ([#24751](https://github.com/ionic-team/ionic/issues/24751)) ([ef46eaf](https://github.com/ionic-team/ionic/commit/ef46eafc9476a85ea3369e542f528d01d3cca0a8)), closes [#24750](https://github.com/ionic-team/ionic/issues/24750)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.6](https://github.com/ionic-team/ionic/compare/v6.0.5...v6.0.6) (2022-02-09)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.5](https://github.com/ionic-team/ionic/compare/v6.0.4...v6.0.5) (2022-02-02)
|
||||
|
||||
**Note:** Version bump only for package @ionic/angular
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.4](https://github.com/ionic-team/ionic/compare/v6.0.3...v6.0.4) (2022-01-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** routerLink with null value works with Angular 13 ([#24622](https://github.com/ionic-team/ionic/issues/24622)) ([90a9a9c](https://github.com/ionic-team/ionic/commit/90a9a9c3e813c8db0a9d6b3b25c152929bea80fe)), closes [#24586](https://github.com/ionic-team/ionic/issues/24586)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.3](https://github.com/ionic-team/ionic/compare/v6.0.2...v6.0.3) (2022-01-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** apply touch, dirty and pristine form control classes ([#24558](https://github.com/ionic-team/ionic/issues/24558)) ([273ae2c](https://github.com/ionic-team/ionic/commit/273ae2cc087b2a5a30fb50a1b0eaeb0a221900fc)), closes [#24483](https://github.com/ionic-team/ionic/issues/24483)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [6.0.2](https://github.com/ionic-team/ionic/compare/v6.0.1...v6.0.2) (2022-01-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** attach change detector ref for inline overlays ([#24521](https://github.com/ionic-team/ionic/issues/24521)) ([5c54593](https://github.com/ionic-team/ionic/commit/5c54593dde64ae61347568405ebf74502cfff370)), closes [#24502](https://github.com/ionic-team/ionic/issues/24502)
|
||||
* **angular:** popover will respect side attribute value ([#24470](https://github.com/ionic-team/ionic/issues/24470)) ([e6955a2](https://github.com/ionic-team/ionic/commit/e6955a26b92fc536c5c73b60b5943881c7d58ee1)), closes [#24466](https://github.com/ionic-team/ionic/issues/24466)
|
||||
@@ -7,6 +7,7 @@ Ionic Angular specific building blocks on top of [@ionic/core](https://www.npmjs
|
||||
|
||||
* [Ionic Core Components](https://www.npmjs.com/package/@ionic/core)
|
||||
* [Ionic Documentation](https://ionicframework.com/docs/)
|
||||
* [Ionic Worldwide Slack](http://ionicworldwide.herokuapp.com/)
|
||||
* [Ionic Forum](https://forum.ionicframework.com/)
|
||||
* [Ionicons](http://ionicons.com/)
|
||||
* [Stencil](https://stenciljs.com/)
|
||||
@@ -16,11 +17,11 @@ Ionic Angular specific building blocks on top of [@ionic/core](https://www.npmjs
|
||||
|
||||
## License
|
||||
|
||||
* [MIT](https://raw.githubusercontent.com/ionic-team/ionic/main/LICENSE)
|
||||
* [MIT](https://raw.githubusercontent.com/ionic-team/ionic/master/LICENSE)
|
||||
|
||||
## Testing ng-add in ionic
|
||||
|
||||
1. Pull the latest from `main`
|
||||
1. Pull the latest from master
|
||||
2. Build ionic/angular: `npm run build`
|
||||
3. Run `npm link` from `ionic/angular/dist` directory
|
||||
4. Create a blank angular project
|
||||
|
||||
12597
angular/package-lock.json
generated
12597
angular/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/angular",
|
||||
"version": "6.0.13",
|
||||
"version": "5.1.1",
|
||||
"description": "Angular specific wrappers for @ionic/core",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -22,76 +22,65 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/ionic-team/ionic/issues"
|
||||
},
|
||||
"publishConfig": {
|
||||
"directory": "dist"
|
||||
},
|
||||
"homepage": "https://ionicframework.com/",
|
||||
"scripts": {
|
||||
"prepublishOnly": "npm run build",
|
||||
"build": "npm run clean && npm run build.ng && npm run build.core && npm run clean-generated",
|
||||
"build.core": "node scripts/build-core.js",
|
||||
"build.fesm": "rollup --config ./scripts/rollup.config.js",
|
||||
"build.link": "npm run build && node scripts/link-copy.js",
|
||||
"build.ng": "ng-packagr -p package.json -c tsconfig.json",
|
||||
"build.ng": "ng-packagr -p package.json",
|
||||
"build.es2015": "ngc -p tsconfig.json && rollup --config ./scripts/rollup.config.js",
|
||||
"build.es5": "ngc -p tsconfig.legacy.json && rollup --config ./scripts/rollup.config.legacy.js",
|
||||
"clean": "node scripts/clean.js",
|
||||
"clean-generated": "node ./scripts/clean-generated.js",
|
||||
"lint": "npm run eslint && npm run prettier -- --check",
|
||||
"fmt": "npm run eslint -- --fix && npm run prettier -- --write",
|
||||
"prettier": "prettier \"**/*.ts\"",
|
||||
"eslint": "eslint . --ext .ts",
|
||||
"lint": "npm run lint.ts",
|
||||
"lint.ts": "tslint --project .",
|
||||
"lint.fix": "tslint --project . --fix",
|
||||
"prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next",
|
||||
"test": "echo 'angular no tests yet'",
|
||||
"tsc": "tsc -p .",
|
||||
"validate": "npm i && npm run lint && npm run test && npm run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ionic/core": "^6.0.13",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
"@ionic/core": "5.1.1",
|
||||
"tslib": "^1.9.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=12.0.0",
|
||||
"@angular/forms": ">=12.0.0",
|
||||
"@angular/router": ">=12.0.0",
|
||||
"rxjs": ">=6.6.0",
|
||||
"zone.js": ">=0.11.0"
|
||||
"@angular/core": ">=8.2.7",
|
||||
"@angular/forms": ">=8.2.7",
|
||||
"@angular/router": ">=8.2.7",
|
||||
"rxjs": ">=6.2.0",
|
||||
"zone.js": ">=0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/core": "^12.0.0",
|
||||
"@angular-devkit/schematics": "^12.0.0",
|
||||
"@angular-eslint/eslint-plugin": "^12.5.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^12.5.0",
|
||||
"@angular-eslint/template-parser": "^12.5.0",
|
||||
"@angular/common": "^12.0.0",
|
||||
"@angular/compiler": "^12.0.0",
|
||||
"@angular/compiler-cli": "^12.0.0",
|
||||
"@angular/core": "^12.0.0",
|
||||
"@angular/forms": "^12.0.0",
|
||||
"@angular/router": "^12.0.0",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
"@schematics/angular": "^12.2.9",
|
||||
"@angular-devkit/core": "8.3.17",
|
||||
"@angular-devkit/schematics": "8.3.17",
|
||||
"@angular/common": "8.2.13",
|
||||
"@angular/compiler": "8.2.13",
|
||||
"@angular/compiler-cli": "8.2.13",
|
||||
"@angular/core": "8.2.13",
|
||||
"@angular/forms": "8.2.13",
|
||||
"@angular/router": "8.2.13",
|
||||
"@types/node": "12.12.5",
|
||||
"@typescript-eslint/eslint-plugin": "^4.32.0",
|
||||
"@typescript-eslint/parser": "^4.32.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"fs-extra": "^7.0.0",
|
||||
"ng-packagr": "^12.0.0",
|
||||
"prettier": "^2.4.1",
|
||||
"rxjs": "^6.6.2",
|
||||
"typescript": "4.2.4",
|
||||
"typescript-eslint-language-service": "^4.1.5",
|
||||
"zone.js": "~0.11.4"
|
||||
"glob": "^7.1.4",
|
||||
"ng-packagr": "^9.1.5",
|
||||
"rollup": "~1.17.0",
|
||||
"rollup-plugin-node-resolve": "~5.2.0",
|
||||
"rxjs": "^6.5.2",
|
||||
"tsickle": "^0.34.0",
|
||||
"tslint": "^5.12.1",
|
||||
"tslint-ionic-rules": "0.0.21",
|
||||
"typescript": "3.4.5",
|
||||
"zone.js": "^0.8.28"
|
||||
},
|
||||
"prettier": "@ionic/prettier-config",
|
||||
"schematics": "./schematics/collection.json",
|
||||
"ngPackage": {
|
||||
"lib": {
|
||||
"entryFile": "src/index.ts"
|
||||
},
|
||||
"allowedNonPeerDependencies": [
|
||||
"@ionic/core",
|
||||
"jsonc-parser"
|
||||
"whitelistedNonPeerDependencies": [
|
||||
"@ionic/core"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import resolve from 'rollup-plugin-node-resolve';
|
||||
|
||||
export default {
|
||||
input: 'build/es2015/core.js',
|
||||
|
||||
@@ -1,22 +1,30 @@
|
||||
import { NgZone } from '@angular/core';
|
||||
import { setupConfig } from '@ionic/core';
|
||||
import { applyPolyfills, defineCustomElements } from '@ionic/core/loader';
|
||||
|
||||
import { Config } from './providers/config';
|
||||
import { IonicWindow } from './types/interfaces';
|
||||
import { raf } from './util/util';
|
||||
|
||||
let didInitialize = false;
|
||||
|
||||
export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
|
||||
return (): any => {
|
||||
const win: IonicWindow | undefined = doc.defaultView as any;
|
||||
if (win && typeof (window as any) !== 'undefined') {
|
||||
setupConfig({
|
||||
...config,
|
||||
_zoneGate: (h: any) => zone.run(h),
|
||||
});
|
||||
if (didInitialize) {
|
||||
console.warn('Ionic Angular was already initialized. Make sure IonicModule.forRoot() is just called once.');
|
||||
}
|
||||
didInitialize = true;
|
||||
const Ionic = win.Ionic = win.Ionic || {};
|
||||
|
||||
const aelFn =
|
||||
'__zone_symbol__addEventListener' in (doc.body as any) ? '__zone_symbol__addEventListener' : 'addEventListener';
|
||||
Ionic.config = {
|
||||
...config,
|
||||
_zoneGate: (h: any) => zone.run(h)
|
||||
};
|
||||
|
||||
const aelFn = '__zone_symbol__addEventListener' in (doc.body as any)
|
||||
? '__zone_symbol__addEventListener'
|
||||
: 'addEventListener';
|
||||
|
||||
return applyPolyfills().then(() => {
|
||||
return defineCustomElements(win, {
|
||||
@@ -29,7 +37,7 @@ export const appInitialize = (config: Config, doc: Document, zone: NgZone) => {
|
||||
},
|
||||
rel(elm, eventName, cb, opts) {
|
||||
elm.removeEventListener(eventName, cb, opts);
|
||||
},
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import { fromEvent } from 'rxjs';
|
||||
|
||||
export const proxyInputs = (Cmp: any, inputs: string[]) => {
|
||||
const Prototype = Cmp.prototype;
|
||||
inputs.forEach(item => {
|
||||
Object.defineProperty(Prototype, item, {
|
||||
get() {
|
||||
return this.el[item];
|
||||
},
|
||||
set(val: any) {
|
||||
this.z.runOutsideAngular(() => (this.el[item] = val));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const proxyMethods = (Cmp: any, methods: string[]) => {
|
||||
const Prototype = Cmp.prototype;
|
||||
methods.forEach(methodName => {
|
||||
Prototype[methodName] = function () {
|
||||
const args = arguments;
|
||||
return this.z.runOutsideAngular(() =>
|
||||
this.el[methodName].apply(this.el, args)
|
||||
);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const proxyOutputs = (instance: any, el: any, events: string[]) => {
|
||||
events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
|
||||
}
|
||||
|
||||
export const defineCustomElement = (tagName: string, customElement: any) => {
|
||||
if (
|
||||
customElement !== undefined &&
|
||||
typeof customElements !== 'undefined' &&
|
||||
!customElements.get(tagName)
|
||||
) {
|
||||
customElements.define(tagName, customElement);
|
||||
}
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: only-arrow-functions
|
||||
export function ProxyCmp(opts: { defineCustomElementFn?: () => void, inputs?: any; methods?: any }) {
|
||||
const decorator = function (cls: any) {
|
||||
const { defineCustomElementFn, inputs, methods } = opts;
|
||||
|
||||
if (defineCustomElementFn !== undefined) {
|
||||
defineCustomElementFn();
|
||||
}
|
||||
|
||||
if (inputs) {
|
||||
proxyInputs(cls, inputs);
|
||||
}
|
||||
if (methods) {
|
||||
proxyMethods(cls, methods);
|
||||
}
|
||||
return cls;
|
||||
};
|
||||
return decorator;
|
||||
}
|
||||
@@ -1,30 +1,32 @@
|
||||
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor, setIonicClasses } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
/* tslint:disable-next-line:directive-selector */
|
||||
selector: 'ion-checkbox,ion-toggle',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: BooleanValueAccessorDirective,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
useExisting: BooleanValueAccessor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class BooleanValueAccessorDirective extends ValueAccessor {
|
||||
constructor(injector: Injector, el: ElementRef) {
|
||||
super(injector, el);
|
||||
export class BooleanValueAccessor extends ValueAccessor {
|
||||
|
||||
constructor(el: ElementRef) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
writeValue(value: any): void {
|
||||
writeValue(value: any) {
|
||||
this.el.nativeElement.checked = this.lastValue = value == null ? false : value;
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleIonChange(el: any): void {
|
||||
_handleIonChange(el: any) {
|
||||
this.handleChangeEvent(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,32 @@
|
||||
import { Directive, HostListener, ElementRef, Injector } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
|
||||
@Directive({
|
||||
/* tslint:disable-next-line:directive-selector */
|
||||
selector: 'ion-input[type=number]',
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: NumericValueAccessorDirective,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
useExisting: NumericValueAccessor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class NumericValueAccessorDirective extends ValueAccessor {
|
||||
constructor(injector: Injector, el: ElementRef) {
|
||||
super(injector, el);
|
||||
export class NumericValueAccessor extends ValueAccessor {
|
||||
|
||||
constructor(el: ElementRef) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleIonChange(el: any): void {
|
||||
_handleIonChange(el: any) {
|
||||
this.handleChangeEvent(el, el.value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number | null) => void): void {
|
||||
super.registerOnChange((value) => {
|
||||
registerOnChange(fn: (_: number | null) => void) {
|
||||
super.registerOnChange(value => {
|
||||
fn(value === '' ? null : parseFloat(value));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
@@ -9,18 +9,19 @@ import { ValueAccessor } from './value-accessor';
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: RadioValueAccessorDirective,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
useExisting: RadioValueAccessor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class RadioValueAccessorDirective extends ValueAccessor {
|
||||
constructor(injector: Injector, el: ElementRef) {
|
||||
super(injector, el);
|
||||
export class RadioValueAccessor extends ValueAccessor {
|
||||
|
||||
constructor(el: ElementRef) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionSelect', ['$event.target'])
|
||||
_handleIonSelect(el: any): void {
|
||||
_handleIonSelect(el: any) {
|
||||
this.handleChangeEvent(el, el.checked);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
@@ -9,18 +9,19 @@ import { ValueAccessor } from './value-accessor';
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: SelectValueAccessorDirective,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
useExisting: SelectValueAccessor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class SelectValueAccessorDirective extends ValueAccessor {
|
||||
constructor(injector: Injector, el: ElementRef) {
|
||||
super(injector, el);
|
||||
export class SelectValueAccessor extends ValueAccessor {
|
||||
|
||||
constructor(el: ElementRef) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleChangeEvent(el: any): void {
|
||||
_handleChangeEvent(el: any) {
|
||||
this.handleChangeEvent(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ElementRef, Injector, Directive, HostListener } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
|
||||
import { ValueAccessor } from './value-accessor';
|
||||
@@ -9,18 +9,19 @@ import { ValueAccessor } from './value-accessor';
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: TextValueAccessorDirective,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
useExisting: TextValueAccessor,
|
||||
multi: true
|
||||
}
|
||||
]
|
||||
})
|
||||
export class TextValueAccessorDirective extends ValueAccessor {
|
||||
constructor(injector: Injector, el: ElementRef) {
|
||||
super(injector, el);
|
||||
export class TextValueAccessor extends ValueAccessor {
|
||||
|
||||
constructor(el: ElementRef) {
|
||||
super(el);
|
||||
}
|
||||
|
||||
@HostListener('ionChange', ['$event.target'])
|
||||
_handleInputEvent(el: any): void {
|
||||
_handleInputEvent(el: any) {
|
||||
this.handleChangeEvent(el, el.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
import { AfterViewInit, ElementRef, Injector, OnDestroy, Directive, HostListener } from '@angular/core';
|
||||
import { ControlValueAccessor, NgControl } from '@angular/forms';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ElementRef, HostListener } from '@angular/core';
|
||||
import { ControlValueAccessor } from '@angular/forms';
|
||||
|
||||
import { raf } from '../../util/util';
|
||||
|
||||
@Directive()
|
||||
export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDestroy {
|
||||
private onChange: (value: any) => void = () => {
|
||||
/**/
|
||||
};
|
||||
private onTouched: () => void = () => {
|
||||
/**/
|
||||
};
|
||||
export class ValueAccessor implements ControlValueAccessor {
|
||||
|
||||
private onChange: (value: any) => void = () => {/**/};
|
||||
private onTouched: () => void = () => {/**/};
|
||||
protected lastValue: any;
|
||||
private statusChanges?: Subscription;
|
||||
|
||||
constructor(protected injector: Injector, protected el: ElementRef) {}
|
||||
constructor(protected el: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
writeValue(value: any) {
|
||||
/**
|
||||
* TODO for Ionic 6:
|
||||
* Change `value == null ? '' : value;`
|
||||
@@ -29,7 +23,7 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
|
||||
handleChangeEvent(el: HTMLElement, value: any): void {
|
||||
handleChangeEvent(el: HTMLElement, value: any) {
|
||||
if (el === this.el.nativeElement) {
|
||||
if (value !== this.lastValue) {
|
||||
this.lastValue = value;
|
||||
@@ -40,75 +34,27 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
|
||||
}
|
||||
|
||||
@HostListener('ionBlur', ['$event.target'])
|
||||
_handleBlurEvent(el: any): void {
|
||||
_handleBlurEvent(el: any) {
|
||||
if (el === this.el.nativeElement) {
|
||||
this.onTouched();
|
||||
setIonicClasses(this.el);
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: any) => void): void {
|
||||
registerOnChange(fn: (value: any) => void) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => void): void {
|
||||
registerOnTouched(fn: () => void) {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
setDisabledState(isDisabled: boolean) {
|
||||
this.el.nativeElement.disabled = isDisabled;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.statusChanges) {
|
||||
this.statusChanges.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
let ngControl;
|
||||
try {
|
||||
ngControl = this.injector.get<NgControl>(NgControl);
|
||||
} catch {
|
||||
/* No FormControl or ngModel binding */
|
||||
}
|
||||
|
||||
if (!ngControl) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen for changes in validity, disabled, or pending states
|
||||
if (ngControl.statusChanges) {
|
||||
this.statusChanges = ngControl.statusChanges.subscribe(() => setIonicClasses(this.el));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO Remove this in favor of https://github.com/angular/angular/issues/10887
|
||||
* whenever it is implemented. Currently, Ionic's form status classes
|
||||
* do not react to changes when developers manually call
|
||||
* Angular form control methods such as markAsTouched.
|
||||
* This results in Ionic's form status classes being out
|
||||
* of sync with the ng form status classes.
|
||||
* This patches the methods to manually sync
|
||||
* the classes until this feature is implemented in Angular.
|
||||
*/
|
||||
const formControl = ngControl.control as any;
|
||||
if (formControl) {
|
||||
const methodsToPatch = ['markAsTouched', 'markAllAsTouched', 'markAsUntouched', 'markAsDirty', 'markAsPristine'];
|
||||
methodsToPatch.forEach((method) => {
|
||||
if (typeof formControl[method] !== 'undefined') {
|
||||
const oldFn = formControl[method].bind(formControl);
|
||||
formControl[method] = (...params: any[]) => {
|
||||
oldFn(...params);
|
||||
setIonicClasses(this.el);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const setIonicClasses = (element: ElementRef): void => {
|
||||
export const setIonicClasses = (element: ElementRef) => {
|
||||
raf(() => {
|
||||
const input = element.nativeElement as HTMLElement;
|
||||
const classes = getClasses(input);
|
||||
@@ -135,11 +81,16 @@ const getClasses = (element: HTMLElement) => {
|
||||
|
||||
const setClasses = (element: HTMLElement, classes: string[]) => {
|
||||
const classList = element.classList;
|
||||
['ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine'].forEach((c) =>
|
||||
classList.remove(c)
|
||||
);
|
||||
[
|
||||
'ion-valid',
|
||||
'ion-invalid',
|
||||
'ion-touched',
|
||||
'ion-untouched',
|
||||
'ion-dirty',
|
||||
'ion-pristine'
|
||||
].forEach(c => classList.remove(c));
|
||||
|
||||
classes.forEach((c) => classList.add(c));
|
||||
classes.forEach(c => classList.add(c));
|
||||
};
|
||||
|
||||
const startsWith = (input: string, search: string): boolean => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { AnimationBuilder } from '@ionic/core';
|
||||
import { Directive, HostListener, Optional } from '@angular/core';
|
||||
|
||||
import { Config } from '../../providers/config';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
@@ -8,13 +7,11 @@ import { IonRouterOutlet } from './ion-router-outlet';
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-back-button',
|
||||
inputs: ['defaultHref'],
|
||||
})
|
||||
export class IonBackButtonDelegateDirective {
|
||||
@Input()
|
||||
defaultHref: string | undefined | null;
|
||||
export class IonBackButtonDelegate {
|
||||
|
||||
@Input()
|
||||
routerAnimation?: AnimationBuilder;
|
||||
defaultHref: string | undefined | null;
|
||||
|
||||
constructor(
|
||||
@Optional() private routerOutlet: IonRouterOutlet,
|
||||
@@ -26,15 +23,14 @@ export class IonBackButtonDelegateDirective {
|
||||
* @internal
|
||||
*/
|
||||
@HostListener('click', ['$event'])
|
||||
onClick(ev: Event): void {
|
||||
onClick(ev: Event) {
|
||||
const defaultHref = this.defaultHref || this.config.get('backButtonDefaultHref');
|
||||
|
||||
if (this.routerOutlet?.canGoBack()) {
|
||||
this.navCtrl.setDirection('back', undefined, undefined, this.routerAnimation);
|
||||
if (this.routerOutlet && this.routerOutlet.canGoBack()) {
|
||||
this.routerOutlet.pop();
|
||||
ev.preventDefault();
|
||||
} else if (defaultHref != null) {
|
||||
this.navCtrl.navigateBack(defaultHref, { animation: this.routerAnimation });
|
||||
this.navCtrl.navigateBack(defaultHref);
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,10 @@
|
||||
import { Location } from '@angular/common';
|
||||
import {
|
||||
ComponentFactoryResolver,
|
||||
ComponentRef,
|
||||
ElementRef,
|
||||
Injector,
|
||||
NgZone,
|
||||
OnDestroy,
|
||||
OnInit,
|
||||
ViewContainerRef,
|
||||
Attribute,
|
||||
Directive,
|
||||
EventEmitter,
|
||||
Optional,
|
||||
Output,
|
||||
SkipSelf,
|
||||
} from '@angular/core';
|
||||
import { OutletContext, Router, ActivatedRoute, ChildrenOutletContexts, PRIMARY_OUTLET } from '@angular/router';
|
||||
import { componentOnReady } from '@ionic/core';
|
||||
import { Observable, BehaviorSubject } from 'rxjs';
|
||||
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
|
||||
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
|
||||
|
||||
import { AnimationBuilder } from '../../ionic-core';
|
||||
import { AnimationBuilder } from '../../';
|
||||
import { Config } from '../../providers/config';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
@@ -30,10 +14,8 @@ import { RouteView, getUrl } from './stack-utils';
|
||||
@Directive({
|
||||
selector: 'ion-router-outlet',
|
||||
exportAs: 'outlet',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['animated', 'animation', 'swipeGesture'],
|
||||
inputs: ['animated', 'animation', 'swipeGesture']
|
||||
})
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
nativeEl: HTMLIonRouterOutletElement;
|
||||
|
||||
@@ -54,9 +36,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
tabsPrefix: string | undefined;
|
||||
|
||||
@Output() stackEvents = new EventEmitter<any>();
|
||||
// eslint-disable-next-line @angular-eslint/no-output-rename
|
||||
@Output('activate') activateEvents = new EventEmitter<any>();
|
||||
// eslint-disable-next-line @angular-eslint/no-output-rename
|
||||
@Output('deactivate') deactivateEvents = new EventEmitter<any>();
|
||||
|
||||
set animation(animation: AnimationBuilder) {
|
||||
@@ -70,13 +50,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
set swipeGesture(swipe: boolean) {
|
||||
this._swipeGesture = swipe;
|
||||
|
||||
this.nativeEl.swipeHandler = swipe
|
||||
? {
|
||||
canStart: () => this.stackCtrl.canGoBack(1) && !this.stackCtrl.hasRunningTask(),
|
||||
onStart: () => this.stackCtrl.startBackTransition(),
|
||||
onEnd: (shouldContinue) => this.stackCtrl.endBackTransition(shouldContinue),
|
||||
}
|
||||
: undefined;
|
||||
this.nativeEl.swipeHandler = swipe ? {
|
||||
canStart: () => this.stackCtrl.canGoBack(1),
|
||||
onStart: () => this.stackCtrl.startBackTransition(),
|
||||
onEnd: shouldContinue => this.stackCtrl.endBackTransition(shouldContinue)
|
||||
} : undefined;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -114,23 +92,24 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
// If the outlet was not instantiated at the time the route got activated we need to populate
|
||||
// the outlet when it is initialized (ie inside a NgIf)
|
||||
const context = this.getContext();
|
||||
if (context?.route) {
|
||||
if (context && context.route) {
|
||||
this.activateWith(context.route, context.resolver || null);
|
||||
}
|
||||
}
|
||||
|
||||
new Promise((resolve) => componentOnReady(this.nativeEl, resolve)).then(() => {
|
||||
if (this._swipeGesture === undefined) {
|
||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
|
||||
}
|
||||
});
|
||||
if ((this.nativeEl as any).componentOnReady) {
|
||||
this.nativeEl.componentOnReady().then(() => {
|
||||
if (this._swipeGesture === undefined) {
|
||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
get isActivated(): boolean {
|
||||
return !!this.activated;
|
||||
}
|
||||
|
||||
get component(): Record<string, unknown> {
|
||||
get component(): object {
|
||||
if (!this.activated) {
|
||||
throw new Error('Outlet is not activated');
|
||||
}
|
||||
@@ -161,41 +140,27 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
/**
|
||||
* Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
attach(_ref: ComponentRef<any>, _activatedRoute: ActivatedRoute): void {
|
||||
attach(_ref: ComponentRef<any>, _activatedRoute: ActivatedRoute) {
|
||||
throw new Error('incompatible reuse strategy');
|
||||
}
|
||||
|
||||
deactivate(): void {
|
||||
if (this.activated) {
|
||||
if (this.activatedView) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const context = this.getContext()!;
|
||||
this.activatedView.savedData = new Map(context.children['contexts']);
|
||||
|
||||
/**
|
||||
* Angular v11.2.10 introduced a change
|
||||
* where this route context is cleared out when
|
||||
* a router-outlet is deactivated, However,
|
||||
* we need this route information in order to
|
||||
* return a user back to the correct tab when
|
||||
* leaving and then going back to the tab context.
|
||||
*/
|
||||
const primaryOutlet = this.activatedView.savedData.get('primary');
|
||||
if (primaryOutlet && context.route) {
|
||||
primaryOutlet.route = { ...context.route };
|
||||
}
|
||||
this.activatedView.savedData = new Map(this.getContext()!.children['contexts']);
|
||||
|
||||
/**
|
||||
* Ensure we are saving the NavigationExtras
|
||||
* data otherwise it will be lost
|
||||
*/
|
||||
this.activatedView.savedExtras = {};
|
||||
const context = this.getContext()!;
|
||||
|
||||
if (context.route) {
|
||||
const contextSnapshot = context.route.snapshot;
|
||||
|
||||
this.activatedView.savedExtras.queryParams = contextSnapshot.queryParams;
|
||||
(this.activatedView.savedExtras.fragment as string | null) = contextSnapshot.fragment;
|
||||
this.activatedView.savedExtras.fragment = contextSnapshot.fragment;
|
||||
}
|
||||
}
|
||||
const c = this.component;
|
||||
@@ -206,7 +171,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null): void {
|
||||
activateWith(activatedRoute: ActivatedRoute, resolver: ComponentFactoryResolver | null) {
|
||||
if (this.isActivated) {
|
||||
throw new Error('Cannot activate an already activated outlet');
|
||||
}
|
||||
@@ -219,7 +184,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
const saved = enteringView.savedData;
|
||||
if (saved) {
|
||||
// self-restore
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const context = this.getContext()!;
|
||||
context.children['contexts'] = saved;
|
||||
}
|
||||
@@ -227,7 +191,6 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
this.updateActivatedRouteProxy(cmpRef.instance, activatedRoute);
|
||||
} else {
|
||||
const snapshot = (activatedRoute as any)._futureSnapshot;
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const component = snapshot.routeConfig!.component as any;
|
||||
resolver = resolver || this.resolver;
|
||||
|
||||
@@ -255,7 +218,7 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
this.activatedView = enteringView;
|
||||
this.stackCtrl.setActive(enteringView).then((data) => {
|
||||
this.stackCtrl.setActive(enteringView).then(data => {
|
||||
this.navCtrl.setTopOutlet(this);
|
||||
this.activateEvents.emit(cmpRef.instance);
|
||||
this.stackEvents.emit(data);
|
||||
@@ -338,11 +301,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
private proxyObservable(component$: Observable<any>, path: string): Observable<any> {
|
||||
return component$.pipe(
|
||||
// First wait until the component instance is pushed
|
||||
filter((component) => !!component),
|
||||
switchMap((component) =>
|
||||
filter(component => !!component),
|
||||
switchMap(component =>
|
||||
this.currentActivatedRoute$.pipe(
|
||||
filter((current) => current !== null && current.component === component),
|
||||
switchMap((current) => current && (current.activatedRoute as any)[path]),
|
||||
filter(current => current !== null && current.component === component),
|
||||
switchMap(current => current && (current.activatedRoute as any)[path]),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
)
|
||||
@@ -369,7 +332,11 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
||||
}
|
||||
|
||||
class OutletInjector implements Injector {
|
||||
constructor(private route: ActivatedRoute, private childContexts: ChildrenOutletContexts, private parent: Injector) {}
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private childContexts: ChildrenOutletContexts,
|
||||
private parent: Injector
|
||||
) { }
|
||||
|
||||
get(token: any, notFoundValue?: any): any {
|
||||
if (token === ActivatedRoute) {
|
||||
@@ -380,6 +347,7 @@ class OutletInjector implements Injector {
|
||||
return this.childContexts;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line
|
||||
return this.parent.get(token, notFoundValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,59 +8,60 @@ import { StackEvent } from './stack-utils';
|
||||
|
||||
@Component({
|
||||
selector: 'ion-tabs',
|
||||
template: ` <ng-content select="[slot=top]"></ng-content>
|
||||
template: `
|
||||
<ng-content select="[slot=top]"></ng-content>
|
||||
<div class="tabs-inner">
|
||||
<ion-router-outlet #outlet tabs="true" (stackEvents)="onPageSelected($event)"></ion-router-outlet>
|
||||
</div>
|
||||
<ng-content></ng-content>`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
styles: [`
|
||||
:host {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
|
||||
flex-direction: column;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
contain: layout size style;
|
||||
z-index: $z-index-page-container;
|
||||
}
|
||||
.tabs-inner {
|
||||
position: relative;
|
||||
contain: layout size style;
|
||||
z-index: $z-index-page-container;
|
||||
}
|
||||
.tabs-inner {
|
||||
position: relative;
|
||||
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
|
||||
contain: layout size style;
|
||||
}
|
||||
`,
|
||||
],
|
||||
contain: layout size style;
|
||||
}`
|
||||
]
|
||||
})
|
||||
// eslint-disable-next-line @angular-eslint/component-class-suffix
|
||||
export class IonTabs {
|
||||
|
||||
@ViewChild('outlet', { read: IonRouterOutlet, static: false }) outlet: IonRouterOutlet;
|
||||
@ContentChild(IonTabBar, { static: false }) tabBar: IonTabBar | undefined;
|
||||
|
||||
@Output() ionTabsWillChange = new EventEmitter<{ tab: string }>();
|
||||
@Output() ionTabsDidChange = new EventEmitter<{ tab: string }>();
|
||||
|
||||
constructor(private navCtrl: NavController) {}
|
||||
constructor(
|
||||
private navCtrl: NavController,
|
||||
) { }
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
onPageSelected(detail: StackEvent): void {
|
||||
onPageSelected(detail: StackEvent) {
|
||||
const stackId = detail.enteringView.stackId;
|
||||
if (detail.tabSwitch && stackId !== undefined) {
|
||||
this.ionTabsWillChange.emit({ tab: stackId });
|
||||
if (this.tabBar) {
|
||||
this.tabBar.selectedTab = stackId;
|
||||
}
|
||||
this.ionTabsWillChange.emit({ tab: stackId });
|
||||
this.ionTabsDidChange.emit({ tab: stackId });
|
||||
}
|
||||
}
|
||||
@@ -85,36 +86,21 @@ export class IonTabs {
|
||||
* b. If the last route view doesn't exist, then navigate
|
||||
* to the default tabRootUrl
|
||||
*/
|
||||
@HostListener('ionTabButtonClick', ['$event'])
|
||||
select(tabOrEvent: string | CustomEvent): Promise<boolean> | undefined {
|
||||
const isTabString = typeof tabOrEvent === 'string';
|
||||
const tab = isTabString ? tabOrEvent : (tabOrEvent as CustomEvent).detail.tab;
|
||||
@HostListener('ionTabButtonClick', ['$event.detail.tab'])
|
||||
select(tab: string) {
|
||||
const alreadySelected = this.outlet.getActiveStackId() === tab;
|
||||
const tabRootUrl = `${this.outlet.tabsPrefix}/${tab}`;
|
||||
|
||||
/**
|
||||
* If this is a nested tab, prevent the event
|
||||
* from bubbling otherwise the outer tabs
|
||||
* will respond to this event too, causing
|
||||
* the app to get directed to the wrong place.
|
||||
*/
|
||||
if (!isTabString) {
|
||||
(tabOrEvent as CustomEvent).stopPropagation();
|
||||
}
|
||||
|
||||
if (alreadySelected) {
|
||||
const activeStackId = this.outlet.getActiveStackId();
|
||||
const activeView = this.outlet.getLastRouteView(activeStackId);
|
||||
|
||||
// If on root tab, do not navigate to root tab again
|
||||
if (activeView?.url === tabRootUrl) {
|
||||
return;
|
||||
}
|
||||
if (activeView.url === tabRootUrl) { return; }
|
||||
|
||||
const rootView = this.outlet.getRootView(tab);
|
||||
const navigationExtras = rootView && tabRootUrl === rootView.url && rootView.savedExtras;
|
||||
return this.navCtrl.navigateRoot(tabRootUrl, {
|
||||
...navigationExtras,
|
||||
...(navigationExtras),
|
||||
animated: true,
|
||||
animationDirection: 'back',
|
||||
});
|
||||
@@ -124,11 +110,11 @@ export class IonTabs {
|
||||
* If there is a lastRoute, goto that, otherwise goto the fallback url of the
|
||||
* selected tab
|
||||
*/
|
||||
const url = lastRoute?.url || tabRootUrl;
|
||||
const navigationExtras = lastRoute?.savedExtras;
|
||||
const url = lastRoute && lastRoute.url || tabRootUrl;
|
||||
const navigationExtras = lastRoute && lastRoute.savedExtras;
|
||||
|
||||
return this.navCtrl.navigateRoot(url, {
|
||||
...navigationExtras,
|
||||
...(navigationExtras),
|
||||
animated: true,
|
||||
animationDirection: 'back',
|
||||
});
|
||||
|
||||
@@ -1,30 +1,15 @@
|
||||
import { ComponentFactoryResolver, ElementRef, Injector, ViewContainerRef, Directive } from '@angular/core';
|
||||
import { ComponentFactoryResolver, Directive, ElementRef, Injector, ViewContainerRef } from '@angular/core';
|
||||
|
||||
import { AngularDelegate } from '../../providers/angular-delegate';
|
||||
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
|
||||
import { ProxyCmp, proxyOutputs } from '../proxies-utils';
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'animation', 'root', 'rootParams', 'swipeGesture'],
|
||||
methods: [
|
||||
'push',
|
||||
'insert',
|
||||
'insertPages',
|
||||
'pop',
|
||||
'popTo',
|
||||
'popToRoot',
|
||||
'removeIndex',
|
||||
'setRoot',
|
||||
'setPages',
|
||||
'getActive',
|
||||
'getByIndex',
|
||||
'canGoBack',
|
||||
'getPrevious',
|
||||
],
|
||||
methods: ['push', 'insert', 'insertPages', 'pop', 'popTo', 'popToRoot', 'removeIndex', 'setRoot', 'setPages', 'getActive', 'getByIndex', 'canGoBack', 'getPrevious']
|
||||
})
|
||||
@Directive({
|
||||
selector: 'ion-nav',
|
||||
selector: 'ion-nav'
|
||||
})
|
||||
// eslint-disable-next-line @angular-eslint/directive-class-suffix
|
||||
export class NavDelegate {
|
||||
protected el: HTMLElement;
|
||||
constructor(
|
||||
@@ -36,6 +21,6 @@ export class NavDelegate {
|
||||
) {
|
||||
this.el = ref.nativeElement;
|
||||
ref.nativeElement.delegate = angularDelegate.create(resolver, injector, location);
|
||||
proxyOutputs(this, this.el, ['ionNavDidChange', 'ionNavWillChange']);
|
||||
proxyOutputs(this, this.el, ['ionNavDidChange' , 'ionNavWillChange' ]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
* ```
|
||||
*/
|
||||
export class NavParams {
|
||||
constructor(public data: { [key: string]: any } = {}) {}
|
||||
|
||||
constructor(public data: {[key: string]: any} = {}) {}
|
||||
|
||||
/**
|
||||
* Get the value of a nav-parameter for the current view
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
import { LocationStrategy } from '@angular/common';
|
||||
import { ElementRef, OnChanges, OnInit, Directive, HostListener, Input, Optional } from '@angular/core';
|
||||
import { Directive, ElementRef, HostListener, Optional } from '@angular/core';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { AnimationBuilder, RouterDirection } from '@ionic/core';
|
||||
import { RouterDirection } from '@ionic/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
@Directive({
|
||||
selector: '[routerLink]',
|
||||
inputs: ['routerDirection']
|
||||
})
|
||||
export class RouterLinkDelegateDirective implements OnInit, OnChanges {
|
||||
@Input()
|
||||
routerDirection: RouterDirection = 'forward';
|
||||
export class RouterLinkDelegate {
|
||||
|
||||
@Input()
|
||||
routerAnimation?: AnimationBuilder;
|
||||
private subscription?: Subscription;
|
||||
|
||||
routerDirection: RouterDirection = 'forward';
|
||||
|
||||
constructor(
|
||||
private locationStrategy: LocationStrategy,
|
||||
private navCtrl: NavController,
|
||||
private elementRef: ElementRef,
|
||||
private router: Router,
|
||||
@Optional() private routerLink?: RouterLink
|
||||
) {}
|
||||
@Optional() private routerLink?: RouterLink,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
ngOnInit() {
|
||||
this.updateTargetUrlAndHref();
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
ngOnChanges(): any {
|
||||
this.updateTargetUrlAndHref();
|
||||
}
|
||||
|
||||
ngOnDestroy(): any {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private updateTargetUrlAndHref() {
|
||||
if (this.routerLink?.urlTree) {
|
||||
if (this.routerLink) {
|
||||
const href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.routerLink.urlTree));
|
||||
this.elementRef.nativeElement.href = href;
|
||||
}
|
||||
@@ -42,8 +49,8 @@ export class RouterLinkDelegateDirective implements OnInit, OnChanges {
|
||||
* @internal
|
||||
*/
|
||||
@HostListener('click', ['$event'])
|
||||
onClick(ev: UIEvent): void {
|
||||
this.navCtrl.setDirection(this.routerDirection, undefined, undefined, this.routerAnimation);
|
||||
onClick(ev: UIEvent) {
|
||||
this.navCtrl.setDirection(this.routerDirection);
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,15 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { ComponentRef, NgZone } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AnimationBuilder, RouterDirection } from '@ionic/core';
|
||||
import { RouterDirection } from '@ionic/core';
|
||||
|
||||
import { bindLifecycleEvents } from '../../providers/angular-delegate';
|
||||
import { NavController } from '../../providers/nav-controller';
|
||||
|
||||
import {
|
||||
RouteView,
|
||||
StackEvent,
|
||||
computeStackId,
|
||||
destroyView,
|
||||
getUrl,
|
||||
insertView,
|
||||
isTabSwitch,
|
||||
toSegments,
|
||||
} from './stack-utils';
|
||||
import { RouteView, StackEvent, computeStackId, destroyView, getUrl, insertView, isTabSwitch, toSegments } from './stack-utils';
|
||||
|
||||
export class StackController {
|
||||
|
||||
private views: RouteView[] = [];
|
||||
private runningTask?: Promise<any>;
|
||||
private skipTransition = false;
|
||||
@@ -38,7 +30,7 @@ export class StackController {
|
||||
|
||||
createView(ref: ComponentRef<any>, activatedRoute: ActivatedRoute): RouteView {
|
||||
const url = getUrl(this.router, activatedRoute);
|
||||
const element = ref?.location?.nativeElement as HTMLElement;
|
||||
const element = (ref && ref.location && ref.location.nativeElement) as HTMLElement;
|
||||
const unlistenEvents = bindLifecycleEvents(this.zone, ref.instance, element);
|
||||
return {
|
||||
id: this.nextId++,
|
||||
@@ -52,7 +44,7 @@ export class StackController {
|
||||
|
||||
getExistingView(activatedRoute: ActivatedRoute): RouteView | undefined {
|
||||
const activatedUrlKey = getUrl(this.router, activatedRoute);
|
||||
const view = this.views.find((vw) => vw.url === activatedUrlKey);
|
||||
const view = this.views.find(vw => vw.url === activatedUrlKey);
|
||||
if (view) {
|
||||
view.ref.changeDetectorRef.reattach();
|
||||
}
|
||||
@@ -60,8 +52,7 @@ export class StackController {
|
||||
}
|
||||
|
||||
setActive(enteringView: RouteView): Promise<StackEvent> {
|
||||
const consumeResult = this.navCtrl.consumeTransition();
|
||||
let { direction, animation, animationBuilder } = consumeResult;
|
||||
let { direction, animation } = this.navCtrl.consumeTransition();
|
||||
const leavingView = this.activeView;
|
||||
const tabSwitch = isTabSwitch(enteringView, leavingView);
|
||||
if (tabSwitch) {
|
||||
@@ -73,14 +64,17 @@ export class StackController {
|
||||
|
||||
let currentNavigation;
|
||||
|
||||
const router = this.router as any;
|
||||
const router = (this.router as any);
|
||||
|
||||
// Angular >= 7.2.0
|
||||
if (router.getCurrentNavigation) {
|
||||
currentNavigation = router.getCurrentNavigation();
|
||||
|
||||
// Angular < 7.2.0
|
||||
} else if (router.navigations?.value) {
|
||||
} else if (
|
||||
router.navigations &&
|
||||
router.navigations.value
|
||||
) {
|
||||
currentNavigation = router.navigations.value;
|
||||
}
|
||||
|
||||
@@ -91,7 +85,11 @@ export class StackController {
|
||||
* we remove the last item
|
||||
* from our views stack
|
||||
*/
|
||||
if (currentNavigation?.extras?.replaceUrl) {
|
||||
if (
|
||||
currentNavigation &&
|
||||
currentNavigation.extras &&
|
||||
currentNavigation.extras.replaceUrl
|
||||
) {
|
||||
if (this.views.length > 0) {
|
||||
this.views.splice(-1, 1);
|
||||
}
|
||||
@@ -107,26 +105,6 @@ export class StackController {
|
||||
enteringView.ref.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* If we are going back from a page that
|
||||
* was presented using a custom animation
|
||||
* we should default to using that
|
||||
* unless the developer explicitly
|
||||
* provided another animation.
|
||||
*/
|
||||
const customAnimation = enteringView.animationBuilder;
|
||||
if (animationBuilder === undefined && direction === 'back' && !tabSwitch && customAnimation !== undefined) {
|
||||
animationBuilder = customAnimation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save any custom animation so that navigating
|
||||
* back will use this custom animation by default.
|
||||
*/
|
||||
if (leavingView) {
|
||||
leavingView.animationBuilder = animationBuilder;
|
||||
}
|
||||
|
||||
// Wait until previous transitions finish
|
||||
return this.zone.runOutsideAngular(() => {
|
||||
return this.wait(() => {
|
||||
@@ -138,13 +116,13 @@ export class StackController {
|
||||
// In case the enteringView is the same as the leavingPage we need to reattach()
|
||||
enteringView.ref.changeDetectorRef.reattach();
|
||||
|
||||
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false, animationBuilder)
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location, this.zone))
|
||||
return this.transition(enteringView, leavingView, animation, this.canGoBack(1), false)
|
||||
.then(() => cleanupAsync(enteringView, views, viewsSnapshot, this.location))
|
||||
.then(() => ({
|
||||
enteringView,
|
||||
direction,
|
||||
animation,
|
||||
tabSwitch,
|
||||
tabSwitch
|
||||
}));
|
||||
});
|
||||
});
|
||||
@@ -154,7 +132,7 @@ export class StackController {
|
||||
return this.getStack(stackId).length > deep;
|
||||
}
|
||||
|
||||
pop(deep: number, stackId = this.getActiveStackId()): Promise<boolean> {
|
||||
pop(deep: number, stackId = this.getActiveStackId()) {
|
||||
return this.zone.run(() => {
|
||||
const views = this.getStack(stackId);
|
||||
if (views.length <= deep) {
|
||||
@@ -166,46 +144,49 @@ export class StackController {
|
||||
const viewSavedData = view.savedData;
|
||||
if (viewSavedData) {
|
||||
const primaryOutlet = viewSavedData.get('primary');
|
||||
if (primaryOutlet?.route?._routerState?.snapshot.url) {
|
||||
if (
|
||||
primaryOutlet &&
|
||||
primaryOutlet.route &&
|
||||
primaryOutlet.route._routerState &&
|
||||
primaryOutlet.route._routerState.snapshot &&
|
||||
primaryOutlet.route._routerState.snapshot.url
|
||||
) {
|
||||
url = primaryOutlet.route._routerState.snapshot.url;
|
||||
}
|
||||
}
|
||||
const { animationBuilder } = this.navCtrl.consumeTransition();
|
||||
return this.navCtrl.navigateBack(url, { ...view.savedExtras, animation: animationBuilder }).then(() => true);
|
||||
|
||||
return this.navCtrl.navigateBack(url, view.savedExtras).then(() => true);
|
||||
});
|
||||
}
|
||||
|
||||
startBackTransition(): Promise<boolean> | Promise<void> {
|
||||
startBackTransition() {
|
||||
const leavingView = this.activeView;
|
||||
if (leavingView) {
|
||||
const views = this.getStack(leavingView.stackId);
|
||||
const enteringView = views[views.length - 2];
|
||||
const customAnimation = enteringView.animationBuilder;
|
||||
|
||||
return this.wait(() => {
|
||||
return this.transition(
|
||||
enteringView, // entering view
|
||||
leavingView, // leaving view
|
||||
'back',
|
||||
this.canGoBack(2),
|
||||
true,
|
||||
customAnimation
|
||||
true
|
||||
);
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
endBackTransition(shouldComplete: boolean): void {
|
||||
endBackTransition(shouldComplete: boolean) {
|
||||
if (shouldComplete) {
|
||||
this.skipTransition = true;
|
||||
this.pop(1);
|
||||
} else if (this.activeView) {
|
||||
cleanup(this.activeView, this.views, this.views, this.location, this.zone);
|
||||
cleanup(this.activeView, this.views, this.views, this.location);
|
||||
}
|
||||
}
|
||||
|
||||
getLastUrl(stackId?: string): RouteView | undefined {
|
||||
getLastUrl(stackId?: string) {
|
||||
const views = this.getStack(stackId);
|
||||
return views.length > 0 ? views[views.length - 1] : undefined;
|
||||
}
|
||||
@@ -213,7 +194,7 @@ export class StackController {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getRootUrl(stackId?: string): RouteView | undefined {
|
||||
getRootUrl(stackId?: string) {
|
||||
const views = this.getStack(stackId);
|
||||
return views.length > 0 ? views[0] : undefined;
|
||||
}
|
||||
@@ -222,12 +203,7 @@ export class StackController {
|
||||
return this.activeView ? this.activeView.stackId : undefined;
|
||||
}
|
||||
|
||||
hasRunningTask(): boolean {
|
||||
return this.runningTask !== undefined;
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
destroy() {
|
||||
this.containerEl = undefined!;
|
||||
this.views.forEach(destroyView);
|
||||
this.activeView = undefined;
|
||||
@@ -235,7 +211,7 @@ export class StackController {
|
||||
}
|
||||
|
||||
private getStack(stackId: string | undefined) {
|
||||
return this.views.filter((v) => v.stackId === stackId);
|
||||
return this.views.filter(v => v.stackId === stackId);
|
||||
}
|
||||
|
||||
private insertView(enteringView: RouteView, direction: RouterDirection) {
|
||||
@@ -249,8 +225,7 @@ export class StackController {
|
||||
leavingView: RouteView | undefined,
|
||||
direction: 'forward' | 'back' | undefined,
|
||||
showGoBack: boolean,
|
||||
progressAnimation: boolean,
|
||||
animationBuilder?: AnimationBuilder
|
||||
progressAnimation: boolean
|
||||
) {
|
||||
if (this.skipTransition) {
|
||||
this.skipTransition = false;
|
||||
@@ -275,8 +250,7 @@ export class StackController {
|
||||
duration: direction === undefined ? 0 : undefined,
|
||||
direction,
|
||||
showGoBack,
|
||||
progressAnimation,
|
||||
animationBuilder,
|
||||
progressAnimation
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -288,23 +262,16 @@ export class StackController {
|
||||
await this.runningTask;
|
||||
this.runningTask = undefined;
|
||||
}
|
||||
const promise = (this.runningTask = task());
|
||||
promise.finally(() => (this.runningTask = undefined));
|
||||
const promise = this.runningTask = task();
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
const cleanupAsync = (
|
||||
activeRoute: RouteView,
|
||||
views: RouteView[],
|
||||
viewsSnapshot: RouteView[],
|
||||
location: Location,
|
||||
zone: NgZone
|
||||
) => {
|
||||
const cleanupAsync = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
|
||||
if (typeof (requestAnimationFrame as any) === 'function') {
|
||||
return new Promise<void>((resolve) => {
|
||||
return new Promise<any>(resolve => {
|
||||
requestAnimationFrame(() => {
|
||||
cleanup(activeRoute, views, viewsSnapshot, location, zone);
|
||||
cleanup(activeRoute, views, viewsSnapshot, location);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
@@ -312,20 +279,12 @@ const cleanupAsync = (
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const cleanup = (
|
||||
activeRoute: RouteView,
|
||||
views: RouteView[],
|
||||
viewsSnapshot: RouteView[],
|
||||
location: Location,
|
||||
zone: NgZone
|
||||
) => {
|
||||
/**
|
||||
* Re-enter the Angular zone when destroying page components. This will allow
|
||||
* lifecycle events (`ngOnDestroy`) to be run inside the Angular zone.
|
||||
*/
|
||||
zone.run(() => viewsSnapshot.filter((view) => !views.includes(view)).forEach(destroyView));
|
||||
const cleanup = (activeRoute: RouteView, views: RouteView[], viewsSnapshot: RouteView[], location: Location) => {
|
||||
viewsSnapshot
|
||||
.filter(view => !views.includes(view))
|
||||
.forEach(destroyView);
|
||||
|
||||
views.forEach((view) => {
|
||||
views.forEach(view => {
|
||||
/**
|
||||
* In the event that a user navigated multiple
|
||||
* times in rapid succession, we want to make sure
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ComponentRef } from '@angular/core';
|
||||
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
|
||||
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
|
||||
import { NavDirection, RouterDirection } from '@ionic/core';
|
||||
|
||||
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection): RouteView[] => {
|
||||
export const insertView = (views: RouteView[], view: RouteView, direction: RouterDirection) => {
|
||||
if (direction === 'root') {
|
||||
return setRoot(views, view);
|
||||
} else if (direction === 'forward') {
|
||||
@@ -13,7 +13,7 @@ export const insertView = (views: RouteView[], view: RouteView, direction: Route
|
||||
};
|
||||
|
||||
const setRoot = (views: RouteView[], view: RouteView) => {
|
||||
views = views.filter((v) => v.stackId !== view.stackId);
|
||||
views = views.filter(v => v.stackId !== view.stackId);
|
||||
views.push(view);
|
||||
return views;
|
||||
};
|
||||
@@ -21,7 +21,7 @@ const setRoot = (views: RouteView[], view: RouteView) => {
|
||||
const setForward = (views: RouteView[], view: RouteView) => {
|
||||
const index = views.indexOf(view);
|
||||
if (index >= 0) {
|
||||
views = views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
|
||||
views = views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
|
||||
} else {
|
||||
views.push(view);
|
||||
}
|
||||
@@ -31,25 +31,25 @@ const setForward = (views: RouteView[], view: RouteView) => {
|
||||
const setBack = (views: RouteView[], view: RouteView) => {
|
||||
const index = views.indexOf(view);
|
||||
if (index >= 0) {
|
||||
return views.filter((v) => v.stackId !== view.stackId || v.id <= view.id);
|
||||
return views.filter(v => v.stackId !== view.stackId || v.id <= view.id);
|
||||
} else {
|
||||
return setRoot(views, view);
|
||||
}
|
||||
};
|
||||
|
||||
export const getUrl = (router: Router, activatedRoute: ActivatedRoute): string => {
|
||||
export const getUrl = (router: Router, activatedRoute: ActivatedRoute) => {
|
||||
const urlTree = router.createUrlTree(['.'], { relativeTo: activatedRoute });
|
||||
return router.serializeUrl(urlTree);
|
||||
};
|
||||
|
||||
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined): boolean => {
|
||||
export const isTabSwitch = (enteringView: RouteView, leavingView: RouteView | undefined) => {
|
||||
if (!leavingView) {
|
||||
return true;
|
||||
}
|
||||
return enteringView.stackId !== leavingView.stackId;
|
||||
};
|
||||
|
||||
export const computeStackId = (prefixUrl: string[] | undefined, url: string): string | undefined => {
|
||||
export const computeStackId = (prefixUrl: string[] | undefined, url: string) => {
|
||||
if (!prefixUrl) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -65,14 +65,14 @@ export const computeStackId = (prefixUrl: string[] | undefined, url: string): st
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const toSegments = (path: string): string[] => {
|
||||
export const toSegments = (path: string) => {
|
||||
return path
|
||||
.split('/')
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s !== '');
|
||||
.map(s => s.trim())
|
||||
.filter(s => s !== '');
|
||||
};
|
||||
|
||||
export const destroyView = (view: RouteView | undefined): void => {
|
||||
export const destroyView = (view: RouteView | undefined) => {
|
||||
if (view) {
|
||||
// TODO lifecycle event
|
||||
view.ref.destroy();
|
||||
@@ -96,5 +96,4 @@ export interface RouteView {
|
||||
savedData?: any;
|
||||
savedExtras?: NavigationExtras;
|
||||
unlistenEvents: () => void;
|
||||
animationBuilder?: AnimationBuilder;
|
||||
}
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
|
||||
import { Components } from '@ionic/core';
|
||||
|
||||
export declare interface IonModal extends Components.IonModal {
|
||||
/**
|
||||
* Emitted after the modal has presented.
|
||||
**/
|
||||
ionModalDidPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted before the modal has presented.
|
||||
*/
|
||||
ionModalWillPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted before the modal has dismissed.
|
||||
*/
|
||||
ionModalWillDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the modal has dismissed.
|
||||
*/
|
||||
ionModalDidDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the modal has presented. Shorthand for ionModalWillDismiss.
|
||||
*/
|
||||
didPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted before the modal has presented. Shorthand for ionModalWillPresent.
|
||||
*/
|
||||
willPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted before the modal has dismissed. Shorthand for ionModalWillDismiss.
|
||||
*/
|
||||
willDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the modal has dismissed. Shorthand for ionModalDidDismiss.
|
||||
*/
|
||||
didDismiss: EventEmitter<CustomEvent>;
|
||||
}
|
||||
@ProxyCmp({
|
||||
inputs: [
|
||||
'animated',
|
||||
'backdropBreakpoint',
|
||||
'backdropDismiss',
|
||||
'breakpoints',
|
||||
'cssClass',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'handle',
|
||||
'initialBreakpoint',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'presentingElement',
|
||||
'showBackdrop',
|
||||
'swipeToClose',
|
||||
'translucent',
|
||||
'trigger',
|
||||
],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-modal',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `<div class="ion-page" *ngIf="isCmpOpen"><ng-container [ngTemplateOutlet]="template"></ng-container></div>`,
|
||||
inputs: [
|
||||
'animated',
|
||||
'backdropBreakpoint',
|
||||
'backdropDismiss',
|
||||
'breakpoints',
|
||||
'cssClass',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'handle',
|
||||
'initialBreakpoint',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'presentingElement',
|
||||
'showBackdrop',
|
||||
'swipeToClose',
|
||||
'translucent',
|
||||
'trigger',
|
||||
],
|
||||
})
|
||||
export class IonModal {
|
||||
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
|
||||
|
||||
isCmpOpen: boolean = false;
|
||||
|
||||
protected el: HTMLElement;
|
||||
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
this.el = r.nativeElement;
|
||||
|
||||
this.el.addEventListener('willPresent', () => {
|
||||
this.isCmpOpen = true;
|
||||
c.detectChanges();
|
||||
});
|
||||
this.el.addEventListener('didDismiss', () => {
|
||||
this.isCmpOpen = false;
|
||||
c.detectChanges();
|
||||
});
|
||||
|
||||
proxyOutputs(this, this.el, [
|
||||
'ionModalDidPresent',
|
||||
'ionModalWillPresent',
|
||||
'ionModalWillDismiss',
|
||||
'ionModalDidDismiss',
|
||||
'didPresent',
|
||||
'willPresent',
|
||||
'willDismiss',
|
||||
'didDismiss',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
ContentChild,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
NgZone,
|
||||
TemplateRef,
|
||||
} from '@angular/core';
|
||||
import { ProxyCmp, proxyOutputs } from '../angular-component-lib/utils';
|
||||
import { Components } from '@ionic/core';
|
||||
export declare interface IonPopover extends Components.IonPopover {
|
||||
/**
|
||||
* Emitted after the popover has presented.
|
||||
*/
|
||||
ionPopoverDidPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted before the popover has presented.
|
||||
*/
|
||||
ionPopoverWillPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the popover has dismissed.
|
||||
*/
|
||||
ionPopoverWillDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the popover has dismissed.
|
||||
*/
|
||||
ionPopoverDidDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
|
||||
*/
|
||||
didPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted before the popover has presented. Shorthand for ionPopoverWillPresent.
|
||||
*/
|
||||
willPresent: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the popover has presented. Shorthand for ionPopoverWillDismiss.
|
||||
*/
|
||||
willDismiss: EventEmitter<CustomEvent>;
|
||||
/**
|
||||
* Emitted after the popover has dismissed. Shorthand for ionPopoverDidDismiss.
|
||||
*/
|
||||
didDismiss: EventEmitter<CustomEvent>;
|
||||
}
|
||||
@ProxyCmp({
|
||||
inputs: [
|
||||
'alignment',
|
||||
'animated',
|
||||
'arrow',
|
||||
'backdropDismiss',
|
||||
'cssClass',
|
||||
'dismissOnSelect',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss'],
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-popover',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `<ng-container [ngTemplateOutlet]="template" *ngIf="isCmpOpen"></ng-container>`,
|
||||
inputs: [
|
||||
'alignment',
|
||||
'animated',
|
||||
'arrow',
|
||||
'backdropDismiss',
|
||||
'cssClass',
|
||||
'dismissOnSelect',
|
||||
'enterAnimation',
|
||||
'event',
|
||||
'isOpen',
|
||||
'keyboardClose',
|
||||
'leaveAnimation',
|
||||
'mode',
|
||||
'showBackdrop',
|
||||
'translucent',
|
||||
'trigger',
|
||||
'triggerAction',
|
||||
'reference',
|
||||
'size',
|
||||
'side',
|
||||
],
|
||||
})
|
||||
export class IonPopover {
|
||||
@ContentChild(TemplateRef, { static: false }) template: TemplateRef<any>;
|
||||
|
||||
isCmpOpen: boolean = false;
|
||||
|
||||
protected el: HTMLElement;
|
||||
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
this.el = r.nativeElement;
|
||||
|
||||
this.el.addEventListener('willPresent', () => {
|
||||
this.isCmpOpen = true;
|
||||
c.detectChanges();
|
||||
});
|
||||
this.el.addEventListener('didDismiss', () => {
|
||||
this.isCmpOpen = false;
|
||||
c.detectChanges();
|
||||
});
|
||||
proxyOutputs(this, this.el, [
|
||||
'ionPopoverDidPresent',
|
||||
'ionPopoverWillPresent',
|
||||
'ionPopoverWillDismiss',
|
||||
'ionPopoverDidDismiss',
|
||||
'didPresent',
|
||||
'willPresent',
|
||||
'willDismiss',
|
||||
'didDismiss',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,11 @@
|
||||
import * as d from './proxies';
|
||||
|
||||
export const DIRECTIVES = [
|
||||
d.IonAccordion,
|
||||
d.IonAccordionGroup,
|
||||
d.IonApp,
|
||||
d.IonAvatar,
|
||||
d.IonBackButton,
|
||||
d.IonBackdrop,
|
||||
d.IonBadge,
|
||||
d.IonBreadcrumb,
|
||||
d.IonBreadcrumbs,
|
||||
d.IonButton,
|
||||
d.IonButtons,
|
||||
d.IonCard,
|
||||
|
||||
46
angular/src/directives/proxies-utils.ts
Normal file
46
angular/src/directives/proxies-utils.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable */
|
||||
/* tslint:disable */
|
||||
import { fromEvent } from 'rxjs';
|
||||
|
||||
export const proxyInputs = (Cmp: any, inputs: string[]) => {
|
||||
const Prototype = Cmp.prototype;
|
||||
inputs.forEach(item => {
|
||||
Object.defineProperty(Prototype, item, {
|
||||
get() {
|
||||
return this.el[item];
|
||||
},
|
||||
set(val: any) {
|
||||
this.z.runOutsideAngular(() => (this.el[item] = val));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const proxyMethods = (Cmp: any, methods: string[]) => {
|
||||
const Prototype = Cmp.prototype;
|
||||
methods.forEach(methodName => {
|
||||
Prototype[methodName] = function () {
|
||||
const args = arguments;
|
||||
return this.z.runOutsideAngular(() =>
|
||||
this.el[methodName].apply(this.el, args)
|
||||
);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
export const proxyOutputs = (instance: any, el: any, events: string[]) => {
|
||||
events.forEach(eventName => instance[eventName] = fromEvent(el, eventName));
|
||||
}
|
||||
|
||||
export function ProxyCmp(opts: { inputs?: any; methods?: any }) {
|
||||
const decorator = function(cls: any){
|
||||
if (opts.inputs) {
|
||||
proxyInputs(cls, opts.inputs);
|
||||
}
|
||||
if (opts.methods) {
|
||||
proxyMethods(cls, opts.methods);
|
||||
}
|
||||
return cls;
|
||||
};
|
||||
return decorator;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EmbeddedViewRef, IterableDiffer, IterableDiffers, NgZone, SimpleChanges, TrackByFunction } from '@angular/core';
|
||||
import { Cell, CellType, FooterHeightFn, HeaderFn, HeaderHeightFn, ItemHeightFn } from '@ionic/core';
|
||||
|
||||
import { ProxyCmp } from '../angular-component-lib/utils';
|
||||
import { ProxyCmp } from '../proxies-utils';
|
||||
|
||||
import { VirtualFooter } from './virtual-footer';
|
||||
import { VirtualHeader } from './virtual-header';
|
||||
@@ -71,7 +71,7 @@ export declare interface IonVirtualScroll {
|
||||
* entire virtual scroll is reset, which is an expensive operation and
|
||||
* should be avoided if possible.
|
||||
*/
|
||||
items?: any[] | null;
|
||||
items?: any[];
|
||||
|
||||
/**
|
||||
* An optional function that maps each item within their height.
|
||||
@@ -205,8 +205,8 @@ export class IonVirtualScroll {
|
||||
case 'item': return this.itmTmp.templateRef;
|
||||
case 'header': return this.hdrTmp.templateRef;
|
||||
case 'footer': return this.ftrTmp.templateRef;
|
||||
default: throw new Error('template for virtual item was not provided');
|
||||
}
|
||||
throw new Error('template for virtual item was not provided');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
// DIRECTIVES
|
||||
export { BooleanValueAccessorDirective as BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
|
||||
export { NumericValueAccessorDirective as NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
|
||||
export { RadioValueAccessorDirective as RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
|
||||
export { SelectValueAccessorDirective as SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
|
||||
export { TextValueAccessorDirective as TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
|
||||
export { BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
|
||||
export { NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
|
||||
export { RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
|
||||
export { SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
|
||||
export { TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
|
||||
export { IonTabs } from './directives/navigation/ion-tabs';
|
||||
export { IonBackButtonDelegateDirective as IonBackButtonDelegate } from './directives/navigation/ion-back-button';
|
||||
export { IonBackButtonDelegate } from './directives/navigation/ion-back-button';
|
||||
export { NavDelegate } from './directives/navigation/nav-delegate';
|
||||
export { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
|
||||
export { RouterLinkDelegateDirective as RouterLinkDelegate } from './directives/navigation/router-link-delegate';
|
||||
export { RouterLinkDelegate } from './directives/navigation/router-link-delegate';
|
||||
export { NavParams } from './directives/navigation/nav-params';
|
||||
export { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
|
||||
export { VirtualItem } from './directives/virtual-scroll/virtual-item';
|
||||
export { VirtualHeader } from './directives/virtual-scroll/virtual-header';
|
||||
export { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
|
||||
export { IonModal } from './directives/overlays/modal';
|
||||
export { IonPopover } from './directives/overlays/popover';
|
||||
export * from './directives/proxies';
|
||||
|
||||
// PROVIDERS
|
||||
@@ -44,84 +42,8 @@ export * from './types/ionic-lifecycle-hooks';
|
||||
// PACKAGE MODULE
|
||||
export { IonicModule } from './ionic-module';
|
||||
|
||||
export {
|
||||
// UTILS
|
||||
createAnimation,
|
||||
createGesture,
|
||||
iosTransitionAnimation,
|
||||
mdTransitionAnimation,
|
||||
IonicSwiper,
|
||||
IonicSlides,
|
||||
getPlatforms,
|
||||
isPlatform,
|
||||
getTimeGivenProgression,
|
||||
// TYPES
|
||||
Animation,
|
||||
AnimationBuilder,
|
||||
AnimationCallbackOptions,
|
||||
AnimationDirection,
|
||||
AnimationFill,
|
||||
AnimationKeyFrames,
|
||||
AnimationLifecycle,
|
||||
Gesture,
|
||||
GestureConfig,
|
||||
GestureDetail,
|
||||
NavComponentWithProps,
|
||||
SpinnerTypes,
|
||||
AccordionGroupCustomEvent,
|
||||
AccordionGroupChangeEventDetail,
|
||||
BreadcrumbCustomEvent,
|
||||
BreadcrumbCollapsedClickEventDetail,
|
||||
ActionSheetOptions,
|
||||
ActionSheetButton,
|
||||
AlertOptions,
|
||||
AlertInput,
|
||||
AlertTextareaAttributes,
|
||||
AlertInputAttributes,
|
||||
AlertButton,
|
||||
BackButtonEvent,
|
||||
CheckboxCustomEvent,
|
||||
CheckboxChangeEventDetail,
|
||||
DatetimeCustomEvent,
|
||||
DatetimeChangeEventDetail,
|
||||
InfiniteScrollCustomEvent,
|
||||
InputCustomEvent,
|
||||
InputChangeEventDetail,
|
||||
ItemReorderEventDetail,
|
||||
ItemReorderCustomEvent,
|
||||
ItemSlidingCustomEvent,
|
||||
IonicSafeString,
|
||||
LoadingOptions,
|
||||
MenuCustomEvent,
|
||||
ModalOptions,
|
||||
NavCustomEvent,
|
||||
PickerOptions,
|
||||
PickerButton,
|
||||
PickerColumn,
|
||||
PickerColumnOption,
|
||||
PlatformConfig,
|
||||
PopoverOptions,
|
||||
RadioGroupCustomEvent,
|
||||
RadioGroupChangeEventDetail,
|
||||
RefresherCustomEvent,
|
||||
RefresherEventDetail,
|
||||
RouterEventDetail,
|
||||
RouterCustomEvent,
|
||||
ScrollBaseCustomEvent,
|
||||
ScrollBaseDetail,
|
||||
ScrollDetail,
|
||||
ScrollCustomEvent,
|
||||
SearchbarCustomEvent,
|
||||
SearchbarChangeEventDetail,
|
||||
SegmentChangeEventDetail,
|
||||
SegmentCustomEvent,
|
||||
SelectChangeEventDetail,
|
||||
SelectCustomEvent,
|
||||
TabsCustomEvent,
|
||||
TextareaChangeEventDetail,
|
||||
TextareaCustomEvent,
|
||||
ToastOptions,
|
||||
ToastButton,
|
||||
ToggleChangeEventDetail,
|
||||
ToggleCustomEvent,
|
||||
} from '@ionic/core';
|
||||
// UTILS
|
||||
export { IonicSafeString, getPlatforms, isPlatform } from '@ionic/core';
|
||||
|
||||
// CORE TYPES
|
||||
export { Animation, AnimationBuilder, AnimationCallbackOptions, AnimationDirection, AnimationFill, AnimationKeyFrames, AnimationLifecycle, Gesture, GestureConfig, GestureDetail, mdTransitionAnimation, iosTransitionAnimation } from '@ionic/core';
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Re-exports from ionic/core
|
||||
|
||||
// UTILS
|
||||
export { IonicSafeString, getPlatforms, isPlatform, createAnimation } from '@ionic/core';
|
||||
|
||||
// CORE TYPES
|
||||
export {
|
||||
Animation,
|
||||
AnimationBuilder,
|
||||
AnimationCallbackOptions,
|
||||
AnimationDirection,
|
||||
AnimationFill,
|
||||
AnimationKeyFrames,
|
||||
AnimationLifecycle,
|
||||
Gesture,
|
||||
GestureConfig,
|
||||
GestureDetail,
|
||||
mdTransitionAnimation,
|
||||
iosTransitionAnimation,
|
||||
NavComponentWithProps,
|
||||
} from '@ionic/core';
|
||||
@@ -1,97 +1,19 @@
|
||||
import { CommonModule, DOCUMENT } from '@angular/common';
|
||||
import { ModuleWithProviders, APP_INITIALIZER, NgModule, NgZone } from '@angular/core';
|
||||
import { APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core';
|
||||
import { IonicConfig } from '@ionic/core';
|
||||
|
||||
import { appInitialize } from './app-initialize';
|
||||
import { BooleanValueAccessorDirective } from './directives/control-value-accessors/boolean-value-accessor';
|
||||
import { NumericValueAccessorDirective } from './directives/control-value-accessors/numeric-value-accesssor';
|
||||
import { RadioValueAccessorDirective } from './directives/control-value-accessors/radio-value-accessor';
|
||||
import { SelectValueAccessorDirective } from './directives/control-value-accessors/select-value-accessor';
|
||||
import { TextValueAccessorDirective } from './directives/control-value-accessors/text-value-accessor';
|
||||
import { IonBackButtonDelegateDirective } from './directives/navigation/ion-back-button';
|
||||
import { BooleanValueAccessor } from './directives/control-value-accessors/boolean-value-accessor';
|
||||
import { NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accesssor';
|
||||
import { RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
|
||||
import { SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
|
||||
import { TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
|
||||
import { IonBackButtonDelegate } from './directives/navigation/ion-back-button';
|
||||
import { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
|
||||
import { IonTabs } from './directives/navigation/ion-tabs';
|
||||
import { NavDelegate } from './directives/navigation/nav-delegate';
|
||||
import { RouterLinkDelegateDirective } from './directives/navigation/router-link-delegate';
|
||||
import { IonModal } from './directives/overlays/modal';
|
||||
import { IonPopover } from './directives/overlays/popover';
|
||||
import {
|
||||
IonAccordion,
|
||||
IonAccordionGroup,
|
||||
IonApp,
|
||||
IonAvatar,
|
||||
IonBackButton,
|
||||
IonBackdrop,
|
||||
IonBadge,
|
||||
IonBreadcrumb,
|
||||
IonBreadcrumbs,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
IonCardContent,
|
||||
IonCardHeader,
|
||||
IonCardSubtitle,
|
||||
IonCardTitle,
|
||||
IonCheckbox,
|
||||
IonChip,
|
||||
IonCol,
|
||||
IonContent,
|
||||
IonDatetime,
|
||||
IonFab,
|
||||
IonFabButton,
|
||||
IonFabList,
|
||||
IonFooter,
|
||||
IonGrid,
|
||||
IonHeader,
|
||||
IonIcon,
|
||||
IonImg,
|
||||
IonInfiniteScroll,
|
||||
IonInfiniteScrollContent,
|
||||
IonInput,
|
||||
IonItem,
|
||||
IonItemDivider,
|
||||
IonItemGroup,
|
||||
IonItemOption,
|
||||
IonItemOptions,
|
||||
IonItemSliding,
|
||||
IonLabel,
|
||||
IonList,
|
||||
IonListHeader,
|
||||
IonMenu,
|
||||
IonMenuButton,
|
||||
IonMenuToggle,
|
||||
IonNav,
|
||||
IonNavLink,
|
||||
IonNote,
|
||||
IonProgressBar,
|
||||
IonRadio,
|
||||
IonRadioGroup,
|
||||
IonRange,
|
||||
IonRefresher,
|
||||
IonRefresherContent,
|
||||
IonReorder,
|
||||
IonReorderGroup,
|
||||
IonRippleEffect,
|
||||
IonRow,
|
||||
IonSearchbar,
|
||||
IonSegment,
|
||||
IonSegmentButton,
|
||||
IonSelect,
|
||||
IonSelectOption,
|
||||
IonSkeletonText,
|
||||
IonSlide,
|
||||
IonSlides,
|
||||
IonSpinner,
|
||||
IonSplitPane,
|
||||
IonTabBar,
|
||||
IonTabButton,
|
||||
IonText,
|
||||
IonTextarea,
|
||||
IonThumbnail,
|
||||
IonTitle,
|
||||
IonToggle,
|
||||
IonToolbar,
|
||||
} from './directives/proxies';
|
||||
import { RouterLinkDelegate } from './directives/navigation/router-link-delegate';
|
||||
import { IonApp, IonAvatar, IonBackButton, IonBackdrop, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCheckbox, IonChip, IonCol, IonContent, IonDatetime, IonFab, IonFabButton, IonFabList, IonFooter, IonGrid, IonHeader, IonIcon, IonImg, IonInfiniteScroll, IonInfiniteScrollContent, IonInput, IonItem, IonItemDivider, IonItemGroup, IonItemOption, IonItemOptions, IonItemSliding, IonLabel, IonList, IonListHeader, IonMenu, IonMenuButton, IonMenuToggle, IonNav, IonNavLink, IonNote, IonProgressBar, IonRadio, IonRadioGroup, IonRange, IonRefresher, IonRefresherContent, IonReorder, IonReorderGroup, IonRippleEffect, IonRow, IonSearchbar, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonSkeletonText, IonSlide, IonSlides, IonSpinner, IonSplitPane, IonTabBar, IonTabButton, IonText, IonTextarea, IonThumbnail, IonTitle, IonToggle, IonToolbar } from './directives/proxies';
|
||||
import { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
|
||||
import { VirtualHeader } from './directives/virtual-scroll/virtual-header';
|
||||
import { VirtualItem } from './directives/virtual-scroll/virtual-item';
|
||||
@@ -103,15 +25,11 @@ import { PopoverController } from './providers/popover-controller';
|
||||
|
||||
const DECLARATIONS = [
|
||||
// proxies
|
||||
IonAccordion,
|
||||
IonAccordionGroup,
|
||||
IonApp,
|
||||
IonAvatar,
|
||||
IonBackButton,
|
||||
IonBackdrop,
|
||||
IonBadge,
|
||||
IonBreadcrumb,
|
||||
IonBreadcrumbs,
|
||||
IonButton,
|
||||
IonButtons,
|
||||
IonCard,
|
||||
@@ -147,11 +65,9 @@ const DECLARATIONS = [
|
||||
IonMenu,
|
||||
IonMenuButton,
|
||||
IonMenuToggle,
|
||||
IonModal,
|
||||
IonNav,
|
||||
IonNavLink,
|
||||
IonNote,
|
||||
IonPopover,
|
||||
IonProgressBar,
|
||||
IonRadio,
|
||||
IonRadioGroup,
|
||||
@@ -184,30 +100,30 @@ const DECLARATIONS = [
|
||||
IonTabs,
|
||||
|
||||
// ngModel accessors
|
||||
BooleanValueAccessorDirective,
|
||||
NumericValueAccessorDirective,
|
||||
RadioValueAccessorDirective,
|
||||
SelectValueAccessorDirective,
|
||||
TextValueAccessorDirective,
|
||||
BooleanValueAccessor,
|
||||
NumericValueAccessor,
|
||||
RadioValueAccessor,
|
||||
SelectValueAccessor,
|
||||
TextValueAccessor,
|
||||
|
||||
// navigation
|
||||
IonRouterOutlet,
|
||||
IonBackButtonDelegateDirective,
|
||||
IonBackButtonDelegate,
|
||||
NavDelegate,
|
||||
RouterLinkDelegateDirective,
|
||||
RouterLinkDelegate,
|
||||
|
||||
// virtual scroll
|
||||
VirtualFooter,
|
||||
VirtualHeader,
|
||||
VirtualItem,
|
||||
IonVirtualScroll,
|
||||
IonVirtualScroll
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: DECLARATIONS,
|
||||
exports: DECLARATIONS,
|
||||
providers: [AngularDelegate, ModalController, PopoverController],
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule]
|
||||
})
|
||||
export class IonicModule {
|
||||
static forRoot(config?: IonicConfig): ModuleWithProviders<IonicModule> {
|
||||
@@ -216,15 +132,19 @@ export class IonicModule {
|
||||
providers: [
|
||||
{
|
||||
provide: ConfigToken,
|
||||
useValue: config,
|
||||
useValue: config
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: appInitialize,
|
||||
multi: true,
|
||||
deps: [ConfigToken, DOCUMENT, NgZone],
|
||||
},
|
||||
],
|
||||
deps: [
|
||||
ConfigToken,
|
||||
DOCUMENT,
|
||||
NgZone
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,27 @@
|
||||
import {
|
||||
ApplicationRef,
|
||||
ComponentFactoryResolver,
|
||||
NgZone,
|
||||
ViewContainerRef,
|
||||
Injectable,
|
||||
InjectionToken,
|
||||
Injector,
|
||||
} from '@angular/core';
|
||||
import {
|
||||
FrameworkDelegate,
|
||||
LIFECYCLE_DID_ENTER,
|
||||
LIFECYCLE_DID_LEAVE,
|
||||
LIFECYCLE_WILL_ENTER,
|
||||
LIFECYCLE_WILL_LEAVE,
|
||||
LIFECYCLE_WILL_UNLOAD,
|
||||
} from '@ionic/core';
|
||||
import { ApplicationRef, ComponentFactoryResolver, Injectable, InjectionToken, Injector, NgZone, ViewContainerRef } from '@angular/core';
|
||||
import { FrameworkDelegate, LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_WILL_UNLOAD } from '@ionic/core';
|
||||
|
||||
import { NavParams } from '../directives/navigation/nav-params';
|
||||
|
||||
@Injectable()
|
||||
export class AngularDelegate {
|
||||
constructor(private zone: NgZone, private appRef: ApplicationRef) {}
|
||||
|
||||
constructor(
|
||||
private zone: NgZone,
|
||||
private appRef: ApplicationRef
|
||||
) {}
|
||||
|
||||
create(
|
||||
resolver: ComponentFactoryResolver,
|
||||
injector: Injector,
|
||||
location?: ViewContainerRef
|
||||
): AngularFrameworkDelegate {
|
||||
location?: ViewContainerRef,
|
||||
) {
|
||||
return new AngularFrameworkDelegate(resolver, injector, location, this.appRef, this.zone);
|
||||
}
|
||||
}
|
||||
|
||||
export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
|
||||
private elRefMap = new WeakMap<HTMLElement, any>();
|
||||
private elEventsMap = new WeakMap<HTMLElement, () => void>();
|
||||
|
||||
@@ -40,24 +30,16 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
private injector: Injector,
|
||||
private location: ViewContainerRef | undefined,
|
||||
private appRef: ApplicationRef,
|
||||
private zone: NgZone
|
||||
private zone: NgZone,
|
||||
) {}
|
||||
|
||||
attachViewToDom(container: any, component: any, params?: any, cssClasses?: string[]): Promise<any> {
|
||||
return this.zone.run(() => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const el = attachView(
|
||||
this.zone,
|
||||
this.resolver,
|
||||
this.injector,
|
||||
this.location,
|
||||
this.appRef,
|
||||
this.elRefMap,
|
||||
this.elEventsMap,
|
||||
container,
|
||||
component,
|
||||
params,
|
||||
cssClasses
|
||||
this.zone, this.resolver, this.injector, this.location, this.appRef,
|
||||
this.elRefMap, this.elEventsMap,
|
||||
container, component, params, cssClasses
|
||||
);
|
||||
resolve(el);
|
||||
});
|
||||
@@ -66,7 +48,7 @@ export class AngularFrameworkDelegate implements FrameworkDelegate {
|
||||
|
||||
removeViewFromDom(_container: any, component: any): Promise<void> {
|
||||
return this.zone.run(() => {
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
const componentRef = this.elRefMap.get(component);
|
||||
if (componentRef) {
|
||||
componentRef.destroy();
|
||||
@@ -91,17 +73,14 @@ export const attachView = (
|
||||
appRef: ApplicationRef,
|
||||
elRefMap: WeakMap<HTMLElement, any>,
|
||||
elEventsMap: WeakMap<HTMLElement, () => void>,
|
||||
container: any,
|
||||
component: any,
|
||||
params: any,
|
||||
cssClasses: string[] | undefined
|
||||
): any => {
|
||||
container: any, component: any, params: any, cssClasses: string[] | undefined
|
||||
) => {
|
||||
const factory = resolver.resolveComponentFactory(component);
|
||||
const childInjector = Injector.create({
|
||||
providers: getProviders(params),
|
||||
parent: injector,
|
||||
parent: injector
|
||||
});
|
||||
const componentRef = location
|
||||
const componentRef = (location)
|
||||
? location.createComponent(factory, location.length, childInjector)
|
||||
: factory.create(childInjector);
|
||||
|
||||
@@ -132,36 +111,35 @@ const LIFECYCLES = [
|
||||
LIFECYCLE_DID_ENTER,
|
||||
LIFECYCLE_WILL_LEAVE,
|
||||
LIFECYCLE_DID_LEAVE,
|
||||
LIFECYCLE_WILL_UNLOAD,
|
||||
LIFECYCLE_WILL_UNLOAD
|
||||
];
|
||||
|
||||
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement): (() => void) => {
|
||||
export const bindLifecycleEvents = (zone: NgZone, instance: any, element: HTMLElement) => {
|
||||
return zone.run(() => {
|
||||
const unregisters = LIFECYCLES.filter((eventName) => typeof instance[eventName] === 'function').map((eventName) => {
|
||||
const handler = (ev: any) => instance[eventName](ev.detail);
|
||||
element.addEventListener(eventName, handler);
|
||||
return () => element.removeEventListener(eventName, handler);
|
||||
});
|
||||
return () => unregisters.forEach((fn) => fn());
|
||||
const unregisters = LIFECYCLES
|
||||
.filter(eventName => typeof instance[eventName] === 'function')
|
||||
.map(eventName => {
|
||||
const handler = (ev: any) => instance[eventName](ev.detail);
|
||||
element.addEventListener(eventName, handler);
|
||||
return () => element.removeEventListener(eventName, handler);
|
||||
});
|
||||
return () => unregisters.forEach(fn => fn());
|
||||
});
|
||||
};
|
||||
|
||||
const NavParamsToken = new InjectionToken<any>('NavParamsToken');
|
||||
|
||||
const getProviders = (params: { [key: string]: any }) => {
|
||||
const getProviders = (params: {[key: string]: any}) => {
|
||||
return [
|
||||
{
|
||||
provide: NavParamsToken,
|
||||
useValue: params,
|
||||
provide: NavParamsToken, useValue: params
|
||||
},
|
||||
{
|
||||
provide: NavParams,
|
||||
useFactory: provideNavParamsInjectable,
|
||||
deps: [NavParamsToken],
|
||||
},
|
||||
provide: NavParams, useFactory: provideNavParamsInjectable, deps: [NavParamsToken]
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
const provideNavParamsInjectable = (params: { [key: string]: any }) => {
|
||||
const provideNavParamsInjectable = (params: {[key: string]: any}) => {
|
||||
return new NavParams(params);
|
||||
};
|
||||
|
||||
@@ -4,9 +4,10 @@ import { Config as CoreConfig, IonicConfig } from '@ionic/core';
|
||||
import { IonicWindow } from '../types/interfaces';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class Config {
|
||||
|
||||
get(key: keyof IonicConfig, fallback?: any): any {
|
||||
const c = getConfig();
|
||||
if (c) {
|
||||
@@ -30,6 +31,14 @@ export class Config {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
set(key: keyof IonicConfig, value?: any) {
|
||||
console.warn(`[DEPRECATION][Config]: The Config.set() method is deprecated and will be removed in Ionic Framework 6.0. Please see https://ionicframework.com/docs/angular/config for alternatives.`);
|
||||
const c = getConfig();
|
||||
if (c) {
|
||||
c.set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const ConfigToken = new InjectionToken<any>('USERCONFIG');
|
||||
@@ -37,7 +46,7 @@ export const ConfigToken = new InjectionToken<any>('USERCONFIG');
|
||||
const getConfig = (): CoreConfig | null => {
|
||||
if (typeof (window as any) !== 'undefined') {
|
||||
const Ionic = (window as any as IonicWindow).Ionic;
|
||||
if (Ionic?.config) {
|
||||
if (Ionic && Ionic.config) {
|
||||
return Ionic.config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import { Injectable } from '@angular/core';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DomController {
|
||||
|
||||
/**
|
||||
* Schedules a task to run during the READ phase of the next frame.
|
||||
* This task should only read the DOM, but never modify it.
|
||||
*/
|
||||
read(cb: RafCallback): void {
|
||||
read(cb: RafCallback) {
|
||||
getQueue().read(cb);
|
||||
}
|
||||
|
||||
@@ -16,29 +17,29 @@ export class DomController {
|
||||
* Schedules a task to run during the WRITE phase of the next frame.
|
||||
* This task should write the DOM, but never READ it.
|
||||
*/
|
||||
write(cb: RafCallback): void {
|
||||
write(cb: RafCallback) {
|
||||
getQueue().write(cb);
|
||||
}
|
||||
}
|
||||
|
||||
const getQueue = () => {
|
||||
const win = typeof (window as any) !== 'undefined' ? window : (null as any);
|
||||
const win = typeof (window as any) !== 'undefined' ? window : null as any;
|
||||
|
||||
if (win != null) {
|
||||
const Ionic = win.Ionic;
|
||||
if (Ionic?.queue) {
|
||||
if (Ionic && Ionic.queue) {
|
||||
return Ionic.queue;
|
||||
}
|
||||
|
||||
return {
|
||||
read: (cb: any) => win.requestAnimationFrame(cb),
|
||||
write: (cb: any) => win.requestAnimationFrame(cb),
|
||||
write: (cb: any) => win.requestAnimationFrame(cb)
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
read: (cb: any) => cb(),
|
||||
write: (cb: any) => cb(),
|
||||
write: (cb: any) => cb()
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NgZone, Injectable } from '@angular/core';
|
||||
import { Injectable, NgZone } from '@angular/core';
|
||||
import { Gesture, GestureConfig, createGesture } from '@ionic/core';
|
||||
|
||||
@Injectable({
|
||||
@@ -11,10 +11,10 @@ export class GestureController {
|
||||
*/
|
||||
create(opts: GestureConfig, runInsideAngularZone = false): Gesture {
|
||||
if (runInsideAngularZone) {
|
||||
Object.getOwnPropertyNames(opts).forEach((key) => {
|
||||
Object.getOwnPropertyNames(opts).forEach(key => {
|
||||
if (typeof opts[key] === 'function') {
|
||||
const fn = opts[key];
|
||||
opts[key] = (...props: any[]) => this.zone.run(() => fn(...props));
|
||||
opts[key] = (...props) => this.zone.run(() => fn(...props));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ import { menuController } from '@ionic/core';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class MenuController {
|
||||
|
||||
/**
|
||||
* Programmatically open the Menu.
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return returns a promise when the menu is fully opened
|
||||
*/
|
||||
open(menuId?: string): Promise<boolean> {
|
||||
open(menuId?: string) {
|
||||
return menuController.open(menuId);
|
||||
}
|
||||
|
||||
@@ -21,7 +22,7 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return returns a promise when the menu is fully closed
|
||||
*/
|
||||
close(menuId?: string): Promise<boolean> {
|
||||
close(menuId?: string) {
|
||||
return menuController.close(menuId);
|
||||
}
|
||||
|
||||
@@ -31,7 +32,7 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return returns a promise when the menu has been toggled
|
||||
*/
|
||||
toggle(menuId?: string): Promise<boolean> {
|
||||
toggle(menuId?: string) {
|
||||
return menuController.toggle(menuId);
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
|
||||
enable(shouldEnable: boolean, menuId?: string) {
|
||||
return menuController.enable(shouldEnable, menuId);
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return Returns the instance of the menu, which is useful for chaining.
|
||||
*/
|
||||
swipeGesture(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement | undefined> {
|
||||
swipeGesture(shouldEnable: boolean, menuId?: string) {
|
||||
return menuController.swipeGesture(shouldEnable, menuId);
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ export class MenuController {
|
||||
* @return Returns true if the specified menu is currently open, otherwise false.
|
||||
* If the menuId is not specified, it returns true if ANY menu is currenly open.
|
||||
*/
|
||||
isOpen(menuId?: string): Promise<boolean> {
|
||||
isOpen(menuId?: string) {
|
||||
return menuController.isOpen(menuId);
|
||||
}
|
||||
|
||||
@@ -70,7 +71,7 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return Returns true if the menu is currently enabled, otherwise false.
|
||||
*/
|
||||
isEnabled(menuId?: string): Promise<boolean> {
|
||||
isEnabled(menuId?: string) {
|
||||
return menuController.isEnabled(menuId);
|
||||
}
|
||||
|
||||
@@ -83,21 +84,21 @@ export class MenuController {
|
||||
* @param [menuId] Optionally get the menu by its id, or side.
|
||||
* @return Returns the instance of the menu if found, otherwise `null`.
|
||||
*/
|
||||
get(menuId?: string): Promise<HTMLIonMenuElement | undefined> {
|
||||
get(menuId?: string) {
|
||||
return menuController.get(menuId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the instance of the menu already opened, otherwise `null`.
|
||||
*/
|
||||
getOpen(): Promise<HTMLIonMenuElement | undefined> {
|
||||
getOpen() {
|
||||
return menuController.getOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns an array of all menu instances.
|
||||
*/
|
||||
getMenus(): Promise<HTMLIonMenuElement[]> {
|
||||
getMenus() {
|
||||
return menuController.getMenus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ComponentFactoryResolver, Injector, Injectable } from '@angular/core';
|
||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||
import { ModalOptions, modalController } from '@ionic/core';
|
||||
|
||||
import { OverlayBaseController } from '../util/overlay';
|
||||
@@ -7,10 +7,11 @@ import { AngularDelegate } from './angular-delegate';
|
||||
|
||||
@Injectable()
|
||||
export class ModalController extends OverlayBaseController<ModalOptions, HTMLIonModalElement> {
|
||||
|
||||
constructor(
|
||||
private angularDelegate: AngularDelegate,
|
||||
private resolver: ComponentFactoryResolver,
|
||||
private injector: Injector
|
||||
private injector: Injector,
|
||||
) {
|
||||
super(modalController);
|
||||
}
|
||||
@@ -18,7 +19,7 @@ export class ModalController extends OverlayBaseController<ModalOptions, HTMLIon
|
||||
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
||||
return super.create({
|
||||
...opts,
|
||||
delegate: this.angularDelegate.create(this.resolver, this.injector),
|
||||
delegate: this.angularDelegate.create(this.resolver, this.injector)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Location } from '@angular/common';
|
||||
import { Injectable, Optional } from '@angular/core';
|
||||
import { NavigationExtras, Router, UrlSerializer, UrlTree, NavigationStart } from '@angular/router';
|
||||
import { AnimationBuilder, NavDirection, RouterDirection } from '@ionic/core';
|
||||
import { NavigationExtras, NavigationStart, Router, UrlSerializer, UrlTree } from '@angular/router';
|
||||
import { NavDirection, RouterDirection } from '@ionic/core';
|
||||
|
||||
import { IonRouterOutlet } from '../directives/navigation/ion-router-outlet';
|
||||
|
||||
@@ -9,7 +9,6 @@ import { Platform } from './platform';
|
||||
|
||||
export interface AnimationOptions {
|
||||
animated?: boolean;
|
||||
animation?: AnimationBuilder;
|
||||
animationDirection?: 'forward' | 'back';
|
||||
}
|
||||
|
||||
@@ -19,10 +18,10 @@ export interface NavigationOptions extends NavigationExtras, AnimationOptions {}
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class NavController {
|
||||
|
||||
private topOutlet?: IonRouterOutlet;
|
||||
private direction: 'forward' | 'back' | 'root' | 'auto' = DEFAULT_DIRECTION;
|
||||
private animated?: NavDirection = DEFAULT_ANIMATED;
|
||||
private animationBuilder?: AnimationBuilder;
|
||||
private guessDirection: RouterDirection = 'forward';
|
||||
private guessAnimation?: NavDirection;
|
||||
private lastNavId = -1;
|
||||
@@ -31,13 +30,13 @@ export class NavController {
|
||||
platform: Platform,
|
||||
private location: Location,
|
||||
private serializer: UrlSerializer,
|
||||
@Optional() private router?: Router
|
||||
@Optional() private router?: Router,
|
||||
) {
|
||||
// Subscribe to router events to detect direction
|
||||
if (router) {
|
||||
router.events.subscribe((ev) => {
|
||||
router.events.subscribe(ev => {
|
||||
if (ev instanceof NavigationStart) {
|
||||
const id = ev.restoredState ? ev.restoredState.navigationId : ev.id;
|
||||
const id = (ev.restoredState) ? ev.restoredState.navigationId : ev.id;
|
||||
this.guessDirection = id < this.lastNavId ? 'back' : 'forward';
|
||||
this.guessAnimation = !ev.restoredState ? this.guessDirection : undefined;
|
||||
this.lastNavId = this.guessDirection === 'forward' ? ev.id : id;
|
||||
@@ -46,7 +45,7 @@ export class NavController {
|
||||
}
|
||||
|
||||
// Subscribe to backButton events
|
||||
platform.backButton.subscribeWithPriority(0, (processNextHandler) => {
|
||||
platform.backButton.subscribeWithPriority(0, processNextHandler => {
|
||||
this.pop();
|
||||
processNextHandler();
|
||||
});
|
||||
@@ -66,7 +65,7 @@ export class NavController {
|
||||
* ```
|
||||
*/
|
||||
navigateForward(url: string | UrlTree | any[], options: NavigationOptions = {}): Promise<boolean> {
|
||||
this.setDirection('forward', options.animated, options.animationDirection, options.animation);
|
||||
this.setDirection('forward', options.animated, options.animationDirection);
|
||||
return this.navigate(url, options);
|
||||
}
|
||||
|
||||
@@ -89,7 +88,7 @@ export class NavController {
|
||||
* ```
|
||||
*/
|
||||
navigateBack(url: string | UrlTree | any[], options: NavigationOptions = {}): Promise<boolean> {
|
||||
this.setDirection('back', options.animated, options.animationDirection, options.animation);
|
||||
this.setDirection('back', options.animated, options.animationDirection);
|
||||
return this.navigate(url, options);
|
||||
}
|
||||
|
||||
@@ -112,7 +111,7 @@ export class NavController {
|
||||
* ```
|
||||
*/
|
||||
navigateRoot(url: string | UrlTree | any[], options: NavigationOptions = {}): Promise<boolean> {
|
||||
this.setDirection('root', options.animated, options.animationDirection, options.animation);
|
||||
this.setDirection('root', options.animated, options.animationDirection);
|
||||
return this.navigate(url, options);
|
||||
}
|
||||
|
||||
@@ -121,8 +120,8 @@ export class NavController {
|
||||
* It will use the standard `window.history.back()` under the hood, but featuring a `back` animation
|
||||
* by default.
|
||||
*/
|
||||
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }): void {
|
||||
this.setDirection('back', options.animated, options.animationDirection, options.animation);
|
||||
back(options: AnimationOptions = { animated: true, animationDirection: 'back' }) {
|
||||
this.setDirection('back', options.animated, options.animationDirection);
|
||||
return this.location.back();
|
||||
}
|
||||
|
||||
@@ -132,7 +131,7 @@ export class NavController {
|
||||
* It recursively finds the top active `ion-router-outlet` and calls `pop()`.
|
||||
* This is the recommended way to go back when you are using `ion-router-outlet`.
|
||||
*/
|
||||
async pop(): Promise<void> {
|
||||
async pop() {
|
||||
let outlet = this.topOutlet;
|
||||
|
||||
while (outlet) {
|
||||
@@ -151,35 +150,24 @@ export class NavController {
|
||||
*
|
||||
* It's recommended to use `navigateForward()`, `navigateBack()` and `navigateRoot()` instead of `setDirection()`.
|
||||
*/
|
||||
setDirection(
|
||||
direction: RouterDirection,
|
||||
animated?: boolean,
|
||||
animationDirection?: 'forward' | 'back',
|
||||
animationBuilder?: AnimationBuilder
|
||||
): void {
|
||||
setDirection(direction: RouterDirection, animated?: boolean, animationDirection?: 'forward' | 'back') {
|
||||
this.direction = direction;
|
||||
this.animated = getAnimation(direction, animated, animationDirection);
|
||||
this.animationBuilder = animationBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
setTopOutlet(outlet: IonRouterOutlet): void {
|
||||
setTopOutlet(outlet: IonRouterOutlet) {
|
||||
this.topOutlet = outlet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
consumeTransition(): {
|
||||
direction: RouterDirection;
|
||||
animation: NavDirection | undefined;
|
||||
animationBuilder: AnimationBuilder | undefined;
|
||||
} {
|
||||
consumeTransition() {
|
||||
let direction: RouterDirection = 'root';
|
||||
let animation: NavDirection | undefined;
|
||||
const animationBuilder = this.animationBuilder;
|
||||
|
||||
if (this.direction === 'auto') {
|
||||
direction = this.guessDirection;
|
||||
@@ -190,20 +178,18 @@ export class NavController {
|
||||
}
|
||||
this.direction = DEFAULT_DIRECTION;
|
||||
this.animated = DEFAULT_ANIMATED;
|
||||
this.animationBuilder = undefined;
|
||||
|
||||
return {
|
||||
direction,
|
||||
animation,
|
||||
animationBuilder,
|
||||
animation
|
||||
};
|
||||
}
|
||||
|
||||
private navigate(url: string | UrlTree | any[], options: NavigationOptions) {
|
||||
if (Array.isArray(url)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.router!.navigate(url, options);
|
||||
} else {
|
||||
|
||||
/**
|
||||
* navigateByUrl ignores any properties that
|
||||
* would change the url, so things like queryParams
|
||||
@@ -225,17 +211,12 @@ export class NavController {
|
||||
* that do not modify the url, such as `replaceUrl` which is why
|
||||
* `options` is passed in here.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.router!.navigateByUrl(urlTree, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getAnimation = (
|
||||
direction: RouterDirection,
|
||||
animated: boolean | undefined,
|
||||
animationDirection: 'forward' | 'back' | undefined
|
||||
): NavDirection | undefined => {
|
||||
const getAnimation = (direction: RouterDirection, animated: boolean | undefined, animationDirection: 'forward' | 'back' | undefined): NavDirection | undefined => {
|
||||
if (animated === false) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { NgZone, Inject, Injectable } from '@angular/core';
|
||||
import { Inject, Injectable, NgZone } from '@angular/core';
|
||||
import { BackButtonEventDetail, KeyboardEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
||||
import { Subscription, Subject } from 'rxjs';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
|
||||
export interface BackButtonEmitter extends Subject<BackButtonEventDetail> {
|
||||
subscribeWithPriority(
|
||||
priority: number,
|
||||
callback: (processNextHandler: () => void) => Promise<any> | void
|
||||
): Subscription;
|
||||
subscribeWithPriority(priority: number, callback: (processNextHandler: () => void) => Promise<any> | void): Subscription;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class Platform {
|
||||
|
||||
private _readyPromise: Promise<string>;
|
||||
private win: any;
|
||||
|
||||
@@ -59,9 +57,9 @@ export class Platform {
|
||||
constructor(@Inject(DOCUMENT) private doc: any, zone: NgZone) {
|
||||
zone.run(() => {
|
||||
this.win = doc.defaultView;
|
||||
this.backButton.subscribeWithPriority = function (priority, callback) {
|
||||
return this.subscribe((ev) => {
|
||||
return ev.register(priority, (processNextHandler) => zone.run(() => callback(processNextHandler)));
|
||||
this.backButton.subscribeWithPriority = function(priority, callback) {
|
||||
return this.subscribe(ev => {
|
||||
return ev.register(priority, processNextHandler => zone.run(() => callback(processNextHandler)));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -73,19 +71,12 @@ export class Platform {
|
||||
proxyEvent(this.keyboardDidHide, this.win, 'ionKeyboardDidHide');
|
||||
|
||||
let readyResolve: (value: string) => void;
|
||||
this._readyPromise = new Promise((res) => {
|
||||
readyResolve = res;
|
||||
});
|
||||
if (this.win?.['cordova']) {
|
||||
doc.addEventListener(
|
||||
'deviceready',
|
||||
() => {
|
||||
readyResolve('cordova');
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
this._readyPromise = new Promise(res => { readyResolve = res; });
|
||||
if (this.win && this.win['cordova']) {
|
||||
doc.addEventListener('deviceready', () => {
|
||||
readyResolve('cordova');
|
||||
}, { once: true });
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
readyResolve!('dom');
|
||||
}
|
||||
});
|
||||
@@ -119,7 +110,6 @@ export class Platform {
|
||||
* | Platform Name | Description |
|
||||
* |-----------------|------------------------------------|
|
||||
* | android | on a device running Android. |
|
||||
* | capacitor | on a device running Capacitor. |
|
||||
* | cordova | on a device running Cordova. |
|
||||
* | ios | on a device running iOS. |
|
||||
* | ipad | on an iPad device. |
|
||||
@@ -219,28 +209,28 @@ export class Platform {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if the app is in portrait mode.
|
||||
* Returns `true` if the app is in portait mode.
|
||||
*/
|
||||
isPortrait(): boolean {
|
||||
return this.win.matchMedia?.('(orientation: portrait)').matches;
|
||||
return this.win.matchMedia && this.win.matchMedia('(orientation: portrait)').matches;
|
||||
}
|
||||
|
||||
testUserAgent(expression: string): boolean {
|
||||
const nav = this.win.navigator;
|
||||
return !!(nav?.userAgent && nav.userAgent.indexOf(expression) >= 0);
|
||||
return !!(nav && nav.userAgent && nav.userAgent.indexOf(expression) >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current url.
|
||||
*/
|
||||
url(): string {
|
||||
url() {
|
||||
return this.win.location.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the width of the platform's viewport using `window.innerWidth`.
|
||||
*/
|
||||
width(): number {
|
||||
width() {
|
||||
return this.win.innerWidth;
|
||||
}
|
||||
|
||||
@@ -253,17 +243,17 @@ export class Platform {
|
||||
}
|
||||
|
||||
const readQueryParam = (url: string, key: string) => {
|
||||
key = key.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
|
||||
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
|
||||
const results = regex.exec(url);
|
||||
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
|
||||
};
|
||||
|
||||
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string) => {
|
||||
if (el as any) {
|
||||
if ((el as any)) {
|
||||
el.addEventListener(eventName, (ev: Event | undefined | null) => {
|
||||
// ?? cordova might emit "null" events
|
||||
emitter.next(ev != null ? ((ev as any).detail as T) : undefined);
|
||||
emitter.next(ev != null ? (ev as any).detail as T : undefined);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user