mirror of
https://github.com/vuejs/core.git
synced 2024-10-23 05:34:13 +08:00
style: format html&markdown files (#11531)
This commit is contained in:
parent
7c75cc3988
commit
8a99f903db
8
.github/commit-convention.md
vendored
8
.github/commit-convention.md
vendored
@ -44,7 +44,7 @@ This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||
|
||||
### Full Message Format
|
||||
|
||||
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
@ -74,9 +74,9 @@ The scope could be anything specifying the place of the commit change. For examp
|
||||
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
* use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
* don't capitalize the first letter
|
||||
* no dot (.) at the end
|
||||
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- don't capitalize the first letter
|
||||
- no dot (.) at the end
|
||||
|
||||
### Body
|
||||
|
||||
|
1
.github/contributing.md
vendored
1
.github/contributing.md
vendored
@ -35,7 +35,6 @@ Hi! I'm really excited that you are interested in contributing to Vue.js. Before
|
||||
|
||||
Another aspect of it is that large scale stylistic changes result in massive diffs that touch multiple files, adding noise to the git history and makes tracing behavior changes across commits more cumbersome.
|
||||
|
||||
|
||||
### Pull Request Checklist
|
||||
|
||||
- Vue core has two primary work branches: `main` and `minor`.
|
||||
|
1
.github/maintenance.md
vendored
1
.github/maintenance.md
vendored
@ -80,6 +80,7 @@ Depending on the type of the PR, different considerations need to be taken into
|
||||
- Make sure it doesn't accidentally cause dev-only or compiler-only code branches to be included in the runtime build. Notable case is that some functions in @vue/shared are compiler-only and should not be used in runtime code, e.g. `isHTMLTag` and `isSVGTag`.
|
||||
|
||||
- Performance
|
||||
|
||||
- Be careful about code changes in "hot paths", in particular the Virtual DOM renderer (`runtime-core/src/renderer.ts`) and component instantiation code.
|
||||
|
||||
- Potential Breakage
|
||||
|
@ -1,4 +1,3 @@
|
||||
dist
|
||||
*.md
|
||||
*.html
|
||||
pnpm-lock.yaml
|
||||
CHANGELOG*.md
|
||||
|
@ -10,6 +10,6 @@ Please note that we do not consider XSS via template expressions a valid attack
|
||||
|
||||
We would like to thank the following security researchers for responsibly disclosing security issues to us.
|
||||
|
||||
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290 )
|
||||
- Jeet Pal - [@jeetpal2007](https://github.com/jeetpal2007) | [Email](jeetpal2007@gmail.com) | [LinkedIn](https://in.linkedin.com/in/jeet-pal-22601a290)
|
||||
- Mix - [@mnixry](https://github.com/mnixry)
|
||||
- Aviv Keller - [@RedYetiDev](https://github.com/redyetidev) | [LinkedIn](https://www.linkedin.com/in/redyetidev) <redyetidev@gmail.com>
|
||||
|
@ -1 +1 @@
|
||||
# @vue/compiler-dom
|
||||
# @vue/compiler-dom
|
||||
|
@ -1 +1 @@
|
||||
# @vue/compiler-ssr
|
||||
# @vue/compiler-ssr
|
||||
|
@ -13,7 +13,7 @@ const { render, createApp } = createRenderer({
|
||||
patchProp,
|
||||
insert,
|
||||
remove,
|
||||
createElement
|
||||
createElement,
|
||||
// ...
|
||||
})
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
# @vue/runtime-dom
|
||||
|
||||
``` js
|
||||
```js
|
||||
import { h, createApp } from '@vue/runtime-dom'
|
||||
|
||||
const RootComponent = {
|
||||
render() {
|
||||
return h('div', 'hello world')
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
createApp(RootComponent).mount('#app')
|
||||
|
@ -4,7 +4,7 @@ This is for Vue's own internal tests only - it ensures logic tested using this p
|
||||
|
||||
It can also be used as a reference for implementing a custom renderer.
|
||||
|
||||
``` js
|
||||
```js
|
||||
import { h, render, nodeOps, dumpOps } from '@vue/runtime-test'
|
||||
|
||||
const App = {
|
||||
|
@ -11,7 +11,7 @@
|
||||
```ts
|
||||
function renderToString(
|
||||
input: App | VNode,
|
||||
context?: SSRContext
|
||||
context?: SSRContext,
|
||||
): Promise<string>
|
||||
```
|
||||
|
||||
@ -23,7 +23,7 @@ const { renderToString } = require('@vue/server-renderer')
|
||||
|
||||
const app = createSSRApp({
|
||||
data: () => ({ msg: 'hello' }),
|
||||
template: `<div>{{ msg }}</div>`
|
||||
template: `<div>{{ msg }}</div>`,
|
||||
})
|
||||
|
||||
;(async () => {
|
||||
@ -74,7 +74,7 @@ Render and pipe to an existing [Node.js Writable stream](https://nodejs.org/api/
|
||||
function pipeToNodeWritable(
|
||||
input: App | VNode,
|
||||
context: SSRContext = {},
|
||||
writable: Writable
|
||||
writable: Writable,
|
||||
): void
|
||||
```
|
||||
|
||||
@ -94,7 +94,7 @@ Renders input as a [Web ReadableStream](https://developer.mozilla.org/en-US/docs
|
||||
```ts
|
||||
function renderToWebStream(
|
||||
input: App | VNode,
|
||||
context?: SSRContext
|
||||
context?: SSRContext,
|
||||
): ReadableStream
|
||||
```
|
||||
|
||||
@ -117,7 +117,7 @@ Render and pipe to an existing [Web WritableStream](https://developer.mozilla.or
|
||||
function pipeToWebWritable(
|
||||
input: App | VNode,
|
||||
context: SSRContext = {},
|
||||
writable: WritableStream
|
||||
writable: WritableStream,
|
||||
): void
|
||||
```
|
||||
|
||||
@ -144,7 +144,7 @@ Renders input in streaming mode using a simple readable interface.
|
||||
function renderToSimpleStream(
|
||||
input: App | VNode,
|
||||
context: SSRContext,
|
||||
options: SimpleReadable
|
||||
options: SimpleReadable,
|
||||
): SimpleReadable
|
||||
|
||||
interface SimpleReadable {
|
||||
@ -172,7 +172,7 @@ renderToSimpleStream(
|
||||
},
|
||||
destroy(err) {
|
||||
// error encountered
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@ -7,13 +7,16 @@
|
||||
<link rel="icon" type="image/svg" href="/logo.svg" />
|
||||
<title>Vue SFC Playground</title>
|
||||
<script>
|
||||
const savedPreferDark = localStorage.getItem('vue-sfc-playground-prefer-dark')
|
||||
if (
|
||||
savedPreferDark === 'true' ||
|
||||
(!savedPreferDark && window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
const savedPreferDark = localStorage.getItem(
|
||||
'vue-sfc-playground-prefer-dark',
|
||||
)
|
||||
if (
|
||||
savedPreferDark === 'true' ||
|
||||
(!savedPreferDark &&
|
||||
window.matchMedia('(prefers-color-scheme: dark)').matches)
|
||||
) {
|
||||
document.documentElement.classList.add('dark')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</head>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
@ -1,6 +1,10 @@
|
||||
<title>Vue Template Explorer</title>
|
||||
<link rel="stylesheet" data-name="vs/editor/editor.main" href="https://unpkg.com/monaco-editor@0.20.0/min/vs/editor/editor.main.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
data-name="vs/editor/editor.main"
|
||||
href="https://unpkg.com/monaco-editor@0.20.0/min/vs/editor/editor.main.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
|
||||
<div id="header"></div>
|
||||
<div id="source" class="editor"></div>
|
||||
@ -8,13 +12,13 @@
|
||||
|
||||
<script src="https://unpkg.com/monaco-editor@0.20.0/min/vs/loader.js"></script>
|
||||
<script>
|
||||
require.config({
|
||||
paths: {
|
||||
'vs': 'https://unpkg.com/monaco-editor@0.20.0/min/vs'
|
||||
}
|
||||
})
|
||||
require.config({
|
||||
paths: {
|
||||
vs: 'https://unpkg.com/monaco-editor@0.20.0/min/vs',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<script src="./dist/template-explorer.global.js"></script>
|
||||
<script>
|
||||
require(['vs/editor/editor.main'], init /* injected by build */)
|
||||
require(['vs/editor/editor.main'], init /* injected by build */)
|
||||
</script>
|
||||
|
@ -1,6 +1,10 @@
|
||||
<title>Vue Template Explorer</title>
|
||||
<link rel="stylesheet" data-name="vs/editor/editor.main" href="./node_modules/monaco-editor/min/vs/editor/editor.main.css">
|
||||
<link rel="stylesheet" href="./style.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
data-name="vs/editor/editor.main"
|
||||
href="./node_modules/monaco-editor/min/vs/editor/editor.main.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="./style.css" />
|
||||
|
||||
<div id="header"></div>
|
||||
<div id="source" class="editor"></div>
|
||||
@ -8,13 +12,13 @@
|
||||
|
||||
<script src="./node_modules/monaco-editor/min/vs/loader.js"></script>
|
||||
<script>
|
||||
require.config({
|
||||
paths: {
|
||||
'vs': './node_modules/monaco-editor/min/vs'
|
||||
}
|
||||
})
|
||||
require.config({
|
||||
paths: {
|
||||
vs: './node_modules/monaco-editor/min/vs',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
<script src="./dist/template-explorer.global.js"></script>
|
||||
<script>
|
||||
require(['vs/editor/editor.main'], init /* injected by build */)
|
||||
require(['vs/editor/editor.main'], init /* injected by build */)
|
||||
</script>
|
||||
|
@ -84,12 +84,12 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
|
||||
...options,
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2
|
||||
}
|
||||
}
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@ -103,8 +103,8 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
|
||||
module.exports = {
|
||||
resolve: {
|
||||
alias: {
|
||||
vue: '@vue/compat'
|
||||
}
|
||||
vue: '@vue/compat',
|
||||
},
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
@ -114,13 +114,13 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
|
||||
options: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@ -134,20 +134,20 @@ The following workflow walks through the steps of migrating an actual Vue 2 app
|
||||
export default {
|
||||
resolve: {
|
||||
alias: {
|
||||
vue: '@vue/compat'
|
||||
}
|
||||
vue: '@vue/compat',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
compatConfig: {
|
||||
MODE: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
MODE: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
@ -207,7 +207,7 @@ import { configureCompat } from 'vue'
|
||||
// disable compat for certain features
|
||||
configureCompat({
|
||||
FEATURE_ID_A: false,
|
||||
FEATURE_ID_B: false
|
||||
FEATURE_ID_B: false,
|
||||
})
|
||||
```
|
||||
|
||||
@ -221,7 +221,7 @@ import { configureCompat } from 'vue'
|
||||
configureCompat({
|
||||
MODE: 3,
|
||||
FEATURE_ID_A: true,
|
||||
FEATURE_ID_B: true
|
||||
FEATURE_ID_B: true,
|
||||
})
|
||||
```
|
||||
|
||||
@ -233,8 +233,8 @@ A component can use the `compatConfig` option, which expects the same options as
|
||||
export default {
|
||||
compatConfig: {
|
||||
MODE: 3, // opt-in to Vue 3 behavior for this component only
|
||||
FEATURE_ID_A: true // features can also be toggled at component level
|
||||
}
|
||||
FEATURE_ID_A: true, // features can also be toggled at component level
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@ -256,73 +256,73 @@ Features that start with `COMPILER_` are compiler-specific: if you are using the
|
||||
|
||||
> Should be fixed upfront or will likely lead to errors
|
||||
|
||||
| ID | Type | Description | Docs |
|
||||
| ------------------------------------- | ---- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| GLOBAL_MOUNT_CONTAINER | ⨂ | Mounted application does not replace the element it's mounted to | [link](https://v3-migration.vuejs.org/breaking-changes/mount-changes.html) |
|
||||
| CONFIG_DEVTOOLS | ⨂ | production devtools is now a build-time flag | [link](https://github.com/vuejs/core/tree/main/packages/vue#bundler-build-feature-flags) |
|
||||
| COMPILER_V_IF_V_FOR_PRECEDENCE | ⨂ | `v-if` and `v-for` precedence when used on the same element has changed | [link](https://v3-migration.vuejs.org/breaking-changes/v-if-v-for.html) |
|
||||
| COMPILER_V_IF_SAME_KEY | ⨂ | `v-if` branches can no longer have the same key | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#on-conditional-branches) |
|
||||
| COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT | ⨂ | `<template v-for>` key should now be placed on `<template>` | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for) |
|
||||
| COMPILER_SFC_FUNCTIONAL | ⨂ | `<template functional>` is no longer supported in SFCs | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html#single-file-components-sfcs) | | |
|
||||
| ID | Type | Description | Docs |
|
||||
| ------------------------------------- | ---- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| GLOBAL_MOUNT_CONTAINER | ⨂ | Mounted application does not replace the element it's mounted to | [link](https://v3-migration.vuejs.org/breaking-changes/mount-changes.html) |
|
||||
| CONFIG_DEVTOOLS | ⨂ | production devtools is now a build-time flag | [link](https://github.com/vuejs/core/tree/main/packages/vue#bundler-build-feature-flags) |
|
||||
| COMPILER_V_IF_V_FOR_PRECEDENCE | ⨂ | `v-if` and `v-for` precedence when used on the same element has changed | [link](https://v3-migration.vuejs.org/breaking-changes/v-if-v-for.html) |
|
||||
| COMPILER_V_IF_SAME_KEY | ⨂ | `v-if` branches can no longer have the same key | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#on-conditional-branches) |
|
||||
| COMPILER_V_FOR_TEMPLATE_KEY_PLACEMENT | ⨂ | `<template v-for>` key should now be placed on `<template>` | [link](https://v3-migration.vuejs.org/breaking-changes/key-attribute.html#with-template-v-for) |
|
||||
| COMPILER_SFC_FUNCTIONAL | ⨂ | `<template functional>` is no longer supported in SFCs | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html#single-file-components-sfcs) |
|
||||
|
||||
### Partially Compatible with Caveats
|
||||
|
||||
| ID | Type | Description | Docs |
|
||||
| ------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
| ID | Type | Description | Docs |
|
||||
| ------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| CONFIG_IGNORED_ELEMENTS | ◐ | `config.ignoredElements` is now `config.compilerOptions.isCustomElement` (only in browser compiler build). If using build setup, `isCustomElement` must be passed via build configuration. | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#config-ignoredelements-is-now-config-iscustomelement) |
|
||||
| COMPILER_INLINE_TEMPLATE | ◐ | `inline-template` removed (compat only supported in browser compiler build) | [link](https://v3-migration.vuejs.org/breaking-changes/inline-template-attribute.html) |
|
||||
| PROPS_DEFAULT_THIS | ◐ | props default factory no longer have access to `this` (in compat mode, `this` is not a real instance - it only exposes props, `$options` and injections) | [link](https://v3-migration.vuejs.org/breaking-changes/props-default-this.html) |
|
||||
| INSTANCE_DESTROY | ◐ | `$destroy` instance method removed (in compat mode, only supported on root instance) | |
|
||||
| GLOBAL_PRIVATE_UTIL | ◐ | `Vue.util` is private and no longer available | |
|
||||
| INSTANCE_DESTROY | ◐ | `$destroy` instance method removed (in compat mode, only supported on root instance) | |
|
||||
| GLOBAL_PRIVATE_UTIL | ◐ | `Vue.util` is private and no longer available | |
|
||||
| CONFIG_PRODUCTION_TIP | ◐ | `config.productionTip` no longer necessary | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#config-productiontip-removed) |
|
||||
| CONFIG_SILENT | ◐ | `config.silent` removed |
|
||||
|
||||
### Compat only (no warning)
|
||||
|
||||
| ID | Type | Description | Docs |
|
||||
| ------------------ | ---- | ------------------------------------- | ---------------------------------------- |
|
||||
| ID | Type | Description | Docs |
|
||||
| ------------------ | ---- | -------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| TRANSITION_CLASSES | ⭘ | Transition enter/leave classes changed | [link](https://v3-migration.vuejs.org/breaking-changes/transition.html) |
|
||||
|
||||
### Fully Compatible
|
||||
|
||||
| ID | Type | Description | Docs |
|
||||
| ---------------------------- | ---- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| GLOBAL_MOUNT | ✔ | new Vue() -> createApp | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#mounting-app-instance) |
|
||||
| GLOBAL_EXTEND | ✔ | Vue.extend removed (use `defineComponent` or `extends` option) | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-extend-replaced-by-definecomponent) |
|
||||
| GLOBAL_PROTOTYPE | ✔ | `Vue.prototype` -> `app.config.globalProperties` | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-prototype-replaced-by-config-globalproperties) |
|
||||
| GLOBAL_SET | ✔ | `Vue.set` removed (no longer needed) | |
|
||||
| GLOBAL_DELETE | ✔ | `Vue.delete` removed (no longer needed) | |
|
||||
| GLOBAL_OBSERVABLE | ✔ | `Vue.observable` removed (use `reactive`) | [link](https://vuejs.org/api/reactivity-core.html#reactive) |
|
||||
| CONFIG_KEY_CODES | ✔ | config.keyCodes removed | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) |
|
||||
| CONFIG_WHITESPACE | ✔ | In Vue 3 whitespace defaults to `"condense"` | |
|
||||
| INSTANCE_SET | ✔ | `vm.$set` removed (no longer needed) | |
|
||||
| INSTANCE_DELETE | ✔ | `vm.$delete` removed (no longer needed) | |
|
||||
| INSTANCE_EVENT_EMITTER | ✔ | `vm.$on`, `vm.$off`, `vm.$once` removed | [link](https://v3-migration.vuejs.org/breaking-changes/events-api.html) |
|
||||
| INSTANCE_EVENT_HOOKS | ✔ | Instance no longer emits `hook:x` events | [link](https://v3-migration.vuejs.org/breaking-changes/vnode-lifecycle-events.html) |
|
||||
| INSTANCE_CHILDREN | ✔ | `vm.$children` removed | [link](https://v3-migration.vuejs.org/breaking-changes/children.html) |
|
||||
| INSTANCE_LISTENERS | ✔ | `vm.$listeners` removed | [link](https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html) |
|
||||
| INSTANCE_SCOPED_SLOTS | ✔ | `vm.$scopedSlots` removed; `vm.$slots` now exposes functions | [link](https://v3-migration.vuejs.org/breaking-changes/slots-unification.html) |
|
||||
| INSTANCE_ATTRS_CLASS_STYLE | ✔ | `$attrs` now includes `class` and `style` | [link](https://v3-migration.vuejs.org/breaking-changes/attrs-includes-class-style.html) |
|
||||
| OPTIONS_DATA_FN | ✔ | `data` must be a function in all cases | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) |
|
||||
| OPTIONS_DATA_MERGE | ✔ | `data` from mixin or extension is now shallow merged | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) |
|
||||
| OPTIONS_BEFORE_DESTROY | ✔ | `beforeDestroy` -> `beforeUnmount` | |
|
||||
| OPTIONS_DESTROYED | ✔ | `destroyed` -> `unmounted` | |
|
||||
| WATCH_ARRAY | ✔ | watching an array no longer triggers on mutation unless deep | [link](https://v3-migration.vuejs.org/breaking-changes/watch.html) |
|
||||
| V_ON_KEYCODE_MODIFIER | ✔ | `v-on` no longer supports keyCode modifiers | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) |
|
||||
| CUSTOM_DIR | ✔ | Custom directive hook names changed | [link](https://v3-migration.vuejs.org/breaking-changes/custom-directives.html) |
|
||||
| ATTR_FALSE_VALUE | ✔ | No longer removes attribute if binding value is boolean `false` | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) |
|
||||
| ATTR_ENUMERATED_COERCION | ✔ | No longer special case enumerated attributes | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) |
|
||||
| TRANSITION_GROUP_ROOT | ✔ | `<transition-group>` no longer renders a root element by default | [link](https://v3-migration.vuejs.org/breaking-changes/transition-group.html) |
|
||||
| COMPONENT_ASYNC | ✔ | Async component API changed (now requires `defineAsyncComponent`) | [link](https://v3-migration.vuejs.org/breaking-changes/async-components.html) |
|
||||
| COMPONENT_FUNCTIONAL | ✔ | Functional component API changed (now must be plain functions) | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html) |
|
||||
| COMPONENT_V_MODEL | ✔ | Component v-model reworked | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) |
|
||||
| RENDER_FUNCTION | ✔ | Render function API changed | [link](https://v3-migration.vuejs.org/breaking-changes/render-function-api.html) |
|
||||
| FILTERS | ✔ | Filters removed (this option affects only runtime filter APIs) | [link](https://v3-migration.vuejs.org/breaking-changes/filters.html) |
|
||||
| COMPILER_IS_ON_ELEMENT | ✔ | `is` usage is now restricted to `<component>` only | [link](https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html) |
|
||||
| COMPILER_V_BIND_SYNC | ✔ | `v-bind.sync` replaced by `v-model` with arguments | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) |
|
||||
| COMPILER_V_BIND_PROP | ✔ | `v-bind.prop` modifier removed | |
|
||||
| COMPILER_V_BIND_OBJECT_ORDER | ✔ | `v-bind="object"` is now order sensitive | [link](https://v3-migration.vuejs.org/breaking-changes/v-bind.html) |
|
||||
| COMPILER_V_ON_NATIVE | ✔ | `v-on.native` modifier removed | [link](https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html) |
|
||||
| COMPILER_V_FOR_REF | ✔ | `ref` in `v-for` (compiler support) | |
|
||||
| COMPILER_NATIVE_TEMPLATE | ✔ | `<template>` with no special directives now renders as native element | |
|
||||
| COMPILER_FILTERS | ✔ | filters (compiler support) | |
|
||||
| ID | Type | Description | Docs |
|
||||
| ---------------------------- | ---- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||
| GLOBAL_MOUNT | ✔ | new Vue() -> createApp | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#mounting-app-instance) |
|
||||
| GLOBAL_EXTEND | ✔ | Vue.extend removed (use `defineComponent` or `extends` option) | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-extend-replaced-by-definecomponent) |
|
||||
| GLOBAL_PROTOTYPE | ✔ | `Vue.prototype` -> `app.config.globalProperties` | [link](https://v3-migration.vuejs.org/breaking-changes/global-api.html#vue-prototype-replaced-by-config-globalproperties) |
|
||||
| GLOBAL_SET | ✔ | `Vue.set` removed (no longer needed) | |
|
||||
| GLOBAL_DELETE | ✔ | `Vue.delete` removed (no longer needed) | |
|
||||
| GLOBAL_OBSERVABLE | ✔ | `Vue.observable` removed (use `reactive`) | [link](https://vuejs.org/api/reactivity-core.html#reactive) |
|
||||
| CONFIG_KEY_CODES | ✔ | config.keyCodes removed | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) |
|
||||
| CONFIG_WHITESPACE | ✔ | In Vue 3 whitespace defaults to `"condense"` | |
|
||||
| INSTANCE_SET | ✔ | `vm.$set` removed (no longer needed) | |
|
||||
| INSTANCE_DELETE | ✔ | `vm.$delete` removed (no longer needed) | |
|
||||
| INSTANCE_EVENT_EMITTER | ✔ | `vm.$on`, `vm.$off`, `vm.$once` removed | [link](https://v3-migration.vuejs.org/breaking-changes/events-api.html) |
|
||||
| INSTANCE_EVENT_HOOKS | ✔ | Instance no longer emits `hook:x` events | [link](https://v3-migration.vuejs.org/breaking-changes/vnode-lifecycle-events.html) |
|
||||
| INSTANCE_CHILDREN | ✔ | `vm.$children` removed | [link](https://v3-migration.vuejs.org/breaking-changes/children.html) |
|
||||
| INSTANCE_LISTENERS | ✔ | `vm.$listeners` removed | [link](https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html) |
|
||||
| INSTANCE_SCOPED_SLOTS | ✔ | `vm.$scopedSlots` removed; `vm.$slots` now exposes functions | [link](https://v3-migration.vuejs.org/breaking-changes/slots-unification.html) |
|
||||
| INSTANCE_ATTRS_CLASS_STYLE | ✔ | `$attrs` now includes `class` and `style` | [link](https://v3-migration.vuejs.org/breaking-changes/attrs-includes-class-style.html) |
|
||||
| OPTIONS_DATA_FN | ✔ | `data` must be a function in all cases | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) |
|
||||
| OPTIONS_DATA_MERGE | ✔ | `data` from mixin or extension is now shallow merged | [link](https://v3-migration.vuejs.org/breaking-changes/data-option.html) |
|
||||
| OPTIONS_BEFORE_DESTROY | ✔ | `beforeDestroy` -> `beforeUnmount` | |
|
||||
| OPTIONS_DESTROYED | ✔ | `destroyed` -> `unmounted` | |
|
||||
| WATCH_ARRAY | ✔ | watching an array no longer triggers on mutation unless deep | [link](https://v3-migration.vuejs.org/breaking-changes/watch.html) |
|
||||
| V_ON_KEYCODE_MODIFIER | ✔ | `v-on` no longer supports keyCode modifiers | [link](https://v3-migration.vuejs.org/breaking-changes/keycode-modifiers.html) |
|
||||
| CUSTOM_DIR | ✔ | Custom directive hook names changed | [link](https://v3-migration.vuejs.org/breaking-changes/custom-directives.html) |
|
||||
| ATTR_FALSE_VALUE | ✔ | No longer removes attribute if binding value is boolean `false` | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) |
|
||||
| ATTR_ENUMERATED_COERCION | ✔ | No longer special case enumerated attributes | [link](https://v3-migration.vuejs.org/breaking-changes/attribute-coercion.html) |
|
||||
| TRANSITION_GROUP_ROOT | ✔ | `<transition-group>` no longer renders a root element by default | [link](https://v3-migration.vuejs.org/breaking-changes/transition-group.html) |
|
||||
| COMPONENT_ASYNC | ✔ | Async component API changed (now requires `defineAsyncComponent`) | [link](https://v3-migration.vuejs.org/breaking-changes/async-components.html) |
|
||||
| COMPONENT_FUNCTIONAL | ✔ | Functional component API changed (now must be plain functions) | [link](https://v3-migration.vuejs.org/breaking-changes/functional-components.html) |
|
||||
| COMPONENT_V_MODEL | ✔ | Component v-model reworked | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) |
|
||||
| RENDER_FUNCTION | ✔ | Render function API changed | [link](https://v3-migration.vuejs.org/breaking-changes/render-function-api.html) |
|
||||
| FILTERS | ✔ | Filters removed (this option affects only runtime filter APIs) | [link](https://v3-migration.vuejs.org/breaking-changes/filters.html) |
|
||||
| COMPILER_IS_ON_ELEMENT | ✔ | `is` usage is now restricted to `<component>` only | [link](https://v3-migration.vuejs.org/breaking-changes/custom-elements-interop.html) |
|
||||
| COMPILER_V_BIND_SYNC | ✔ | `v-bind.sync` replaced by `v-model` with arguments | [link](https://v3-migration.vuejs.org/breaking-changes/v-model.html) |
|
||||
| COMPILER_V_BIND_PROP | ✔ | `v-bind.prop` modifier removed | |
|
||||
| COMPILER_V_BIND_OBJECT_ORDER | ✔ | `v-bind="object"` is now order sensitive | [link](https://v3-migration.vuejs.org/breaking-changes/v-bind.html) |
|
||||
| COMPILER_V_ON_NATIVE | ✔ | `v-on.native` modifier removed | [link](https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html) |
|
||||
| COMPILER_V_FOR_REF | ✔ | `ref` in `v-for` (compiler support) | |
|
||||
| COMPILER_NATIVE_TEMPLATE | ✔ | `<template>` with no special directives now renders as native element | |
|
||||
| COMPILER_FILTERS | ✔ | filters (compiler support) | |
|
||||
|
@ -5,8 +5,9 @@
|
||||
### From CDN or without a Bundler
|
||||
|
||||
- **`vue(.runtime).global(.prod).js`**:
|
||||
|
||||
- For direct use via `<script src="...">` in the browser. Exposes the `Vue` global.
|
||||
- Note that global builds are not [UMD](https://github.com/umdjs/umd) builds. They are built as [IIFEs](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) and is only meant for direct use via `<script src="...">`.
|
||||
- Note that global builds are not [UMD](https://github.com/umdjs/umd) builds. They are built as [IIFEs](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) and is only meant for direct use via `<script src="...">`.
|
||||
- In-browser template compilation:
|
||||
- **`vue.global.js`** is the "full" build that includes both the compiler and the runtime so it supports compiling templates on the fly.
|
||||
- **`vue.runtime.global.js`** contains only the runtime and requires templates to be pre-compiled during a build step.
|
||||
@ -38,10 +39,12 @@
|
||||
`esm-bundler` builds of Vue expose global feature flags that can be overwritten at compile time:
|
||||
|
||||
- `__VUE_OPTIONS_API__`
|
||||
|
||||
- Default: `true`
|
||||
- Enable / disable Options API support
|
||||
|
||||
- `__VUE_PROD_DEVTOOLS__`
|
||||
|
||||
- Default: `false`
|
||||
- Enable / disable devtools support in production
|
||||
|
||||
|
@ -10,9 +10,14 @@
|
||||
-webkit-transition: -webkit-transform 50ms ease;
|
||||
transition: transform 50ms ease;
|
||||
}
|
||||
.v-appear, .v-enter, .v-leave-active,
|
||||
.test-appear, .test-enter, .test-leave-active,
|
||||
.hello, .bye.active,
|
||||
.v-appear,
|
||||
.v-enter,
|
||||
.v-leave-active,
|
||||
.test-appear,
|
||||
.test-enter,
|
||||
.test-leave-active,
|
||||
.hello,
|
||||
.bye.active,
|
||||
.changed-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
@ -33,19 +38,35 @@
|
||||
-webkit-animation: test-leave 100ms;
|
||||
}
|
||||
@keyframes test-enter {
|
||||
from { opacity: 0 }
|
||||
to { opacity: 1 }
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes test-enter {
|
||||
from { opacity: 0 }
|
||||
to { opacity: 1 }
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes test-leave {
|
||||
from { opacity: 1 }
|
||||
to { opacity: 0 }
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes test-leave {
|
||||
from { opacity: 1 }
|
||||
to { opacity: 0 }
|
||||
from {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,59 +3,68 @@
|
||||
<div id="demo">
|
||||
<h1>Latest Vue.js Commits</h1>
|
||||
<template v-for="branch in branches">
|
||||
<input type="radio"
|
||||
<input
|
||||
type="radio"
|
||||
:id="branch"
|
||||
:value="branch"
|
||||
name="branch"
|
||||
v-model="currentBranch">
|
||||
v-model="currentBranch"
|
||||
/>
|
||||
<label :for="branch">{{ branch }}</label>
|
||||
</template>
|
||||
<p>vuejs/core@{{ currentBranch }}</p>
|
||||
<ul>
|
||||
<li v-for="{ html_url, sha, author, commit } in commits">
|
||||
<a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a>
|
||||
- <span class="message">{{ truncate(commit.message) }}</span><br>
|
||||
by <span class="author"><a :href="author.html_url" target="_blank">{{ commit.author.name }}</a></span>
|
||||
<a :href="html_url" target="_blank" class="commit"
|
||||
>{{ sha.slice(0, 7) }}</a
|
||||
>
|
||||
- <span class="message">{{ truncate(commit.message) }}</span><br />
|
||||
by
|
||||
<span class="author"
|
||||
><a :href="author.html_url" target="_blank"
|
||||
>{{ commit.author.name }}</a
|
||||
></span
|
||||
>
|
||||
at <span class="date">{{ formatDate(commit.author.date) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
|
||||
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
|
||||
|
||||
Vue.createApp({
|
||||
data: () => ({
|
||||
branches: ['main', 'v2-compat'],
|
||||
currentBranch: 'main',
|
||||
commits: null
|
||||
}),
|
||||
Vue.createApp({
|
||||
data: () => ({
|
||||
branches: ['main', 'v2-compat'],
|
||||
currentBranch: 'main',
|
||||
commits: null,
|
||||
}),
|
||||
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
|
||||
watch: {
|
||||
currentBranch: 'fetchData'
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchData() {
|
||||
fetch(`${API_URL}${this.currentBranch}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
this.commits = data
|
||||
})
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
truncate(v) {
|
||||
const newline = v.indexOf('\n')
|
||||
return newline > 0 ? v.slice(0, newline) : v
|
||||
|
||||
watch: {
|
||||
currentBranch: 'fetchData',
|
||||
},
|
||||
formatDate(v) {
|
||||
return v.replace(/T|Z/g, ' ')
|
||||
}
|
||||
}
|
||||
}).mount('#demo')
|
||||
|
||||
methods: {
|
||||
fetchData() {
|
||||
fetch(`${API_URL}${this.currentBranch}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
this.commits = data
|
||||
})
|
||||
},
|
||||
truncate(v) {
|
||||
const newline = v.indexOf('\n')
|
||||
return newline > 0 ? v.slice(0, newline) : v
|
||||
},
|
||||
formatDate(v) {
|
||||
return v.replace(/T|Z/g, ' ')
|
||||
},
|
||||
},
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@ -70,7 +79,8 @@ Vue.createApp({
|
||||
line-height: 1.5em;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.author, .date {
|
||||
.author,
|
||||
.date {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
@ -26,142 +26,141 @@
|
||||
</script>
|
||||
<!-- DemoGrid component script -->
|
||||
<script>
|
||||
const DemoGrid = {
|
||||
template: '#grid-template',
|
||||
props: {
|
||||
data: Array,
|
||||
columns: Array,
|
||||
filterKey: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sortKey: '',
|
||||
sortOrders: this.columns.reduce((o, key) => (o[key] = 1, o), {})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredData() {
|
||||
const sortKey = this.sortKey
|
||||
const filterKey = this.filterKey && this.filterKey.toLowerCase()
|
||||
const order = this.sortOrders[sortKey] || 1
|
||||
let data = this.data
|
||||
if (filterKey) {
|
||||
data = data.filter(row => {
|
||||
return Object.keys(row).some(key => {
|
||||
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
|
||||
})
|
||||
})
|
||||
}
|
||||
if (sortKey) {
|
||||
data = data.slice().sort((a, b) => {
|
||||
a = a[sortKey]
|
||||
b = b[sortKey]
|
||||
return (a === b ? 0 : a > b ? 1 : -1) * order
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
sortBy(key) {
|
||||
this.sortKey = key
|
||||
this.sortOrders[key] = this.sortOrders[key] * -1
|
||||
const DemoGrid = {
|
||||
template: '#grid-template',
|
||||
props: {
|
||||
data: Array,
|
||||
columns: Array,
|
||||
filterKey: String,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sortKey: '',
|
||||
sortOrders: this.columns.reduce((o, key) => ((o[key] = 1), o), {}),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredData() {
|
||||
const sortKey = this.sortKey
|
||||
const filterKey = this.filterKey && this.filterKey.toLowerCase()
|
||||
const order = this.sortOrders[sortKey] || 1
|
||||
let data = this.data
|
||||
if (filterKey) {
|
||||
data = data.filter(row => {
|
||||
return Object.keys(row).some(key => {
|
||||
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
|
||||
})
|
||||
})
|
||||
}
|
||||
if (sortKey) {
|
||||
data = data.slice().sort((a, b) => {
|
||||
a = a[sortKey]
|
||||
b = b[sortKey]
|
||||
return (a === b ? 0 : a > b ? 1 : -1) * order
|
||||
})
|
||||
}
|
||||
return data
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
sortBy(key) {
|
||||
this.sortKey = key
|
||||
this.sortOrders[key] = this.sortOrders[key] * -1
|
||||
},
|
||||
capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
},
|
||||
},
|
||||
capitalize(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- App template (in DOM) -->
|
||||
<div id="demo">
|
||||
<form id="search">
|
||||
Search <input name="query" v-model="searchQuery">
|
||||
</form>
|
||||
<demo-grid
|
||||
:data="gridData"
|
||||
:columns="gridColumns"
|
||||
:filter-key="searchQuery">
|
||||
<form id="search">Search <input name="query" v-model="searchQuery" /></form>
|
||||
<demo-grid :data="gridData" :columns="gridColumns" :filter-key="searchQuery">
|
||||
</demo-grid>
|
||||
</div>
|
||||
<!-- App script -->
|
||||
<script>
|
||||
Vue.createApp({
|
||||
components: {
|
||||
DemoGrid
|
||||
},
|
||||
data: () => ({
|
||||
searchQuery: '',
|
||||
gridColumns: ['name', 'power'],
|
||||
gridData: [
|
||||
{ name: 'Chuck Norris', power: Infinity },
|
||||
{ name: 'Bruce Lee', power: 9000 },
|
||||
{ name: 'Jackie Chan', power: 7000 },
|
||||
{ name: 'Jet Li', power: 8000 }
|
||||
]
|
||||
})
|
||||
}).mount('#demo')
|
||||
Vue.createApp({
|
||||
components: {
|
||||
DemoGrid,
|
||||
},
|
||||
data: () => ({
|
||||
searchQuery: '',
|
||||
gridColumns: ['name', 'power'],
|
||||
gridData: [
|
||||
{ name: 'Chuck Norris', power: Infinity },
|
||||
{ name: 'Bruce Lee', power: 9000 },
|
||||
{ name: 'Jackie Chan', power: 7000 },
|
||||
{ name: 'Jet Li', power: 8000 },
|
||||
],
|
||||
}),
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 2px solid #42b983;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
table {
|
||||
border: 2px solid #42b983;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #42b983;
|
||||
color: rgba(255,255,255,0.66);
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
th {
|
||||
background-color: #42b983;
|
||||
color: rgba(255, 255, 255, 0.66);
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
th, td {
|
||||
min-width: 120px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
min-width: 120px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
th.active {
|
||||
color: #fff;
|
||||
}
|
||||
th.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
th.active .arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
th.active .arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
|
||||
.arrow.asc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid #fff;
|
||||
}
|
||||
.arrow.asc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid #fff;
|
||||
}
|
||||
|
||||
.arrow.dsc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #fff;
|
||||
}
|
||||
.arrow.dsc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #fff;
|
||||
}
|
||||
</style>
|
||||
|
@ -8,55 +8,58 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
Vue.createApp({
|
||||
data: () => ({
|
||||
input: '# hello'
|
||||
}),
|
||||
computed: {
|
||||
compiledMarkdown() {
|
||||
return marked.marked(this.input, { sanitize: true })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update: _.debounce(function (e) {
|
||||
this.input = e.target.value
|
||||
}, 50)
|
||||
}
|
||||
}).mount('#editor')
|
||||
Vue.createApp({
|
||||
data: () => ({
|
||||
input: '# hello',
|
||||
}),
|
||||
computed: {
|
||||
compiledMarkdown() {
|
||||
return marked.marked(this.input, { sanitize: true })
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
update: _.debounce(function (e) {
|
||||
this.input = e.target.value
|
||||
}, 50),
|
||||
},
|
||||
}).mount('#editor')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body, #editor {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
#editor {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
textarea, #editor div {
|
||||
display: inline-block;
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
}
|
||||
textarea,
|
||||
#editor div {
|
||||
display: inline-block;
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: none;
|
||||
border-right: 1px solid #ccc;
|
||||
resize: none;
|
||||
outline: none;
|
||||
background-color: #f6f6f6;
|
||||
font-size: 14px;
|
||||
font-family: 'Monaco', courier, monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
textarea {
|
||||
border: none;
|
||||
border-right: 1px solid #ccc;
|
||||
resize: none;
|
||||
outline: none;
|
||||
background-color: #f6f6f6;
|
||||
font-size: 14px;
|
||||
font-family: 'Monaco', courier, monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #f66;
|
||||
}
|
||||
code {
|
||||
color: #f66;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,37 +1,33 @@
|
||||
<script src="../../dist/vue.global.js"></script>
|
||||
<script>
|
||||
// math helper...
|
||||
function valueToPoint (value, index, total) {
|
||||
var x = 0
|
||||
var y = -value * 0.8
|
||||
var angle = Math.PI * 2 / total * index
|
||||
var cos = Math.cos(angle)
|
||||
var sin = Math.sin(angle)
|
||||
var tx = x * cos - y * sin + 100
|
||||
var ty = x * sin + y * cos + 100
|
||||
return {
|
||||
x: tx,
|
||||
y: ty
|
||||
}
|
||||
}
|
||||
|
||||
const AxisLabel = {
|
||||
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
|
||||
props: {
|
||||
stat: Object,
|
||||
index: Number,
|
||||
total: Number
|
||||
},
|
||||
computed: {
|
||||
point: function () {
|
||||
return valueToPoint(
|
||||
+this.stat.value + 10,
|
||||
this.index,
|
||||
this.total
|
||||
)
|
||||
// math helper...
|
||||
function valueToPoint(value, index, total) {
|
||||
var x = 0
|
||||
var y = -value * 0.8
|
||||
var angle = ((Math.PI * 2) / total) * index
|
||||
var cos = Math.cos(angle)
|
||||
var sin = Math.sin(angle)
|
||||
var tx = x * cos - y * sin + 100
|
||||
var ty = x * sin + y * cos + 100
|
||||
return {
|
||||
x: tx,
|
||||
y: ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AxisLabel = {
|
||||
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
|
||||
props: {
|
||||
stat: Object,
|
||||
index: Number,
|
||||
total: Number,
|
||||
},
|
||||
computed: {
|
||||
point: function () {
|
||||
return valueToPoint(+this.stat.value + 10, this.index, this.total)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- template for the polygraph component. -->
|
||||
@ -49,23 +45,25 @@ const AxisLabel = {
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const Polygraph = {
|
||||
props: ['stats'],
|
||||
template: '#polygraph-template',
|
||||
computed: {
|
||||
// a computed property for the polygon's points
|
||||
points() {
|
||||
const total = this.stats.length
|
||||
return this.stats.map((stat, i) => {
|
||||
const point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
}).join(' ')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AxisLabel
|
||||
const Polygraph = {
|
||||
props: ['stats'],
|
||||
template: '#polygraph-template',
|
||||
computed: {
|
||||
// a computed property for the polygon's points
|
||||
points() {
|
||||
const total = this.stats.length
|
||||
return this.stats
|
||||
.map((stat, i) => {
|
||||
const point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
})
|
||||
.join(' ')
|
||||
},
|
||||
},
|
||||
components: {
|
||||
AxisLabel,
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- demo root element -->
|
||||
@ -77,86 +75,92 @@ const Polygraph = {
|
||||
<!-- controls -->
|
||||
<div v-for="stat in stats">
|
||||
<label>{{stat.label}}</label>
|
||||
<input type="range" v-model="stat.value" min="0" max="100">
|
||||
<input type="range" v-model="stat.value" min="0" max="100" />
|
||||
<span>{{stat.value}}</span>
|
||||
<button @click="remove(stat)" class="remove">X</button>
|
||||
</div>
|
||||
<form id="add">
|
||||
<input name="newlabel" v-model="newLabel">
|
||||
<input name="newlabel" v-model="newLabel" />
|
||||
<button @click="add">Add a Stat</button>
|
||||
</form>
|
||||
<pre id="raw">{{ stats }}</pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const globalStats = [
|
||||
{ label: 'A', value: 100 },
|
||||
{ label: 'B', value: 100 },
|
||||
{ label: 'C', value: 100 },
|
||||
{ label: 'D', value: 100 },
|
||||
{ label: 'E', value: 100 },
|
||||
{ label: 'F', value: 100 }
|
||||
]
|
||||
const globalStats = [
|
||||
{ label: 'A', value: 100 },
|
||||
{ label: 'B', value: 100 },
|
||||
{ label: 'C', value: 100 },
|
||||
{ label: 'D', value: 100 },
|
||||
{ label: 'E', value: 100 },
|
||||
{ label: 'F', value: 100 },
|
||||
]
|
||||
|
||||
Vue.createApp({
|
||||
components: {
|
||||
Polygraph
|
||||
},
|
||||
data: () => ({
|
||||
newLabel: '',
|
||||
stats: globalStats
|
||||
}),
|
||||
methods: {
|
||||
add(e) {
|
||||
e.preventDefault()
|
||||
if (!this.newLabel) return
|
||||
this.stats.push({
|
||||
label: this.newLabel,
|
||||
value: 100
|
||||
})
|
||||
this.newLabel = ''
|
||||
Vue.createApp({
|
||||
components: {
|
||||
Polygraph,
|
||||
},
|
||||
remove(stat) {
|
||||
if (this.stats.length > 3) {
|
||||
this.stats.splice(this.stats.indexOf(stat), 1)
|
||||
} else {
|
||||
alert('Can\'t delete more!')
|
||||
}
|
||||
}
|
||||
}
|
||||
}).mount('#demo')
|
||||
data: () => ({
|
||||
newLabel: '',
|
||||
stats: globalStats,
|
||||
}),
|
||||
methods: {
|
||||
add(e) {
|
||||
e.preventDefault()
|
||||
if (!this.newLabel) return
|
||||
this.stats.push({
|
||||
label: this.newLabel,
|
||||
value: 100,
|
||||
})
|
||||
this.newLabel = ''
|
||||
},
|
||||
remove(stat) {
|
||||
if (this.stats.length > 3) {
|
||||
this.stats.splice(this.stats.indexOf(stat), 1)
|
||||
} else {
|
||||
alert("Can't delete more!")
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
polygon {
|
||||
polygon {
|
||||
fill: #42b983;
|
||||
opacity: .75;
|
||||
}
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
circle {
|
||||
circle {
|
||||
fill: transparent;
|
||||
stroke: #999;
|
||||
}
|
||||
}
|
||||
|
||||
text {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
text {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-size: 10px;
|
||||
fill: #666;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#raw {
|
||||
#raw {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,49 +1,81 @@
|
||||
<script src="../../dist/vue.global.js"></script>
|
||||
<link rel="stylesheet" href="../../../../node_modules/todomvc-app-css/index.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../../../../node_modules/todomvc-app-css/index.css"
|
||||
/>
|
||||
|
||||
<div id="app">
|
||||
<section class="todoapp">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<input class="new-todo"
|
||||
autofocus autocomplete="off"
|
||||
placeholder="What needs to be done?"
|
||||
v-model="newTodo"
|
||||
@keyup.enter="addTodo">
|
||||
<input
|
||||
class="new-todo"
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
placeholder="What needs to be done?"
|
||||
v-model="newTodo"
|
||||
@keyup.enter="addTodo"
|
||||
/>
|
||||
</header>
|
||||
<section class="main" v-show="todos.length">
|
||||
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="allDone">
|
||||
<input
|
||||
id="toggle-all"
|
||||
class="toggle-all"
|
||||
type="checkbox"
|
||||
v-model="allDone"
|
||||
/>
|
||||
<label for="toggle-all">Mark all as complete</label>
|
||||
<ul class="todo-list">
|
||||
<li v-for="todo in filteredTodos"
|
||||
class="todo"
|
||||
:key="todo.id"
|
||||
:class="{ completed: todo.completed, editing: todo === editedTodo }">
|
||||
<li
|
||||
v-for="todo in filteredTodos"
|
||||
class="todo"
|
||||
:key="todo.id"
|
||||
:class="{ completed: todo.completed, editing: todo === editedTodo }"
|
||||
>
|
||||
<div class="view">
|
||||
<input class="toggle" type="checkbox" v-model="todo.completed">
|
||||
<input class="toggle" type="checkbox" v-model="todo.completed" />
|
||||
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
|
||||
<button class="destroy" @click="removeTodo(todo)"></button>
|
||||
</div>
|
||||
<input class="edit" type="text"
|
||||
v-model="todo.title"
|
||||
v-todo-focus="todo === editedTodo"
|
||||
@blur="doneEdit(todo)"
|
||||
@keyup.enter="doneEdit(todo)"
|
||||
@keyup.escape="cancelEdit(todo)"
|
||||
>
|
||||
<input
|
||||
class="edit"
|
||||
type="text"
|
||||
v-model="todo.title"
|
||||
v-todo-focus="todo === editedTodo"
|
||||
@blur="doneEdit(todo)"
|
||||
@keyup.enter="doneEdit(todo)"
|
||||
@keyup.escape="cancelEdit(todo)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer class="footer" v-show="todos.length">
|
||||
<span class="todo-count">
|
||||
<strong>{{ remaining }}</strong> <span>{{ pluralize(remaining) }} left</span>
|
||||
</span>
|
||||
<span class="todo-count">
|
||||
<strong>{{ remaining }}</strong>
|
||||
<span>{{ pluralize(remaining) }} left</span>
|
||||
</span>
|
||||
<ul class="filters">
|
||||
<li><a href="#/all" :class="{ selected: visibility === 'all' }">All</a></li>
|
||||
<li><a href="#/active" :class="{ selected: visibility === 'active' }">Active</a></li>
|
||||
<li><a href="#/completed" :class="{ selected: visibility === 'completed' }">Completed</a></li>
|
||||
<li>
|
||||
<a href="#/all" :class="{ selected: visibility === 'all' }">All</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#/active" :class="{ selected: visibility === 'active' }"
|
||||
>Active</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#/completed"
|
||||
:class="{ selected: visibility === 'completed' }"
|
||||
>Completed</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
|
||||
<button
|
||||
class="clear-completed"
|
||||
@click="removeCompleted"
|
||||
v-show="todos.length > remaining"
|
||||
>
|
||||
Clear completed
|
||||
</button>
|
||||
</footer>
|
||||
@ -51,146 +83,146 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const STORAGE_KEY = 'todos-vuejs-3.x'
|
||||
const todoStorage = {
|
||||
fetch() {
|
||||
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
|
||||
todos.forEach((todo, index) => {
|
||||
todo.id = index
|
||||
})
|
||||
todoStorage.uid = todos.length
|
||||
return todos
|
||||
},
|
||||
save(todos) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
|
||||
}
|
||||
}
|
||||
|
||||
const filters = {
|
||||
all(todos) {
|
||||
return todos
|
||||
},
|
||||
active(todos) {
|
||||
return todos.filter((todo) => {
|
||||
return !todo.completed
|
||||
})
|
||||
},
|
||||
completed(todos) {
|
||||
return todos.filter(function (todo) {
|
||||
return todo.completed
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Vue.createApp({
|
||||
// app initial state
|
||||
data: () => ({
|
||||
todos: todoStorage.fetch(),
|
||||
newTodo: '',
|
||||
editedTodo: null,
|
||||
visibility: 'all'
|
||||
}),
|
||||
|
||||
// watch todos change for localStorage persistence
|
||||
watch: {
|
||||
todos: {
|
||||
handler(todos) {
|
||||
todoStorage.save(todos)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
window.addEventListener('hashchange', this.onHashChange)
|
||||
this.onHashChange()
|
||||
},
|
||||
|
||||
computed: {
|
||||
filteredTodos() {
|
||||
return filters[this.visibility](this.todos)
|
||||
},
|
||||
remaining() {
|
||||
return filters.active(this.todos).length
|
||||
},
|
||||
allDone: {
|
||||
get() {
|
||||
return this.remaining === 0
|
||||
},
|
||||
set(value) {
|
||||
this.todos.forEach(function (todo) {
|
||||
todo.completed = value
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// methods that implement data logic.
|
||||
// note there's no DOM manipulation here at all.
|
||||
methods: {
|
||||
addTodo() {
|
||||
var value = this.newTodo && this.newTodo.trim()
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
this.todos.push({
|
||||
id: todoStorage.uid++,
|
||||
title: value,
|
||||
completed: false
|
||||
const STORAGE_KEY = 'todos-vuejs-3.x'
|
||||
const todoStorage = {
|
||||
fetch() {
|
||||
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
|
||||
todos.forEach((todo, index) => {
|
||||
todo.id = index
|
||||
})
|
||||
this.newTodo = ''
|
||||
todoStorage.uid = todos.length
|
||||
return todos
|
||||
},
|
||||
|
||||
removeTodo(todo) {
|
||||
this.todos.splice(this.todos.indexOf(todo), 1)
|
||||
save(todos) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
|
||||
},
|
||||
|
||||
editTodo(todo) {
|
||||
this.beforeEditCache = todo.title
|
||||
this.editedTodo = todo
|
||||
},
|
||||
|
||||
doneEdit(todo) {
|
||||
if (!this.editedTodo) {
|
||||
return
|
||||
}
|
||||
this.editedTodo = null
|
||||
todo.title = todo.title.trim()
|
||||
if (!todo.title) {
|
||||
this.removeTodo(todo)
|
||||
}
|
||||
},
|
||||
|
||||
cancelEdit(todo) {
|
||||
this.editedTodo = null
|
||||
todo.title = this.beforeEditCache
|
||||
},
|
||||
|
||||
removeCompleted() {
|
||||
this.todos = filters.active(this.todos)
|
||||
},
|
||||
|
||||
onHashChange() {
|
||||
var visibility = window.location.hash.replace(/#\/?/, '')
|
||||
if (filters[visibility]) {
|
||||
this.visibility = visibility
|
||||
} else {
|
||||
window.location.hash = ''
|
||||
this.visibility = 'all'
|
||||
}
|
||||
},
|
||||
|
||||
pluralize (n) {
|
||||
return n === 1 ? 'item' : 'items'
|
||||
}
|
||||
},
|
||||
|
||||
directives: {
|
||||
'todo-focus'(el, binding) {
|
||||
if (binding.value) {
|
||||
el.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}).mount('#app')
|
||||
|
||||
const filters = {
|
||||
all(todos) {
|
||||
return todos
|
||||
},
|
||||
active(todos) {
|
||||
return todos.filter(todo => {
|
||||
return !todo.completed
|
||||
})
|
||||
},
|
||||
completed(todos) {
|
||||
return todos.filter(function (todo) {
|
||||
return todo.completed
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
Vue.createApp({
|
||||
// app initial state
|
||||
data: () => ({
|
||||
todos: todoStorage.fetch(),
|
||||
newTodo: '',
|
||||
editedTodo: null,
|
||||
visibility: 'all',
|
||||
}),
|
||||
|
||||
// watch todos change for localStorage persistence
|
||||
watch: {
|
||||
todos: {
|
||||
handler(todos) {
|
||||
todoStorage.save(todos)
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
window.addEventListener('hashchange', this.onHashChange)
|
||||
this.onHashChange()
|
||||
},
|
||||
|
||||
computed: {
|
||||
filteredTodos() {
|
||||
return filters[this.visibility](this.todos)
|
||||
},
|
||||
remaining() {
|
||||
return filters.active(this.todos).length
|
||||
},
|
||||
allDone: {
|
||||
get() {
|
||||
return this.remaining === 0
|
||||
},
|
||||
set(value) {
|
||||
this.todos.forEach(function (todo) {
|
||||
todo.completed = value
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// methods that implement data logic.
|
||||
// note there's no DOM manipulation here at all.
|
||||
methods: {
|
||||
addTodo() {
|
||||
var value = this.newTodo && this.newTodo.trim()
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
this.todos.push({
|
||||
id: todoStorage.uid++,
|
||||
title: value,
|
||||
completed: false,
|
||||
})
|
||||
this.newTodo = ''
|
||||
},
|
||||
|
||||
removeTodo(todo) {
|
||||
this.todos.splice(this.todos.indexOf(todo), 1)
|
||||
},
|
||||
|
||||
editTodo(todo) {
|
||||
this.beforeEditCache = todo.title
|
||||
this.editedTodo = todo
|
||||
},
|
||||
|
||||
doneEdit(todo) {
|
||||
if (!this.editedTodo) {
|
||||
return
|
||||
}
|
||||
this.editedTodo = null
|
||||
todo.title = todo.title.trim()
|
||||
if (!todo.title) {
|
||||
this.removeTodo(todo)
|
||||
}
|
||||
},
|
||||
|
||||
cancelEdit(todo) {
|
||||
this.editedTodo = null
|
||||
todo.title = this.beforeEditCache
|
||||
},
|
||||
|
||||
removeCompleted() {
|
||||
this.todos = filters.active(this.todos)
|
||||
},
|
||||
|
||||
onHashChange() {
|
||||
var visibility = window.location.hash.replace(/#\/?/, '')
|
||||
if (filters[visibility]) {
|
||||
this.visibility = visibility
|
||||
} else {
|
||||
window.location.hash = ''
|
||||
this.visibility = 'all'
|
||||
}
|
||||
},
|
||||
|
||||
pluralize(n) {
|
||||
return n === 1 ? 'item' : 'items'
|
||||
},
|
||||
},
|
||||
|
||||
directives: {
|
||||
'todo-focus'(el, binding) {
|
||||
if (binding.value) {
|
||||
el.focus()
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount('#app')
|
||||
</script>
|
||||
|
@ -22,43 +22,42 @@
|
||||
</script>
|
||||
<!-- item script -->
|
||||
<script>
|
||||
const TreeItem = {
|
||||
name: 'TreeItem', // necessary for self-reference
|
||||
template: '#item-template',
|
||||
props: {
|
||||
model: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
open: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isFolder() {
|
||||
return this.model.children &&
|
||||
this.model.children.length
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.isFolder) {
|
||||
this.open = !this.open
|
||||
const TreeItem = {
|
||||
name: 'TreeItem', // necessary for self-reference
|
||||
template: '#item-template',
|
||||
props: {
|
||||
model: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
open: false,
|
||||
}
|
||||
},
|
||||
changeType() {
|
||||
if (!this.isFolder) {
|
||||
this.model.children = []
|
||||
this.addChild()
|
||||
this.open = true
|
||||
}
|
||||
computed: {
|
||||
isFolder() {
|
||||
return this.model.children && this.model.children.length
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
if (this.isFolder) {
|
||||
this.open = !this.open
|
||||
}
|
||||
},
|
||||
changeType() {
|
||||
if (!this.isFolder) {
|
||||
this.model.children = []
|
||||
this.addChild()
|
||||
this.open = true
|
||||
}
|
||||
},
|
||||
addChild() {
|
||||
this.model.children.push({
|
||||
name: 'new stuff',
|
||||
})
|
||||
},
|
||||
},
|
||||
addChild() {
|
||||
this.model.children.push({
|
||||
name: 'new stuff'
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>(You can double click on an item to turn it into a folder.)</p>
|
||||
@ -69,43 +68,37 @@ const TreeItem = {
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
const treeData = {
|
||||
name: 'My Tree',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' }
|
||||
]
|
||||
},
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
const treeData = {
|
||||
name: 'My Tree',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [{ name: 'hello' }, { name: 'wat' }],
|
||||
},
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [{ name: 'hello' }, { name: 'wat' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Vue.createApp({
|
||||
components: {
|
||||
TreeItem
|
||||
},
|
||||
data: () => ({
|
||||
treeData
|
||||
})
|
||||
}).mount('#demo')
|
||||
Vue.createApp({
|
||||
components: {
|
||||
TreeItem,
|
||||
},
|
||||
data: () => ({
|
||||
treeData,
|
||||
}),
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -3,58 +3,67 @@
|
||||
<div id="demo">
|
||||
<h1>Latest Vue.js Commits</h1>
|
||||
<template v-for="branch in branches">
|
||||
<input type="radio"
|
||||
<input
|
||||
type="radio"
|
||||
:id="branch"
|
||||
:value="branch"
|
||||
name="branch"
|
||||
v-model="currentBranch">
|
||||
v-model="currentBranch"
|
||||
/>
|
||||
<label :for="branch">{{ branch }}</label>
|
||||
</template>
|
||||
<p>vuejs/core@{{ currentBranch }}</p>
|
||||
<ul>
|
||||
<li v-for="{ html_url, sha, author, commit } in commits">
|
||||
<a :href="html_url" target="_blank" class="commit">{{ sha.slice(0, 7) }}</a>
|
||||
- <span class="message">{{ truncate(commit.message) }}</span><br>
|
||||
by <span class="author"><a :href="author.html_url" target="_blank">{{ commit.author.name }}</a></span>
|
||||
<a :href="html_url" target="_blank" class="commit"
|
||||
>{{ sha.slice(0, 7) }}</a
|
||||
>
|
||||
- <span class="message">{{ truncate(commit.message) }}</span><br />
|
||||
by
|
||||
<span class="author"
|
||||
><a :href="author.html_url" target="_blank"
|
||||
>{{ commit.author.name }}</a
|
||||
></span
|
||||
>
|
||||
at <span class="date">{{ formatDate(commit.author.date) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp, ref, watchEffect } = Vue
|
||||
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
|
||||
const { createApp, ref, watchEffect } = Vue
|
||||
const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=`
|
||||
|
||||
const truncate = v => {
|
||||
const newline = v.indexOf('\n')
|
||||
return newline > 0 ? v.slice(0, newline) : v
|
||||
}
|
||||
|
||||
const formatDate = v => v.replace(/T|Z/g, ' ')
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
const currentBranch = ref('main')
|
||||
const commits = ref(null)
|
||||
|
||||
watchEffect(() => {
|
||||
fetch(`${API_URL}${currentBranch.value}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
commits.value = data
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
branches: ['main', 'v2-compat'],
|
||||
currentBranch,
|
||||
commits,
|
||||
truncate,
|
||||
formatDate
|
||||
}
|
||||
const truncate = v => {
|
||||
const newline = v.indexOf('\n')
|
||||
return newline > 0 ? v.slice(0, newline) : v
|
||||
}
|
||||
}).mount('#demo')
|
||||
|
||||
const formatDate = v => v.replace(/T|Z/g, ' ')
|
||||
|
||||
createApp({
|
||||
setup() {
|
||||
const currentBranch = ref('main')
|
||||
const commits = ref(null)
|
||||
|
||||
watchEffect(() => {
|
||||
fetch(`${API_URL}${currentBranch.value}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
console.log(data)
|
||||
commits.value = data
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
branches: ['main', 'v2-compat'],
|
||||
currentBranch,
|
||||
commits,
|
||||
truncate,
|
||||
formatDate,
|
||||
}
|
||||
},
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@ -69,7 +78,8 @@ createApp({
|
||||
line-height: 1.5em;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.author, .date {
|
||||
.author,
|
||||
.date {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
@ -26,148 +26,147 @@
|
||||
</script>
|
||||
<!-- DemoGrid component script -->
|
||||
<script>
|
||||
const { reactive, computed } = Vue
|
||||
const { reactive, computed } = Vue
|
||||
|
||||
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
|
||||
|
||||
const DemoGrid = {
|
||||
template: '#grid-template',
|
||||
props: {
|
||||
data: Array,
|
||||
columns: Array,
|
||||
filterKey: String
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
sortKey: '',
|
||||
sortOrders: props.columns.reduce((o, key) => (o[key] = 1, o), {})
|
||||
})
|
||||
const DemoGrid = {
|
||||
template: '#grid-template',
|
||||
props: {
|
||||
data: Array,
|
||||
columns: Array,
|
||||
filterKey: String,
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
sortKey: '',
|
||||
sortOrders: props.columns.reduce((o, key) => ((o[key] = 1), o), {}),
|
||||
})
|
||||
|
||||
const filteredData = computed(() => {
|
||||
let { data, filterKey } = props
|
||||
if (filterKey) {
|
||||
filterKey = filterKey.toLowerCase()
|
||||
data = data.filter(row => {
|
||||
return Object.keys(row).some(key => {
|
||||
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
|
||||
const filteredData = computed(() => {
|
||||
let { data, filterKey } = props
|
||||
if (filterKey) {
|
||||
filterKey = filterKey.toLowerCase()
|
||||
data = data.filter(row => {
|
||||
return Object.keys(row).some(key => {
|
||||
return String(row[key]).toLowerCase().indexOf(filterKey) > -1
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
const { sortKey } = state
|
||||
if (sortKey) {
|
||||
const order = state.sortOrders[sortKey]
|
||||
data = data.slice().sort((a, b) => {
|
||||
a = a[sortKey]
|
||||
b = b[sortKey]
|
||||
return (a === b ? 0 : a > b ? 1 : -1) * order
|
||||
})
|
||||
}
|
||||
return data
|
||||
})
|
||||
}
|
||||
const { sortKey } = state
|
||||
if (sortKey) {
|
||||
const order = state.sortOrders[sortKey]
|
||||
data = data.slice().sort((a, b) => {
|
||||
a = a[sortKey]
|
||||
b = b[sortKey]
|
||||
return (a === b ? 0 : a > b ? 1 : -1) * order
|
||||
})
|
||||
}
|
||||
return data
|
||||
})
|
||||
|
||||
function sortBy(key) {
|
||||
state.sortKey = key
|
||||
state.sortOrders[key] *= -1
|
||||
}
|
||||
function sortBy(key) {
|
||||
state.sortKey = key
|
||||
state.sortOrders[key] *= -1
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
filteredData,
|
||||
sortBy,
|
||||
capitalize
|
||||
}
|
||||
return {
|
||||
state,
|
||||
filteredData,
|
||||
sortBy,
|
||||
capitalize,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- App template (in DOM) -->
|
||||
<div id="demo">
|
||||
<form id="search">
|
||||
Search <input name="query" v-model="searchQuery">
|
||||
</form>
|
||||
<demo-grid
|
||||
:data="gridData"
|
||||
:columns="gridColumns"
|
||||
:filter-key="searchQuery">
|
||||
<form id="search">Search <input name="query" v-model="searchQuery" /></form>
|
||||
<demo-grid :data="gridData" :columns="gridColumns" :filter-key="searchQuery">
|
||||
</demo-grid>
|
||||
</div>
|
||||
<!-- App script -->
|
||||
<script>
|
||||
Vue.createApp({
|
||||
components: {
|
||||
DemoGrid
|
||||
},
|
||||
data: () => ({
|
||||
searchQuery: '',
|
||||
gridColumns: ['name', 'power'],
|
||||
gridData: [
|
||||
{ name: 'Chuck Norris', power: Infinity },
|
||||
{ name: 'Bruce Lee', power: 9000 },
|
||||
{ name: 'Jackie Chan', power: 7000 },
|
||||
{ name: 'Jet Li', power: 8000 }
|
||||
]
|
||||
})
|
||||
}).mount('#demo')
|
||||
Vue.createApp({
|
||||
components: {
|
||||
DemoGrid,
|
||||
},
|
||||
data: () => ({
|
||||
searchQuery: '',
|
||||
gridColumns: ['name', 'power'],
|
||||
gridData: [
|
||||
{ name: 'Chuck Norris', power: Infinity },
|
||||
{ name: 'Bruce Lee', power: 9000 },
|
||||
{ name: 'Jackie Chan', power: 7000 },
|
||||
{ name: 'Jet Li', power: 8000 },
|
||||
],
|
||||
}),
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-size: 14px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
table {
|
||||
border: 2px solid #42b983;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
table {
|
||||
border: 2px solid #42b983;
|
||||
border-radius: 3px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #42b983;
|
||||
color: rgba(255,255,255,0.66);
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
th {
|
||||
background-color: #42b983;
|
||||
color: rgba(255, 255, 255, 0.66);
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
th, td {
|
||||
min-width: 120px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
min-width: 120px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
th.active {
|
||||
color: #fff;
|
||||
}
|
||||
th.active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
th.active .arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
th.active .arrow {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 5px;
|
||||
opacity: 0.66;
|
||||
}
|
||||
|
||||
.arrow.asc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid #fff;
|
||||
}
|
||||
.arrow.asc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid #fff;
|
||||
}
|
||||
|
||||
.arrow.dsc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #fff;
|
||||
}
|
||||
.arrow.dsc {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid #fff;
|
||||
}
|
||||
</style>
|
||||
|
@ -8,55 +8,62 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { ref, computed } = Vue
|
||||
const { ref, computed } = Vue
|
||||
|
||||
Vue.createApp({
|
||||
setup() {
|
||||
const input = ref('# hello')
|
||||
const output = computed(() => marked.marked(input.value, { sanitize: true }))
|
||||
const update = _.debounce(e => { input.value = e.target.value }, 50)
|
||||
Vue.createApp({
|
||||
setup() {
|
||||
const input = ref('# hello')
|
||||
const output = computed(() =>
|
||||
marked.marked(input.value, { sanitize: true }),
|
||||
)
|
||||
const update = _.debounce(e => {
|
||||
input.value = e.target.value
|
||||
}, 50)
|
||||
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
update
|
||||
}
|
||||
}
|
||||
}).mount('#editor')
|
||||
return {
|
||||
input,
|
||||
output,
|
||||
update,
|
||||
}
|
||||
},
|
||||
}).mount('#editor')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html, body, #editor {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
#editor {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
textarea, #editor div {
|
||||
display: inline-block;
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
}
|
||||
textarea,
|
||||
#editor div {
|
||||
display: inline-block;
|
||||
overflow: auto;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border: none;
|
||||
border-right: 1px solid #ccc;
|
||||
resize: none;
|
||||
outline: none;
|
||||
background-color: #f6f6f6;
|
||||
font-size: 14px;
|
||||
font-family: 'Monaco', courier, monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
textarea {
|
||||
border: none;
|
||||
border-right: 1px solid #ccc;
|
||||
resize: none;
|
||||
outline: none;
|
||||
background-color: #f6f6f6;
|
||||
font-size: 14px;
|
||||
font-family: 'Monaco', courier, monospace;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #f66;
|
||||
}
|
||||
code {
|
||||
color: #f66;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,39 +1,37 @@
|
||||
<script src="../../dist/vue.global.js"></script>
|
||||
<script>
|
||||
const { ref, reactive, computed, createApp } = Vue
|
||||
const { ref, reactive, computed, createApp } = Vue
|
||||
|
||||
// math helper...
|
||||
function valueToPoint (value, index, total) {
|
||||
var x = 0
|
||||
var y = -value * 0.8
|
||||
var angle = Math.PI * 2 / total * index
|
||||
var cos = Math.cos(angle)
|
||||
var sin = Math.sin(angle)
|
||||
var tx = x * cos - y * sin + 100
|
||||
var ty = x * sin + y * cos + 100
|
||||
return {
|
||||
x: tx,
|
||||
y: ty
|
||||
}
|
||||
}
|
||||
|
||||
const AxisLabel = {
|
||||
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
|
||||
props: {
|
||||
stat: Object,
|
||||
index: Number,
|
||||
total: Number
|
||||
},
|
||||
setup(props) {
|
||||
// math helper...
|
||||
function valueToPoint(value, index, total) {
|
||||
var x = 0
|
||||
var y = -value * 0.8
|
||||
var angle = ((Math.PI * 2) / total) * index
|
||||
var cos = Math.cos(angle)
|
||||
var sin = Math.sin(angle)
|
||||
var tx = x * cos - y * sin + 100
|
||||
var ty = x * sin + y * cos + 100
|
||||
return {
|
||||
point: computed(() => valueToPoint(
|
||||
+props.stat.value + 10,
|
||||
props.index,
|
||||
props.total
|
||||
))
|
||||
x: tx,
|
||||
y: ty,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const AxisLabel = {
|
||||
template: '<text :x="point.x" :y="point.y">{{stat.label}}</text>',
|
||||
props: {
|
||||
stat: Object,
|
||||
index: Number,
|
||||
total: Number,
|
||||
},
|
||||
setup(props) {
|
||||
return {
|
||||
point: computed(() =>
|
||||
valueToPoint(+props.stat.value + 10, props.index, props.total),
|
||||
),
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- template for the polygraph component. -->
|
||||
@ -51,24 +49,26 @@ const AxisLabel = {
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const Polygraph = {
|
||||
props: ['stats'],
|
||||
template: '#polygraph-template',
|
||||
setup(props) {
|
||||
return {
|
||||
points: computed(() => {
|
||||
const total = props.stats.length
|
||||
return props.stats.map((stat, i) => {
|
||||
const point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
}).join(' ')
|
||||
})
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AxisLabel
|
||||
const Polygraph = {
|
||||
props: ['stats'],
|
||||
template: '#polygraph-template',
|
||||
setup(props) {
|
||||
return {
|
||||
points: computed(() => {
|
||||
const total = props.stats.length
|
||||
return props.stats
|
||||
.map((stat, i) => {
|
||||
const point = valueToPoint(stat.value, i, total)
|
||||
return point.x + ',' + point.y
|
||||
})
|
||||
.join(' ')
|
||||
}),
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AxisLabel,
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- demo root element -->
|
||||
@ -80,93 +80,99 @@ const Polygraph = {
|
||||
<!-- controls -->
|
||||
<div v-for="stat in stats">
|
||||
<label>{{stat.label}}</label>
|
||||
<input type="range" v-model="stat.value" min="0" max="100">
|
||||
<input type="range" v-model="stat.value" min="0" max="100" />
|
||||
<span>{{stat.value}}</span>
|
||||
<button @click="remove(stat)" class="remove">X</button>
|
||||
</div>
|
||||
<form id="add">
|
||||
<input name="newlabel" v-model="newLabel">
|
||||
<input name="newlabel" v-model="newLabel" />
|
||||
<button @click="add">Add a Stat</button>
|
||||
</form>
|
||||
<pre id="raw">{{ stats }}</pre>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const globalStats = [
|
||||
{ label: 'A', value: 100 },
|
||||
{ label: 'B', value: 100 },
|
||||
{ label: 'C', value: 100 },
|
||||
{ label: 'D', value: 100 },
|
||||
{ label: 'E', value: 100 },
|
||||
{ label: 'F', value: 100 }
|
||||
]
|
||||
const globalStats = [
|
||||
{ label: 'A', value: 100 },
|
||||
{ label: 'B', value: 100 },
|
||||
{ label: 'C', value: 100 },
|
||||
{ label: 'D', value: 100 },
|
||||
{ label: 'E', value: 100 },
|
||||
{ label: 'F', value: 100 },
|
||||
]
|
||||
|
||||
createApp({
|
||||
components: {
|
||||
Polygraph
|
||||
},
|
||||
setup() {
|
||||
const newLabel = ref('')
|
||||
const stats = reactive(globalStats)
|
||||
createApp({
|
||||
components: {
|
||||
Polygraph,
|
||||
},
|
||||
setup() {
|
||||
const newLabel = ref('')
|
||||
const stats = reactive(globalStats)
|
||||
|
||||
function add(e) {
|
||||
e.preventDefault()
|
||||
if (!newLabel.value) return
|
||||
stats.push({
|
||||
label: newLabel.value,
|
||||
value: 100
|
||||
})
|
||||
newLabel.value = ''
|
||||
}
|
||||
|
||||
function remove(stat) {
|
||||
if (stats.length > 3) {
|
||||
stats.splice(stats.indexOf(stat), 1)
|
||||
} else {
|
||||
alert('Can\'t delete more!')
|
||||
function add(e) {
|
||||
e.preventDefault()
|
||||
if (!newLabel.value) return
|
||||
stats.push({
|
||||
label: newLabel.value,
|
||||
value: 100,
|
||||
})
|
||||
newLabel.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
newLabel,
|
||||
stats,
|
||||
add,
|
||||
remove
|
||||
}
|
||||
}
|
||||
}).mount('#demo')
|
||||
function remove(stat) {
|
||||
if (stats.length > 3) {
|
||||
stats.splice(stats.indexOf(stat), 1)
|
||||
} else {
|
||||
alert("Can't delete more!")
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
newLabel,
|
||||
stats,
|
||||
add,
|
||||
remove,
|
||||
}
|
||||
},
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
}
|
||||
body {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
polygon {
|
||||
polygon {
|
||||
fill: #42b983;
|
||||
opacity: .75;
|
||||
}
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
circle {
|
||||
circle {
|
||||
fill: transparent;
|
||||
stroke: #999;
|
||||
}
|
||||
}
|
||||
|
||||
text {
|
||||
font-family: Helvetica Neue, Arial, sans-serif;
|
||||
text {
|
||||
font-family:
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-size: 10px;
|
||||
fill: #666;
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
#raw {
|
||||
#raw {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,51 +1,86 @@
|
||||
<script src="../../dist/vue.global.js"></script>
|
||||
<link rel="stylesheet" href="../../../../node_modules/todomvc-app-css/index.css">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../../../../node_modules/todomvc-app-css/index.css"
|
||||
/>
|
||||
|
||||
<div id="app">
|
||||
<section class="todoapp">
|
||||
<header class="header">
|
||||
<h1>todos</h1>
|
||||
<input class="new-todo"
|
||||
autofocus autocomplete="off"
|
||||
placeholder="What needs to be done?"
|
||||
v-model="state.newTodo"
|
||||
@keyup.enter="addTodo">
|
||||
<input
|
||||
class="new-todo"
|
||||
autofocus
|
||||
autocomplete="off"
|
||||
placeholder="What needs to be done?"
|
||||
v-model="state.newTodo"
|
||||
@keyup.enter="addTodo"
|
||||
/>
|
||||
</header>
|
||||
<section class="main" v-show="state.todos.length">
|
||||
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="state.allDone">
|
||||
<input
|
||||
id="toggle-all"
|
||||
class="toggle-all"
|
||||
type="checkbox"
|
||||
v-model="state.allDone"
|
||||
/>
|
||||
<label for="toggle-all">Mark all as complete</label>
|
||||
<ul class="todo-list">
|
||||
<li v-for="todo in state.filteredTodos"
|
||||
class="todo"
|
||||
:key="todo.id"
|
||||
:class="{ completed: todo.completed, editing: todo === state.editedTodo }">
|
||||
<li
|
||||
v-for="todo in state.filteredTodos"
|
||||
class="todo"
|
||||
:key="todo.id"
|
||||
:class="{ completed: todo.completed, editing: todo === state.editedTodo }"
|
||||
>
|
||||
<div class="view">
|
||||
<input class="toggle" type="checkbox" v-model="todo.completed">
|
||||
<input class="toggle" type="checkbox" v-model="todo.completed" />
|
||||
<label @dblclick="editTodo(todo)">{{ todo.title }}</label>
|
||||
<button class="destroy" @click="removeTodo(todo)"></button>
|
||||
</div>
|
||||
<input class="edit" type="text"
|
||||
v-model="todo.title"
|
||||
v-todo-focus="todo === state.editedTodo"
|
||||
@blur="doneEdit(todo)"
|
||||
@keyup.enter="doneEdit(todo)"
|
||||
@keyup.escape="cancelEdit(todo)"
|
||||
>
|
||||
<input
|
||||
class="edit"
|
||||
type="text"
|
||||
v-model="todo.title"
|
||||
v-todo-focus="todo === state.editedTodo"
|
||||
@blur="doneEdit(todo)"
|
||||
@keyup.enter="doneEdit(todo)"
|
||||
@keyup.escape="cancelEdit(todo)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer class="footer" v-show="state.todos.length">
|
||||
<span class="todo-count">
|
||||
<strong>{{ state.remaining }}</strong>
|
||||
<span>{{ state.remainingText }}</span>
|
||||
</span>
|
||||
<span class="todo-count">
|
||||
<strong>{{ state.remaining }}</strong>
|
||||
<span>{{ state.remainingText }}</span>
|
||||
</span>
|
||||
<ul class="filters">
|
||||
<li><a href="#/all" :class="{ selected: state.visibility === 'all' }">All</a></li>
|
||||
<li><a href="#/active" :class="{ selected: state.visibility === 'active' }">Active</a></li>
|
||||
<li><a href="#/completed" :class="{ selected: state.visibility === 'completed' }">Completed</a></li>
|
||||
<li>
|
||||
<a href="#/all" :class="{ selected: state.visibility === 'all' }"
|
||||
>All</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#/active"
|
||||
:class="{ selected: state.visibility === 'active' }"
|
||||
>Active</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="#/completed"
|
||||
:class="{ selected: state.visibility === 'completed' }"
|
||||
>Completed</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<button class="clear-completed" @click="removeCompleted" v-show="state.todos.length > state.remaining">
|
||||
<button
|
||||
class="clear-completed"
|
||||
@click="removeCompleted"
|
||||
v-show="state.todos.length > state.remaining"
|
||||
>
|
||||
Clear completed
|
||||
</button>
|
||||
</footer>
|
||||
@ -53,154 +88,155 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp, reactive, computed, watchEffect, onMounted, onUnmounted } = Vue
|
||||
const { createApp, reactive, computed, watchEffect, onMounted, onUnmounted } =
|
||||
Vue
|
||||
|
||||
const STORAGE_KEY = 'todos-vuejs-3.x'
|
||||
const todoStorage = {
|
||||
fetch () {
|
||||
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
|
||||
todos.forEach((todo, index) => {
|
||||
todo.id = index
|
||||
})
|
||||
todoStorage.uid = todos.length
|
||||
return todos
|
||||
},
|
||||
save (todos) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
|
||||
const STORAGE_KEY = 'todos-vuejs-3.x'
|
||||
const todoStorage = {
|
||||
fetch() {
|
||||
const todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]')
|
||||
todos.forEach((todo, index) => {
|
||||
todo.id = index
|
||||
})
|
||||
todoStorage.uid = todos.length
|
||||
return todos
|
||||
},
|
||||
save(todos) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const filters = {
|
||||
all (todos) {
|
||||
return todos
|
||||
},
|
||||
active (todos) {
|
||||
return todos.filter((todo) => {
|
||||
return !todo.completed
|
||||
})
|
||||
},
|
||||
completed (todos) {
|
||||
return todos.filter(function (todo) {
|
||||
return todo.completed
|
||||
})
|
||||
const filters = {
|
||||
all(todos) {
|
||||
return todos
|
||||
},
|
||||
active(todos) {
|
||||
return todos.filter(todo => {
|
||||
return !todo.completed
|
||||
})
|
||||
},
|
||||
completed(todos) {
|
||||
return todos.filter(function (todo) {
|
||||
return todo.completed
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function pluralize (n) {
|
||||
return n === 1 ? 'item' : 'items'
|
||||
}
|
||||
function pluralize(n) {
|
||||
return n === 1 ? 'item' : 'items'
|
||||
}
|
||||
|
||||
createApp({
|
||||
setup () {
|
||||
const state = reactive({
|
||||
todos: todoStorage.fetch(),
|
||||
editedTodo: null,
|
||||
newTodo: '',
|
||||
beforeEditCache: '',
|
||||
visibility: 'all',
|
||||
remaining: computed(() => {
|
||||
return filters.active(state.todos).length
|
||||
}),
|
||||
remainingText: computed(() => {
|
||||
return ` ${pluralize(state.remaining)} left`
|
||||
}),
|
||||
filteredTodos: computed(() => {
|
||||
return filters[state.visibility](state.todos)
|
||||
}),
|
||||
allDone: computed({
|
||||
get: function () {
|
||||
return state.remaining === 0
|
||||
},
|
||||
set: function (value) {
|
||||
state.todos.forEach((todo) => {
|
||||
todo.completed = value
|
||||
})
|
||||
createApp({
|
||||
setup() {
|
||||
const state = reactive({
|
||||
todos: todoStorage.fetch(),
|
||||
editedTodo: null,
|
||||
newTodo: '',
|
||||
beforeEditCache: '',
|
||||
visibility: 'all',
|
||||
remaining: computed(() => {
|
||||
return filters.active(state.todos).length
|
||||
}),
|
||||
remainingText: computed(() => {
|
||||
return ` ${pluralize(state.remaining)} left`
|
||||
}),
|
||||
filteredTodos: computed(() => {
|
||||
return filters[state.visibility](state.todos)
|
||||
}),
|
||||
allDone: computed({
|
||||
get: function () {
|
||||
return state.remaining === 0
|
||||
},
|
||||
set: function (value) {
|
||||
state.todos.forEach(todo => {
|
||||
todo.completed = value
|
||||
})
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
todoStorage.save(state.todos)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('hashchange', onHashChange)
|
||||
onHashChange()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('hashchange', onHashChange)
|
||||
})
|
||||
|
||||
function onHashChange() {
|
||||
const visibility = window.location.hash.replace(/#\/?/, '')
|
||||
if (filters[visibility]) {
|
||||
state.visibility = visibility
|
||||
} else {
|
||||
window.location.hash = ''
|
||||
state.visibility = 'all'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
todoStorage.save(state.todos)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('hashchange', onHashChange)
|
||||
onHashChange()
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('hashchange', onHashChange)
|
||||
})
|
||||
|
||||
function onHashChange () {
|
||||
const visibility = window.location.hash.replace(/#\/?/, '')
|
||||
if (filters[visibility]) {
|
||||
state.visibility = visibility
|
||||
} else {
|
||||
window.location.hash = ''
|
||||
state.visibility = 'all'
|
||||
}
|
||||
}
|
||||
|
||||
function addTodo () {
|
||||
const value = state.newTodo && state.newTodo.trim()
|
||||
if (!value) {
|
||||
return
|
||||
function addTodo() {
|
||||
const value = state.newTodo && state.newTodo.trim()
|
||||
if (!value) {
|
||||
return
|
||||
}
|
||||
state.todos.push({
|
||||
id: todoStorage.uid++,
|
||||
title: value,
|
||||
completed: false,
|
||||
})
|
||||
state.newTodo = ''
|
||||
}
|
||||
state.todos.push({
|
||||
id: todoStorage.uid++,
|
||||
title: value,
|
||||
completed: false
|
||||
})
|
||||
state.newTodo = ''
|
||||
}
|
||||
|
||||
function removeTodo (todo) {
|
||||
state.todos.splice(state.todos.indexOf(todo), 1)
|
||||
}
|
||||
|
||||
function editTodo (todo) {
|
||||
state.beforeEditCache = todo.title
|
||||
state.editedTodo = todo
|
||||
}
|
||||
|
||||
function doneEdit (todo) {
|
||||
if (!state.editedTodo) {
|
||||
return
|
||||
function removeTodo(todo) {
|
||||
state.todos.splice(state.todos.indexOf(todo), 1)
|
||||
}
|
||||
state.editedTodo = null
|
||||
todo.title = todo.title.trim()
|
||||
if (!todo.title) {
|
||||
removeTodo(todo)
|
||||
|
||||
function editTodo(todo) {
|
||||
state.beforeEditCache = todo.title
|
||||
state.editedTodo = todo
|
||||
}
|
||||
}
|
||||
|
||||
function cancelEdit (todo) {
|
||||
state.editedTodo = null
|
||||
todo.title = state.beforeEditCache
|
||||
}
|
||||
|
||||
function removeCompleted () {
|
||||
state.todos = filters.active(state.todos)
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
addTodo,
|
||||
removeTodo,
|
||||
editTodo,
|
||||
doneEdit,
|
||||
cancelEdit,
|
||||
removeCompleted
|
||||
}
|
||||
},
|
||||
|
||||
directives: {
|
||||
'todo-focus': (el, { value }) => {
|
||||
if (value) {
|
||||
el.focus()
|
||||
function doneEdit(todo) {
|
||||
if (!state.editedTodo) {
|
||||
return
|
||||
}
|
||||
state.editedTodo = null
|
||||
todo.title = todo.title.trim()
|
||||
if (!todo.title) {
|
||||
removeTodo(todo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).mount('#app')
|
||||
|
||||
function cancelEdit(todo) {
|
||||
state.editedTodo = null
|
||||
todo.title = state.beforeEditCache
|
||||
}
|
||||
|
||||
function removeCompleted() {
|
||||
state.todos = filters.active(state.todos)
|
||||
}
|
||||
|
||||
return {
|
||||
state,
|
||||
addTodo,
|
||||
removeTodo,
|
||||
editTodo,
|
||||
doneEdit,
|
||||
cancelEdit,
|
||||
removeCompleted,
|
||||
}
|
||||
},
|
||||
|
||||
directives: {
|
||||
'todo-focus': (el, { value }) => {
|
||||
if (value) {
|
||||
el.focus()
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount('#app')
|
||||
</script>
|
||||
|
@ -22,46 +22,46 @@
|
||||
</script>
|
||||
<!-- item script -->
|
||||
<script>
|
||||
const { reactive, computed, toRefs } = Vue
|
||||
const { reactive, computed, toRefs } = Vue
|
||||
|
||||
const TreeItem = {
|
||||
name: 'TreeItem', // necessary for self-reference
|
||||
template: '#item-template',
|
||||
props: {
|
||||
model: Object
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
open: false,
|
||||
isFolder: computed(() => {
|
||||
return props.model.children && props.model.children.length
|
||||
const TreeItem = {
|
||||
name: 'TreeItem', // necessary for self-reference
|
||||
template: '#item-template',
|
||||
props: {
|
||||
model: Object,
|
||||
},
|
||||
setup(props) {
|
||||
const state = reactive({
|
||||
open: false,
|
||||
isFolder: computed(() => {
|
||||
return props.model.children && props.model.children.length
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
function toggle() {
|
||||
state.open = !state.open
|
||||
}
|
||||
|
||||
function changeType() {
|
||||
if (!state.isFolder) {
|
||||
props.model.children = []
|
||||
addChild()
|
||||
state.open = true
|
||||
function toggle() {
|
||||
state.open = !state.open
|
||||
}
|
||||
}
|
||||
|
||||
function addChild() {
|
||||
props.model.children.push({ name: 'new stuff' })
|
||||
}
|
||||
function changeType() {
|
||||
if (!state.isFolder) {
|
||||
props.model.children = []
|
||||
addChild()
|
||||
state.open = true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
toggle,
|
||||
changeType,
|
||||
addChild
|
||||
}
|
||||
function addChild() {
|
||||
props.model.children.push({ name: 'new stuff' })
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
toggle,
|
||||
changeType,
|
||||
addChild,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>(You can double click on an item to turn it into a folder.)</p>
|
||||
@ -72,43 +72,37 @@ const TreeItem = {
|
||||
</ul>
|
||||
|
||||
<script>
|
||||
const treeData = {
|
||||
name: 'My Tree',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' }
|
||||
]
|
||||
},
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
const treeData = {
|
||||
name: 'My Tree',
|
||||
children: [
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [{ name: 'hello' }, { name: 'wat' }],
|
||||
},
|
||||
{ name: 'hello' },
|
||||
{ name: 'wat' },
|
||||
{
|
||||
name: 'child folder',
|
||||
children: [{ name: 'hello' }, { name: 'wat' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Vue.createApp({
|
||||
components: {
|
||||
TreeItem
|
||||
},
|
||||
data: () => ({
|
||||
treeData
|
||||
})
|
||||
}).mount('#demo')
|
||||
Vue.createApp({
|
||||
components: {
|
||||
TreeItem,
|
||||
},
|
||||
data: () => ({
|
||||
treeData,
|
||||
}),
|
||||
}).mount('#demo')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -6,78 +6,83 @@
|
||||
<button @click="reset">reset</button>
|
||||
<button @click="shuffle">shuffle</button>
|
||||
<transition-group tag="ul" name="fade" class="container">
|
||||
<item v-for="item in items"
|
||||
<item
|
||||
v-for="item in items"
|
||||
class="item"
|
||||
:msg="item"
|
||||
:key="item"
|
||||
@rm="remove(item)">
|
||||
@rm="remove(item)"
|
||||
>
|
||||
</item>
|
||||
</transition-group>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const getInitialItems = () => [1, 2, 3, 4, 5]
|
||||
let id = getInitialItems().length + 1
|
||||
const getInitialItems = () => [1, 2, 3, 4, 5]
|
||||
let id = getInitialItems().length + 1
|
||||
|
||||
const Item = {
|
||||
props: ['msg'],
|
||||
template: `<div>{{ msg }} <button @click="$emit('rm')">x</button></div>`
|
||||
}
|
||||
|
||||
Vue.createApp({
|
||||
components: {
|
||||
Item
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: getInitialItems()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
insert () {
|
||||
const i = Math.round(Math.random() * this.items.length)
|
||||
this.items.splice(i, 0, id++)
|
||||
},
|
||||
reset () {
|
||||
this.items = getInitialItems()
|
||||
},
|
||||
shuffle () {
|
||||
this.items = _.shuffle(this.items)
|
||||
},
|
||||
remove (item) {
|
||||
const i = this.items.indexOf(item)
|
||||
if (i > -1) {
|
||||
this.items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
const Item = {
|
||||
props: ['msg'],
|
||||
template: `<div>{{ msg }} <button @click="$emit('rm')">x</button></div>`,
|
||||
}
|
||||
}).mount('#app')
|
||||
|
||||
Vue.createApp({
|
||||
components: {
|
||||
Item,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: getInitialItems(),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
insert() {
|
||||
const i = Math.round(Math.random() * this.items.length)
|
||||
this.items.splice(i, 0, id++)
|
||||
},
|
||||
reset() {
|
||||
this.items = getInitialItems()
|
||||
},
|
||||
shuffle() {
|
||||
this.items = _.shuffle(this.items)
|
||||
},
|
||||
remove(item) {
|
||||
const i = this.items.indexOf(item)
|
||||
if (i > -1) {
|
||||
this.items.splice(i, 1)
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount('#app')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.container {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
.item {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background-color: #f3f3f3;
|
||||
border: 1px solid #666;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 1. declare transition */
|
||||
.fade-move, .fade-enter-active, .fade-leave-active {
|
||||
transition: all .5s cubic-bezier(.55,0,.1,1);
|
||||
}
|
||||
/* 2. declare enter from and leave to state */
|
||||
.fade-enter-from, .fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.01) translate(30px, 0);
|
||||
}
|
||||
/* 3. ensure leaving items are taken out of layout flow so that moving
|
||||
.container {
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
.item {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
background-color: #f3f3f3;
|
||||
border: 1px solid #666;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* 1. declare transition */
|
||||
.fade-move,
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1);
|
||||
}
|
||||
/* 2. declare enter from and leave to state */
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: scaleY(0.01) translate(30px, 0);
|
||||
}
|
||||
/* 3. ensure leaving items are taken out of layout flow so that moving
|
||||
animations can be calculated correctly. */
|
||||
.fade-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
.fade-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
|
@ -33,10 +33,10 @@
|
||||
</transition>
|
||||
</script>
|
||||
<script>
|
||||
const Modal = {
|
||||
template: '#modal-template',
|
||||
props: ['show']
|
||||
}
|
||||
const Modal = {
|
||||
template: '#modal-template',
|
||||
props: ['show'],
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- modal container that lives outside of app root -->
|
||||
@ -56,57 +56,57 @@ const Modal = {
|
||||
</div>
|
||||
|
||||
<script>
|
||||
Vue.createApp({
|
||||
components: { Modal },
|
||||
data: () => ({
|
||||
showModal: false
|
||||
})
|
||||
}).mount('#app')
|
||||
Vue.createApp({
|
||||
components: { Modal },
|
||||
data: () => ({
|
||||
showModal: false,
|
||||
}),
|
||||
}).mount('#app')
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
display: table;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
.modal-mask {
|
||||
position: fixed;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: table;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.modal-wrapper {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.modal-wrapper {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
width: 300px;
|
||||
margin: 0px auto;
|
||||
padding: 20px 30px;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, .33);
|
||||
transition: all .3s ease;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.modal-container {
|
||||
width: 300px;
|
||||
margin: 0px auto;
|
||||
padding: 20px 30px;
|
||||
background-color: #fff;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
|
||||
transition: all 0.3s ease;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.modal-header h3 {
|
||||
margin-top: 0;
|
||||
color: #42b983;
|
||||
}
|
||||
.modal-header h3 {
|
||||
margin-top: 0;
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.modal-body {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.modal-default-button {
|
||||
float: right;
|
||||
}
|
||||
.modal-default-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="modal" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
@ -115,17 +115,17 @@ Vue.createApp({
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.modal-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
.modal-enter-from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.modal-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-enter-from .modal-container,
|
||||
.modal-leave-to .modal-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.modal-enter-from .modal-container,
|
||||
.modal-leave-to .modal-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user