├── .editorconfig ├── .github ├── contributing │ ├── oxford-comma.jpg │ └── writing-guide.md ├── dependabot.yml ├── scripts │ └── tag-alert-blocks.js └── workflows │ ├── automerge.yml │ └── autosync.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── .vitepress ├── config.ts ├── headerMdPlugin.ts ├── inlined-scripts │ ├── perfops.js │ ├── restorePreference.js │ └── uwu.js ├── textAdMdPlugin.ts └── theme │ ├── components │ ├── Banner.vue │ ├── CallToActionSection.vue │ ├── CardList.vue │ ├── Home.vue │ ├── NewsLetter.vue │ ├── PageHero.vue │ ├── PageShowcaseListLayout.vue │ ├── PreferenceSwitch.vue │ ├── PreferenceTooltip.vue │ ├── ReplLoading.vue │ ├── ScrimbaLink.vue │ ├── SecurityUpdateBtn.vue │ ├── SiteMap.vue │ ├── SponsorsAside.vue │ ├── SponsorsGroup.vue │ ├── TextAd.vue │ ├── VueJobs.vue │ ├── VueMasteryModal.vue │ ├── VueSchoolLink.vue │ ├── preferences.ts │ └── sponsors.ts │ ├── index.ts │ └── styles │ ├── badges.css │ ├── index.css │ ├── inline-demo.css │ ├── options-boxes.css │ ├── overrides.css │ ├── pages.css │ ├── style-guide.css │ ├── utilities.css │ └── vue-mastery.css ├── .vscode └── settings.json ├── GLOSSARY.md ├── LICENSE ├── README.md ├── env.d.ts ├── netlify.toml ├── package.json ├── pnpm-lock.yaml ├── src ├── about │ ├── coc.md │ ├── community-guide.md │ ├── faq.md │ ├── images │ │ ├── ben-hong.jpeg │ │ └── evan-you.jpeg │ ├── privacy.md │ ├── releases.md │ ├── team.md │ └── team │ │ ├── Member.ts │ │ ├── TeamHero.vue │ │ ├── TeamList.vue │ │ ├── TeamMember.vue │ │ ├── TeamPage.vue │ │ ├── members-core.json │ │ ├── members-emeriti.json │ │ └── members-partner.json ├── api │ ├── ApiIndex.vue │ ├── api.data.ts │ ├── application.md │ ├── built-in-components.md │ ├── built-in-directives.md │ ├── built-in-special-attributes.md │ ├── built-in-special-elements.md │ ├── compile-time-flags.md │ ├── component-instance.md │ ├── composition-api-dependency-injection.md │ ├── composition-api-helpers.md │ ├── composition-api-lifecycle.md │ ├── composition-api-setup.md │ ├── custom-elements.md │ ├── custom-renderer.md │ ├── general.md │ ├── index.md │ ├── options-composition.md │ ├── options-lifecycle.md │ ├── options-misc.md │ ├── options-rendering.md │ ├── options-state.md │ ├── reactivity-advanced.md │ ├── reactivity-core.md │ ├── reactivity-utilities.md │ ├── render-function.md │ ├── sfc-css-features.md │ ├── sfc-script-setup.md │ ├── sfc-spec.md │ ├── ssr.md │ └── utility-types.md ├── developers │ ├── [developerSlug].md │ ├── [developerSlug].paths.ts │ ├── components │ │ ├── DeveloperCard.vue │ │ ├── DeveloperCompensations.vue │ │ ├── DeveloperEducation.vue │ │ ├── DeveloperExperiences.vue │ │ ├── DeveloperHero.vue │ │ ├── DeveloperImage.vue │ │ ├── DeveloperJoin.vue │ │ ├── DeveloperLanding.vue │ │ ├── DeveloperPage.vue │ │ ├── DeveloperPageFooter.vue │ │ ├── DeveloperProficiencies.vue │ │ ├── DeveloperProfileDiagram.vue │ │ ├── type.ts │ │ └── utils.ts │ ├── developers.json │ ├── index.md │ └── partnerConfig.js ├── ecosystem │ ├── newsletters.md │ ├── themes.md │ └── themes │ │ ├── ThemeContact.vue │ │ ├── ThemeHero.vue │ │ ├── ThemeList.vue │ │ ├── ThemeListItem.vue │ │ ├── ThemePage.vue │ │ ├── ThemeProduct.vue │ │ └── themes.json ├── error-reference │ ├── ErrorsTable.vue │ ├── errors.data.ts │ └── index.md ├── examples │ ├── ExampleRepl.vue │ ├── examples.data.ts │ ├── index.md │ ├── src │ │ ├── attribute-bindings │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── cells │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ ├── Cell │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ ├── description.txt │ │ │ └── store.js │ │ ├── circle-drawer │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── conditionals-and-loops │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── counter │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── crud │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── fetching-data │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── flight-booker │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── form-bindings │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── grid │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ ├── Grid │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── handling-input │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── hello-world │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── list-transition │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ ├── description.txt │ │ │ └── import-map.json │ │ ├── markdown │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ ├── description.txt │ │ │ └── import-map.json │ │ ├── modal │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ ├── Modal │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── simple-component │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ ├── TodoItem │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── svg │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ ├── AxisLabel │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ ├── PolyGraph │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ ├── description.txt │ │ │ └── util.js │ │ ├── temperature-converter │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ │ └── description.txt │ │ ├── timer │ │ │ ├── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ ├── style.css │ │ │ │ └── template.html │ │ │ └── description.txt │ │ └── tree │ │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ ├── style.css │ │ │ └── template.html │ │ │ ├── TreeItem │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ │ └── description.txt │ └── utils.ts ├── glossary │ └── index.md ├── guide │ ├── best-practices │ │ ├── accessibility.md │ │ ├── images │ │ │ ├── AccessibilityChromeDeveloperTools.png │ │ │ ├── AccessibleARIAdescribedby.png │ │ │ ├── AccessibleARIAlabelDevTools.png │ │ │ ├── AccessibleARIAlabelledbyDevTools.png │ │ │ ├── AccessibleLabelChromeDevTools.png │ │ │ └── AccessiblePlaceholder.png │ │ ├── performance.md │ │ ├── production-deployment.md │ │ └── security.md │ ├── built-ins │ │ ├── images │ │ │ └── transition-classes.png │ │ ├── keep-alive-demos │ │ │ ├── CompA.vue │ │ │ ├── CompB.vue │ │ │ └── SwitchComponent.vue │ │ ├── keep-alive.md │ │ ├── suspense.md │ │ ├── teleport.md │ │ ├── transition-demos │ │ │ ├── Basic.vue │ │ │ ├── BetweenComponents.vue │ │ │ ├── BetweenElements.vue │ │ │ ├── CssAnimation.vue │ │ │ ├── JsHooks.vue │ │ │ ├── ListBasic.vue │ │ │ ├── ListMove.vue │ │ │ ├── ListStagger.vue │ │ │ ├── NestedTransitions.vue │ │ │ └── SlideFade.vue │ │ ├── transition-group.md │ │ └── transition.md │ ├── components │ │ ├── async.md │ │ ├── attrs.md │ │ ├── events.md │ │ ├── images │ │ │ ├── named-slots.png │ │ │ ├── prop-drilling.png │ │ │ ├── provide-inject.png │ │ │ ├── scoped-slots.svg │ │ │ └── slots.png │ │ ├── props.md │ │ ├── provide-inject.md │ │ ├── registration.md │ │ ├── slots.md │ │ └── v-model.md │ ├── essentials │ │ ├── application.md │ │ ├── class-and-style.md │ │ ├── component-basics.md │ │ ├── computed.md │ │ ├── conditional.md │ │ ├── event-handling.md │ │ ├── forms.md │ │ ├── images │ │ │ ├── components.png │ │ │ ├── directive.png │ │ │ └── lifecycle.png │ │ ├── lifecycle.md │ │ ├── list.md │ │ ├── reactivity-fundamentals.md │ │ ├── template-refs.md │ │ ├── template-syntax.md │ │ └── watchers.md │ ├── extras │ │ ├── animation.md │ │ ├── composition-api-faq.md │ │ ├── demos │ │ │ ├── AnimateWatcher.vue │ │ │ ├── Colors.vue │ │ │ ├── DisabledButton.vue │ │ │ ├── ElasticHeader.vue │ │ │ ├── SpreadSheet.vue │ │ │ ├── SpreadSheetCell.vue │ │ │ └── spreadSheetStore.js │ │ ├── images │ │ │ ├── composition-api-after.png │ │ │ ├── options-api.png │ │ │ └── render-pipeline.png │ │ ├── reactivity-in-depth.md │ │ ├── reactivity-transform.md │ │ ├── render-function.md │ │ ├── rendering-mechanism.md │ │ ├── ways-of-using-vue.md │ │ └── web-components.md │ ├── introduction.md │ ├── quick-start.md │ ├── reusability │ │ ├── composables.md │ │ ├── custom-directives.md │ │ ├── mouse.js │ │ └── plugins.md │ ├── scaling-up │ │ ├── images │ │ │ ├── devtools.png │ │ │ └── state-flow.png │ │ ├── routing.md │ │ ├── sfc.md │ │ ├── ssr.md │ │ ├── state-management.md │ │ ├── testing.md │ │ └── tooling.md │ └── typescript │ │ ├── composition-api.md │ │ ├── options-api.md │ │ └── overview.md ├── index.md ├── partners │ ├── [partnerId].md │ ├── [partnerId].paths.ts │ ├── all.md │ ├── components │ │ ├── PartnerAll.vue │ │ ├── PartnerCard.vue │ │ ├── PartnerHero.vue │ │ ├── PartnerJoin.vue │ │ ├── PartnerLanding.vue │ │ ├── PartnerList.vue │ │ ├── PartnerLocation.vue │ │ ├── PartnerPage.vue │ │ ├── type.ts │ │ └── utils.ts │ ├── index.md │ └── partners.json ├── public │ ├── _headers │ ├── _redirects │ ├── funding.json │ ├── images │ │ ├── developers │ │ │ ├── 1020-profile.svg │ │ │ ├── 1020-score.svg │ │ │ ├── 1346-profile.svg │ │ │ ├── 1346-score.svg │ │ │ ├── 2030-profile.svg │ │ │ ├── 2030-score.svg │ │ │ ├── 2535-profile.svg │ │ │ ├── 2535-score.svg │ │ │ ├── 3021-profile.svg │ │ │ ├── 3021-score.svg │ │ │ ├── 3709-profile.svg │ │ │ ├── 3709-score.svg │ │ │ ├── 4290-profile.svg │ │ │ ├── 4290-score.svg │ │ │ ├── 5022-profile.svg │ │ │ ├── 5022-score.svg │ │ │ ├── 5328-profile.svg │ │ │ ├── 5328-score.svg │ │ │ ├── 5697-profile.svg │ │ │ └── 5697-score.svg │ │ ├── lambdatest.svg │ │ ├── logo.png │ │ ├── partners │ │ │ ├── 64robots-hero.jpg │ │ │ ├── 64robots.svg │ │ │ ├── curotec-hero.jpg │ │ │ ├── curotec.png │ │ │ ├── epicmax.png │ │ │ ├── epicmax.svg │ │ │ ├── herodevs-dark.png │ │ │ ├── herodevs-hero.png │ │ │ ├── herodevs.png │ │ │ ├── jump24-dark.svg │ │ │ ├── jump24-hero.jpg │ │ │ ├── jump24.svg │ │ │ ├── monterail-dark.png │ │ │ ├── monterail-hero.png │ │ │ ├── monterail.png │ │ │ ├── passionatepeople-dark.png │ │ │ ├── passionatepeople-hero.jpg │ │ │ ├── passionatepeople.png │ │ │ ├── proxify-dark.svg │ │ │ ├── proxify-hero.jpg │ │ │ ├── proxify.svg │ │ │ ├── redberry-hero.jpg │ │ │ ├── redberry.png │ │ │ ├── tighten-dark.svg │ │ │ ├── tighten-hero.jpg │ │ │ ├── tighten.svg │ │ │ ├── vehikl-dark.svg │ │ │ ├── vehikl-hero.jpg │ │ │ ├── vehikl.svg │ │ │ ├── webreinvent-hero.jpg │ │ │ └── webreinvent.png │ │ └── paypal.png │ ├── logo-uwu.png │ ├── logo.svg │ ├── rom3.min.js │ └── service-worker.js ├── sponsor │ └── index.md ├── style-guide │ ├── index.md │ ├── rules-essential.md │ ├── rules-recommended.md │ ├── rules-strongly-recommended.md │ └── rules-use-with-caution.md ├── translations │ └── index.md └── tutorial │ ├── TutorialRepl.vue │ ├── index.md │ ├── src │ ├── step-1 │ │ ├── App │ │ │ └── template.html │ │ └── description.md │ ├── step-10 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ └── options.js │ │ └── description.md │ ├── step-11 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── ChildComp │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ └── description.md │ ├── step-12 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── ChildComp │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ └── template.html │ │ └── description.md │ ├── step-13 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── ChildComp │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ └── template.html │ │ └── description.md │ ├── step-14 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── ChildComp │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ └── template.html │ │ └── description.md │ ├── step-15 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ ├── style.css │ │ │ └── template.html │ │ ├── description.md │ │ └── import-map.json │ ├── step-2 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ └── description.md │ ├── step-3 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ ├── style.css │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ └── template.html │ │ └── description.md │ ├── step-4 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ └── description.md │ ├── step-5 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ └── description.md │ ├── step-6 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ └── description.md │ ├── step-7 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ └── options.js │ │ └── description.md │ ├── step-8 │ │ ├── App │ │ │ ├── composition.js │ │ │ ├── options.js │ │ │ ├── style.css │ │ │ └── template.html │ │ ├── _hint │ │ │ └── App │ │ │ │ ├── composition.js │ │ │ │ ├── options.js │ │ │ │ └── template.html │ │ └── description.md │ └── step-9 │ │ ├── App │ │ ├── composition.js │ │ ├── options.js │ │ └── template.html │ │ ├── _hint │ │ └── App │ │ │ ├── composition.js │ │ │ └── options.js │ │ └── description.md │ └── tutorial.data.ts ├── tsconfig.json └── vercel.json /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | indent_style = space 4 | indent_size = 2 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.md] 10 | trim_trailing_whitespace = false 11 | -------------------------------------------------------------------------------- /.github/contributing/oxford-comma.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/.github/contributing/oxford-comma.jpg -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Enable version updates for npm 4 | - package-ecosystem: 'npm' 5 | # Look for `package.json` and `lock` files in the `root` directory 6 | directory: '/' 7 | # Check the npm registry for updates every day (weekdays) 8 | schedule: 9 | interval: 'daily' 10 | open-pull-requests-limit: 10 11 | versioning-strategy: lockfile-only 12 | allow: 13 | - dependency-name: 'vue' 14 | - dependency-name: 'vitepress' 15 | - dependency-name: '@vue/theme' 16 | - dependency-name: '@vue/repl' 17 | -------------------------------------------------------------------------------- /.github/scripts/tag-alert-blocks.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { exec } = require('child_process') 4 | 5 | /** 6 | * Execute a command and return stdout as string. 7 | * @param {string} command 8 | * @returns {Promise} 9 | */ 10 | function run(command) { 11 | return new Promise((resolve, reject) => { 12 | exec(command, { encoding: 'utf-8' }, (error, stdout) => 13 | error ? reject(error) : resolve(stdout) 14 | ) 15 | }) 16 | } 17 | 18 | const ALERT_BLOCK = /^\+\s*:::\s?(\w+)/m 19 | 20 | async function isUsingAlertBlock(base = 'origin/master') { 21 | const result = await run(`git diff --name-only ${base}`) 22 | const files = ( 23 | await Promise.all( 24 | result 25 | .trim() 26 | .split(/\r?\n/) 27 | .map(file => 28 | run(`git diff ${base} -- ${file}`) 29 | .then(diff => ALERT_BLOCK.test(diff)) 30 | .then(usesAlertBlock => (usesAlertBlock ? file : '')) 31 | ) 32 | ) 33 | ).filter(Boolean) 34 | 35 | if (files.length) { 36 | return true 37 | } 38 | 39 | return false 40 | } 41 | 42 | module.exports = { isUsingAlertBlock } 43 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v1.1.1 16 | with: 17 | github-token: '${{ secrets.GITHUB_TOKEN }}' 18 | - name: Enable auto-merge for theme 19 | if: ${{contains(steps.metadata.outputs.dependency-names, '@vue/theme') && steps.metadata.outputs.update-type != 'version-update:semver-major'}} 20 | run: gh pr merge --auto --merge "$PR_URL" 21 | env: 22 | PR_URL: ${{github.event.pull_request.html_url}} 23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 24 | -------------------------------------------------------------------------------- /.github/workflows/autosync.yml: -------------------------------------------------------------------------------- 1 | name: Auto Sync 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # At 00:00. https://crontab.guru/ 6 | workflow_dispatch: # on button click 7 | 8 | jobs: 9 | sync: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: TobKed/github-forks-sync-action@master 13 | with: 14 | github_token: ${{ secrets.GITHUB_TOKEN }} 15 | upstream_repository: vuejs/docs 16 | upstream_branch: main 17 | target_repository: vuejs-translations/docs-fa 18 | target_branch: upstream 19 | force: false 20 | tags: false 21 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-manager-strict=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.vue 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 75 6 | } 7 | -------------------------------------------------------------------------------- /.vitepress/headerMdPlugin.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A markdown-it plugin to support custom header metadata 3 | * Headers that end with * are Options API only 4 | * Headers that end with ** are Composition API only 5 | * This plugin strips the markers and augments the extracted header data, 6 | * which can be then used by the theme to filter headers. 7 | * 8 | * TODO: we will likely also need special syntax for preserving the same anchor 9 | * links across translations similar to the one at 10 | * https://github.com/vitejs/docs-cn/tree/main/.vitepress/markdown-it-custom-anchor 11 | */ 12 | 13 | import MarkdownIt from 'markdown-it' 14 | import { Header } from 'vitepress' 15 | 16 | export interface AugmentedHeader extends Header { 17 | compositionOnly?: boolean 18 | optionsOnly?: boolean 19 | } 20 | 21 | export const headerPlugin = (md: MarkdownIt) => { 22 | md.renderer.rules.heading_open = (tokens, i, options, env, self) => { 23 | for (const child of tokens[i + 1].children!) { 24 | if (child.type === 'text' && child.content.endsWith('*')) { 25 | child.content = child.content.replace(/\s*\*+$/, '') 26 | } 27 | } 28 | return self.renderToken(tokens, i, options) 29 | } 30 | 31 | const render = md.render 32 | md.render = (content, env) => { 33 | const res = render(content, env) 34 | 35 | if (env && env.headers) { 36 | processHeaders(env.headers) 37 | } 38 | 39 | return res 40 | } 41 | } 42 | 43 | function processHeaders(headers: AugmentedHeader[]) { 44 | for (const h of headers) { 45 | if (h.title.endsWith('*')) { 46 | if (h.title.endsWith('**')) { 47 | h.compositionOnly = true 48 | } else { 49 | h.optionsOnly = true 50 | } 51 | h.title = h.title.replace(/\s*\*+$/, '') 52 | } 53 | if (h.children) { 54 | processHeaders(h.children) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.vitepress/inlined-scripts/perfops.js: -------------------------------------------------------------------------------- 1 | ;((d) => { 2 | window.rum = { key: 'a9efvfeu' } 3 | var script = d.createElement('script') 4 | script.src = '/rom3.min.js' 5 | script.type = 'text/javascript' 6 | script.defer = true 7 | script.async = true 8 | d.getElementsByTagName('head')[0].appendChild(script) 9 | })(document) 10 | -------------------------------------------------------------------------------- /.vitepress/inlined-scripts/restorePreference.js: -------------------------------------------------------------------------------- 1 | ;(() => { 2 | const restore = (key, cls, def = false) => { 3 | const saved = localStorage.getItem(key) 4 | if (saved ? saved !== 'false' : def) { 5 | document.documentElement.classList.add(cls) 6 | } 7 | } 8 | restore('vue-docs-prefer-composition', 'prefer-composition', true) 9 | restore('vue-docs-prefer-sfc', 'prefer-sfc', true) 10 | 11 | // window.__VUE_BANNER_ID__ = '' 12 | // restore(`vue-docs-banner-${__VUE_BANNER_ID__}`, 'banner-dismissed') 13 | })() 14 | -------------------------------------------------------------------------------- /.vitepress/inlined-scripts/uwu.js: -------------------------------------------------------------------------------- 1 | if (location.search.includes('?uwu')) { 2 | document.documentElement.classList.add('uwu') 3 | } 4 | -------------------------------------------------------------------------------- /.vitepress/textAdMdPlugin.ts: -------------------------------------------------------------------------------- 1 | import MarkdownIt from 'markdown-it' 2 | 3 | const excludedPages = [ 4 | 'guide/introduction.md', 5 | // 'guide/quick-start.md', 6 | // 'guide/essentials/computed.md', 7 | // 'guide/essentials/conditional.md', 8 | // 'guide/essentials/list.md', 9 | // 'guide/essentials/event-handling.md', 10 | // 'guide/essentials/forms.md', 11 | // 'guide/components/registration.md', 12 | // 'guide/components/props.md', 13 | // 'guide/components/events.md', 14 | // 'guide/components/slots.md', 15 | // 'guide/built-ins/teleport.md', 16 | 'about/faq.md', 17 | 'about/team.md', 18 | 'about/releases.md', 19 | 'about/community-guide.md', 20 | 'about/coc.md', 21 | 'sponsor/index.md', 22 | 'translations/index.md' 23 | ] 24 | 25 | export const textAdPlugin = (md: MarkdownIt) => { 26 | md.renderer.rules.heading_close = (tokens, i, options, env, self) => { 27 | const relativePath = env.relativePath 28 | const renderedContent = self.renderToken(tokens, i, options) 29 | 30 | return excludedPages.includes(relativePath) 31 | ? renderedContent 32 | : renderedContent.replace(/<\/h1>/, '') 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.vitepress/theme/components/CallToActionSection.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 24 | 25 | 74 | -------------------------------------------------------------------------------- /.vitepress/theme/components/PageHero.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 64 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ReplLoading.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 60 | -------------------------------------------------------------------------------- /.vitepress/theme/components/ScrimbaLink.vue: -------------------------------------------------------------------------------- 1 | 13 | 21 | -------------------------------------------------------------------------------- /.vitepress/theme/components/SiteMap.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 27 | 28 | 68 | -------------------------------------------------------------------------------- /.vitepress/theme/components/SponsorsAside.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 26 | -------------------------------------------------------------------------------- /.vitepress/theme/components/TextAd.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 54 | -------------------------------------------------------------------------------- /.vitepress/theme/components/VueSchoolLink.vue: -------------------------------------------------------------------------------- 1 | 13 | 21 | 60 | -------------------------------------------------------------------------------- /.vitepress/theme/components/preferences.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { AugmentedHeader } from '../../headerMdPlugin' 3 | 4 | export const inBrowser = typeof window !== 'undefined' 5 | const get = (key: string, defaultValue = false): boolean => 6 | inBrowser 7 | ? JSON.parse(localStorage.getItem(key) || String(defaultValue)) 8 | : defaultValue 9 | 10 | export const preferCompositionKey = 'vue-docs-prefer-composition' 11 | export const preferComposition = ref(get(preferCompositionKey, true)) 12 | 13 | export const preferSFCKey = 'vue-docs-prefer-sfc' 14 | export const preferSFC = ref(get(preferSFCKey, true)) 15 | 16 | export function filterHeadersByPreference(h: AugmentedHeader) { 17 | return preferComposition.value ? !h.optionsOnly : !h.compositionOnly 18 | } 19 | -------------------------------------------------------------------------------- /.vitepress/theme/components/sponsors.ts: -------------------------------------------------------------------------------- 1 | // shared data across instances so we load only once 2 | 3 | import { ref } from 'vue' 4 | 5 | declare global { 6 | const fathom: { 7 | trackGoal: (id: string, value: number) => any 8 | } 9 | } 10 | 11 | export interface Sponsor { 12 | url: string 13 | img: string 14 | name: string 15 | description?: string 16 | priority?: boolean 17 | } 18 | 19 | export interface SponsorData { 20 | special: Sponsor[] 21 | platinum: Sponsor[] 22 | platinum_china: Sponsor[] 23 | gold: Sponsor[] 24 | silver: Sponsor[] 25 | bronze: Sponsor[] 26 | } 27 | 28 | export const data = ref() 29 | export const pending = ref(false) 30 | 31 | export const base = `https://automation.vuejs.org` 32 | 33 | export const load = async () => { 34 | if (!pending.value) { 35 | pending.value = true 36 | data.value = await (await fetch(`${base}/data.json`)).json() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | import './styles/index.css' 2 | import { h, App } from 'vue' 3 | import { VPTheme } from '@vue/theme' 4 | import PreferenceSwitch from './components/PreferenceSwitch.vue' 5 | import SecurityUpdateBtn from './components/SecurityUpdateBtn.vue' 6 | import { 7 | preferComposition, 8 | preferSFC, 9 | filterHeadersByPreference 10 | } from './components/preferences' 11 | import SponsorsAside from './components/SponsorsAside.vue' 12 | import VueSchoolLink from './components/VueSchoolLink.vue' 13 | import ScrimbaLink from './components/ScrimbaLink.vue' 14 | // import Banner from './components/Banner.vue' 15 | // import TextAd from './components/TextAd.vue' 16 | 17 | export default Object.assign({}, VPTheme, { 18 | Layout: () => { 19 | // @ts-ignore 20 | return h(VPTheme.Layout, null, { 21 | // banner: () => h(Banner), 22 | 'sidebar-top': () => h(PreferenceSwitch), 23 | 'sidebar-bottom': () => h(SecurityUpdateBtn), 24 | 'aside-mid': () => h(SponsorsAside) 25 | }) 26 | }, 27 | enhanceApp({ app }: { app: App }) { 28 | app.provide('prefer-composition', preferComposition) 29 | app.provide('prefer-sfc', preferSFC) 30 | app.provide('filter-headers', filterHeadersByPreference) 31 | app.component('VueSchoolLink', VueSchoolLink) 32 | app.component('ScrimbaLink', ScrimbaLink) 33 | // app.component('TextAd', TextAd) 34 | } 35 | }) 36 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/badges.css: -------------------------------------------------------------------------------- 1 | .vt-badge.wip:before { 2 | content: 'WIP'; 3 | } 4 | 5 | .vt-badge.ts { 6 | background-color: #3178c6; 7 | } 8 | .vt-badge.ts:before { 9 | content: 'TS'; 10 | } 11 | 12 | .vt-badge.dev-only, 13 | .vt-badge.experimental { 14 | color: var(--vt-c-text-light-1); 15 | background-color: var(--vt-c-yellow); 16 | } 17 | 18 | .vt-badge.dev-only:before { 19 | content: 'Dev only'; 20 | } 21 | 22 | .vt-badge.experimental:before { 23 | content: 'Experimental'; 24 | } 25 | 26 | .vt-badge[data-text]:before { 27 | content: attr(data-text); 28 | } 29 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/index.css: -------------------------------------------------------------------------------- 1 | @import './pages.css'; 2 | @import './badges.css'; 3 | @import './options-boxes.css'; 4 | @import './inline-demo.css'; 5 | @import './utilities.css'; 6 | @import './style-guide.css'; 7 | @import './overrides.css'; 8 | 9 | /* bugfix: https://github.com/vuejs/theme/pull/95 */ 10 | 11 | .vt-flyout-menu { 12 | max-height: calc( 13 | 100vh - var(--vt-nav-height) - var(--vt-banner-height, 0px) 14 | ) !important; 15 | } 16 | 17 | .tutorial .vt-flyout .vt-flyout-menu { 18 | right: unset; 19 | left: 0; 20 | } 21 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/options-boxes.css: -------------------------------------------------------------------------------- 1 | .next-steps { 2 | margin-top: 3rem; 3 | } 4 | 5 | .next-steps .vt-box { 6 | border: 1px solid var(--vt-c-bg-soft); 7 | } 8 | 9 | .next-steps .vt-box:hover { 10 | border-color: var(--vt-c-green-light); 11 | transition: border-color 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); 12 | } 13 | 14 | .vt-doc .next-steps-link { 15 | font-size: 20px; 16 | line-height: 1.4; 17 | letter-spacing: -0.02em; 18 | margin-bottom: 0.75em; 19 | display: block; 20 | color: var(--vt-c-green); 21 | } 22 | 23 | .vt-doc .next-steps-caption { 24 | margin-bottom: 0; 25 | color: var(--vt-c-text-2); 26 | transition: color 0.5s; 27 | } 28 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/pages.css: -------------------------------------------------------------------------------- 1 | /* always show anchors on /api/ and /style-guide/ pages */ 2 | .vt-doc.api h2 .header-anchor, 3 | .vt-doc.style-guide h2 .header-anchor { 4 | opacity: 1; 5 | } 6 | 7 | .vt-doc.sponsor h3 { 8 | text-align: center; 9 | padding-bottom: 1em; 10 | border-bottom: 1px solid var(--vt-c-divider-light); 11 | } 12 | 13 | .vt-doc.sponsor h3 .header-anchor { 14 | display: none; 15 | } 16 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/style-guide.css: -------------------------------------------------------------------------------- 1 | .style-example { 2 | border-radius: 8px 8px 12px 12px; 3 | margin: 1.6em 0; 4 | padding: 1.6em 1.6em 0.1px; 5 | position: relative; 6 | border: 1px solid transparent; 7 | transition: background-color 0.25s ease, border-color 0.25s ease; 8 | } 9 | 10 | .vt-doc .style-example h3 { 11 | margin: 0; 12 | font-size: 1.1em; 13 | } 14 | 15 | .style-example-bad { 16 | background: #f7e8e8; 17 | } 18 | .dark .style-example-bad { 19 | background: transparent; 20 | border-color: var(--vt-c-red); 21 | } 22 | 23 | .style-example-bad h3 { 24 | color: var(--vt-c-red); 25 | } 26 | 27 | .style-example-good { 28 | background: #ecfaf7; 29 | } 30 | .dark .style-example-good { 31 | background: transparent; 32 | border-color: var(--vt-c-green); 33 | } 34 | 35 | .style-example-good h3 { 36 | color: var(--vt-c-green); 37 | } 38 | 39 | .details summary { 40 | font-weight: bold !important; 41 | } 42 | 43 | .style-verb { 44 | font-size: 0.6em; 45 | display: inline-block; 46 | border-radius: 6px; 47 | font-size: 0.65em; 48 | line-height: 1; 49 | font-weight: 600; 50 | padding: 0.35em 0.4em 0.3em; 51 | position: relative; 52 | top: -0.15em; 53 | margin-right: 0.5em; 54 | color: var(--vt-c-bg); 55 | transition: color 0.5s; 56 | background-color: var(--vt-c-brand); 57 | } 58 | 59 | .style-verb.avoid { 60 | background-color: var(--vt-c-red); 61 | } 62 | .vt-doc summary { 63 | width: fit-content; 64 | cursor: pointer; 65 | } 66 | .vt-doc blockquote { 67 | border-right: 0.2rem solid var(--vt-c-divider) !important; 68 | border-left: none !important; 69 | padding-right: 1rem !important; 70 | padding-left: 0 !important; 71 | } 72 | .vt-doc pre { 73 | direction: ltr; 74 | } 75 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/utilities.css: -------------------------------------------------------------------------------- 1 | .nowrap { 2 | white-space: nowrap; 3 | } 4 | 5 | .sr-only { 6 | position: absolute; 7 | width: 1px; 8 | height: 1px; 9 | padding: 0; 10 | margin: -1px; 11 | overflow: hidden; 12 | clip: rect(0, 0, 0, 0); 13 | border: 0; 14 | } 15 | -------------------------------------------------------------------------------- /.vitepress/theme/styles/vue-mastery.css: -------------------------------------------------------------------------------- 1 | .vue-mastery-link { 2 | background-color: var(--vt-c-bg-soft); 3 | border-radius: 8px; 4 | padding: 8px 8px 8px 16px; 5 | transition: color 0.5s, background-color 0.5s; 6 | } 7 | 8 | .vue-mastery-link a { 9 | display: flex; 10 | align-items: center; 11 | } 12 | 13 | .vue-mastery-link .banner { 14 | background-color: var(--vt-c-white-soft); 15 | border-radius: 4px; 16 | width: 96px; 17 | height: 56px; 18 | object-fit: cover; 19 | } 20 | 21 | .vue-mastery-link .description { 22 | flex: 1; 23 | font-weight: 500; 24 | font-size: 14px; 25 | line-height: 20px; 26 | color: var(--vt-c-text-1); 27 | margin: 0 16px 0 0px; 28 | transition: color 0.5s; 29 | } 30 | 31 | .vue-mastery-link .description span { 32 | color: var(--vt-c-brand); 33 | } 34 | 35 | .vue-mastery-link .logo-wrapper { 36 | position: relative; 37 | width: 48px; 38 | height: 48px; 39 | border-radius: 50%; 40 | background-color: var(--vt-c-white); 41 | display: flex; 42 | justify-content: center; 43 | align-items: center; 44 | } 45 | 46 | .vue-mastery-link .logo-wrapper img { 47 | width: 25px; 48 | object-fit: contain; 49 | } 50 | 51 | @media (max-width: 576px) { 52 | .vue-mastery-link .banner { 53 | width: 56px; 54 | } 55 | 56 | .vue-mastery-link .description { 57 | font-size: 12px; 58 | line-height: 18px; 59 | } 60 | .vue-mastery-link .logo-wrapper { 61 | position: relative; 62 | width: 32px; 63 | height: 32px; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "nuxt.isNuxtApp": false, 3 | "cSpell.words": [ 4 | "vitepress", 5 | "آبجکت", 6 | "آبجکتی", 7 | "آپشن", 8 | "آرگومان", 9 | "اپلیکیشن", 10 | "اتریبیوت", 11 | "استایل", 12 | "استایلی", 13 | "استرینگ", 14 | "استک", 15 | "اِستیت", 16 | "اسکرول", 17 | "اسکوپ", 18 | "اسلات", 19 | "المنت", 20 | "المنتی", 21 | "اینپوت", 22 | "بولین", 23 | "بیلد", 24 | "پارسر", 25 | "پراپ", 26 | "پراپرتی", 27 | "پروداکشن", 28 | "پلاتینیومی", 29 | "تایپ", 30 | "ترنزیشن‌", 31 | "ترنزیشن", 32 | "تلپورت", 33 | "تمپلیت", 34 | "جاوااسکریپت", 35 | "جاوااسکریپتی", 36 | "دایرکتیو", 37 | "دایرکتیوها", 38 | "دسکتاپ", 39 | "دیباگ", 40 | "رانتایم", 41 | "رندر", 42 | "رِندر", 43 | "رندرینگ", 44 | "ستاپ", 45 | "سلکتور", 46 | "سینتکس", 47 | "فریمورک", 48 | "کامپایل", 49 | "کامپوننت", 50 | "کامپوننتی", 51 | "کامندلاین", 52 | "کامیت", 53 | "کانتینر", 54 | "کدنویسی", 55 | "کیلوبایتی", 56 | "گیت‌هاب", 57 | "ماژولار", 58 | "وبسایت" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '@vue/theme/config' { 4 | import { UserConfig } from 'vitepress' 5 | const config: () => Promise 6 | export default config 7 | } 8 | 9 | declare module '@vue/theme/highlight' { 10 | const createHighlighter: () => Promise<(input: string) => string> 11 | export default createHighlighter 12 | } 13 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "22" 3 | 4 | [build] 5 | publish = ".vitepress/dist" 6 | command = "pnpm run build" 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "engines": { 3 | "node": ">=18.0.0" 4 | }, 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vitepress", 8 | "build": "vitepress build", 9 | "preview": "vitepress preview", 10 | "preinstall": "npx only-allow pnpm", 11 | "type": "vue-tsc --noEmit" 12 | }, 13 | "dependencies": { 14 | "@vue/repl": "^4.4.2", 15 | "@vue/theme": "^2.3.0", 16 | "dynamics.js": "^1.1.5", 17 | "gsap": "^3.12.5", 18 | "vitepress": "^1.4.3", 19 | "vue": "^3.5.12" 20 | }, 21 | "devDependencies": { 22 | "@types/body-scroll-lock": "^3.1.2", 23 | "@types/markdown-it": "^14.1.2", 24 | "@types/node": "^22.7.5", 25 | "typescript": "^5.6.3", 26 | "vue-tsc": "^2.1.6" 27 | }, 28 | "packageManager": "pnpm@9.12.1" 29 | } 30 | -------------------------------------------------------------------------------- /src/about/images/ben-hong.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/about/images/ben-hong.jpeg -------------------------------------------------------------------------------- /src/about/images/evan-you.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/about/images/evan-you.jpeg -------------------------------------------------------------------------------- /src/about/team.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | title: با گروه ما ملاقات کنید 4 | --- 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/about/team/Member.ts: -------------------------------------------------------------------------------- 1 | export interface Member { 2 | name: string 3 | avatarPic?: string 4 | title: string 5 | company?: string 6 | companyLink?: string 7 | projects: Link[] 8 | location: string | string[] 9 | languages: string[] 10 | website?: Link 11 | socials: Socials 12 | sponsor?: boolean | string 13 | reposPersonal?: string[] 14 | } 15 | 16 | export interface Link { 17 | label: string 18 | url: string 19 | } 20 | 21 | export interface Socials { 22 | github: string 23 | twitter?: string 24 | linkedin?: string 25 | codepen?: string 26 | } 27 | -------------------------------------------------------------------------------- /src/about/team/TeamHero.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 76 | -------------------------------------------------------------------------------- /src/api/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API Reference 3 | sidebar: false 4 | page: true 5 | footer: false 6 | --- 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/developers/[developerSlug].md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | footer: false 4 | title: Vue Developer 5 | --- 6 | 7 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/developers/[developerSlug].paths.ts: -------------------------------------------------------------------------------- 1 | import developers from './developers.json' 2 | 3 | export default { 4 | paths: developers.map((developer) => { 5 | return { 6 | params: { 7 | developerId: developer.id, 8 | developerSlug: developer.slug, 9 | } 10 | } 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/developers/components/DeveloperCompensations.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 27 | 28 | 38 | -------------------------------------------------------------------------------- /src/developers/components/DeveloperEducation.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 21 | 22 | 36 | -------------------------------------------------------------------------------- /src/developers/components/DeveloperHero.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 38 | 39 | 66 | -------------------------------------------------------------------------------- /src/developers/components/DeveloperImage.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 50 | 51 | 58 | -------------------------------------------------------------------------------- /src/developers/components/DeveloperJoin.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | -------------------------------------------------------------------------------- /src/developers/components/DeveloperProfileDiagram.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 31 | 32 | 45 | -------------------------------------------------------------------------------- /src/developers/components/type.ts: -------------------------------------------------------------------------------- 1 | export interface DeveloperExperienceDescription { 2 | isGrouped: boolean 3 | content: string | string[] 4 | } 5 | export interface DeveloperExperience { 6 | id: number 7 | role: string 8 | company: string 9 | startDate: string 10 | endDate: string 11 | period: string 12 | description: DeveloperExperienceDescription[] 13 | skills: string[] 14 | } 15 | 16 | export interface DeveloperEducation { 17 | id: number 18 | degree: string 19 | school: string 20 | startDate: string 21 | endDate: string 22 | } 23 | 24 | export interface DeveloperCompensations { 25 | partTime: string 26 | monthly: string 27 | } 28 | 29 | export interface DeveloperProfile { 30 | id: number 31 | slug: string 32 | name: string 33 | alias: string 34 | description: string[] 35 | proficiencies: string[] 36 | compensations: DeveloperCompensations 37 | location: string 38 | region: string 39 | experiences?: DeveloperExperience[] 40 | education?: DeveloperEducation[] 41 | } 42 | 43 | export interface DeveloperProfiles extends Array { 44 | } 45 | -------------------------------------------------------------------------------- /src/developers/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | footer: false 4 | title: Vue Developers 5 | --- 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/ecosystem/newsletters.md: -------------------------------------------------------------------------------- 1 | # خبرنامه‌های کامیونیتی {#community-newsletters} 2 | 3 | تعداد زیادی خبرنامه یا وبلاگ‌های اختصاصی Vue از طرف کامیونیتی وجود دارد که آخرین اخبار و اتفاقات اکوسیستم Vue را برای شما ارائه می‌دهد. در اینجا فهرستی غیرجامع از موارد فعالی که ما با آنها برخورد کرده‌ایم آورده شده است: 4 | 5 | - [Vue.js Feed](https://vuejsfeed.com/) 6 | - [Michael Thiessen](https://michaelnthiessen.com/newsletter) 7 | - [Jakub Andrzejewski](https://dev.to/jacobandrewsky) 8 | - [Weekly Vue News](https://weekly-vue.news/) 9 | - [Vue.js Developers Newsletter](https://vuejsdevelopers.com/newsletter/) 10 | 11 | اگر مورد مناسبی را می‌شناسید که در لیست بالا وجود ندارد، لطفاً با استفاده از لینک زیر یک PR ارسال کنید! 12 | -------------------------------------------------------------------------------- /src/ecosystem/themes.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | --- 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/ecosystem/themes/ThemeContact.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 50 | -------------------------------------------------------------------------------- /src/ecosystem/themes/ThemeHero.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 56 | -------------------------------------------------------------------------------- /src/ecosystem/themes/ThemeList.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | 42 | -------------------------------------------------------------------------------- /src/ecosystem/themes/ThemePage.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | -------------------------------------------------------------------------------- /src/error-reference/ErrorsTable.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | 29 | 35 | -------------------------------------------------------------------------------- /src/error-reference/errors.data.ts: -------------------------------------------------------------------------------- 1 | import { defineLoader } from 'vitepress' 2 | import { errorMessages } from 'vue/compiler-sfc' 3 | // @ts-expect-error internal api 4 | import { ErrorTypeStrings } from 'vue' 5 | 6 | function filterEmptyMsg(data: Record) { 7 | return Object.fromEntries(Object.entries(data).filter(([_, msg]) => msg)) 8 | } 9 | 10 | export default defineLoader({ 11 | load() { 12 | return { 13 | compiler: filterEmptyMsg(errorMessages), 14 | runtime: filterEmptyMsg(ErrorTypeStrings) 15 | } 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /src/error-reference/index.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | # Production Error Code Reference {#error-reference} 13 | 14 | ## Runtime Errors {#runtime-errors} 15 | 16 | In production builds, the 3rd argument passed to the following error handler APIs will be a short code instead of the full information string: 17 | 18 | - [`app.config.errorHandler`](/api/application#app-config-errorhandler) 19 | - [`onErrorCaptured`](/api/composition-api-lifecycle#onerrorcaptured) (Composition API) 20 | - [`errorCaptured`](/api/options-lifecycle#errorcaptured) (Options API) 21 | 22 | The following table maps the codes to their original full information strings. 23 | 24 | 25 | 26 | ## Compiler Errors {#compiler-errors} 27 | 28 | The following table provides a mapping of the production compiler error codes to their original messages. 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/examples/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | title: مثال‌ها 4 | aside: false 5 | footer: false 6 | outline: false 7 | --- 8 | 9 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/examples/src/attribute-bindings/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const message = ref('سلام دنیا!') 6 | const isRed = ref(true) 7 | const color = ref('green') 8 | 9 | function toggleRed() { 10 | isRed.value = !isRed.value 11 | } 12 | 13 | function toggleColor() { 14 | color.value = color.value === 'green' ? 'blue' : 'green' 15 | } 16 | 17 | return { 18 | message, 19 | isRed, 20 | color, 21 | toggleRed, 22 | toggleColor 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/examples/src/attribute-bindings/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | message: 'سلام دنیا!', 5 | isRed: true, 6 | color: 'green' 7 | } 8 | }, 9 | methods: { 10 | toggleRed() { 11 | this.isRed = !this.isRed 12 | }, 13 | toggleColor() { 14 | this.color = this.color === 'green' ? 'blue' : 'green' 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/examples/src/attribute-bindings/App/style.css: -------------------------------------------------------------------------------- 1 | .red { 2 | color: red; 3 | } 4 | p { 5 | direction: rtl; 6 | margin-left: auto; 7 | } -------------------------------------------------------------------------------- /src/examples/src/attribute-bindings/App/template.html: -------------------------------------------------------------------------------- 1 |

2 | 3 | برای چند ثانیه ماوس خود را روی من نگه دارید تا عنوان دینامیک من 4 | را ببینید! 5 | 6 |

7 | 8 | 12 |

13 | این باید قرمز باشد... اما روی من کلیک کنید تا تغییر کند. 14 |

15 | 16 | 17 |

18 | این باید سبز باشد و باید با هر کلیک بین سبز و آبی تغییر کند. 19 |

20 | -------------------------------------------------------------------------------- /src/examples/src/attribute-bindings/description.txt: -------------------------------------------------------------------------------- 1 | متصل می‌کنیم state اتریبیوت‌ها / پراپرتی‌های المان را به reactive، در اینجا ما به طور 2 | است v-bind:title کوتاه شده‌ی :title سینتکس -------------------------------------------------------------------------------- /src/examples/src/cells/App/composition.js: -------------------------------------------------------------------------------- 1 | import Cell from './Cell.vue' 2 | import { cells } from './store.js' 3 | 4 | export default { 5 | components: { 6 | Cell 7 | }, 8 | setup() { 9 | const cols = cells.map((_, i) => String.fromCharCode(65 + i)) 10 | return { 11 | cols, 12 | cells 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/examples/src/cells/App/options.js: -------------------------------------------------------------------------------- 1 | import Cell from './Cell.vue' 2 | import { cells } from './store.js' 3 | 4 | export default { 5 | components: { 6 | Cell 7 | }, 8 | data() { 9 | return { 10 | cols: cells.map((_, i) => String.fromCharCode(65 + i)), 11 | cells 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/examples/src/cells/App/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | table { 6 | border-collapse: collapse; 7 | table-layout: fixed; 8 | width: 100%; 9 | } 10 | 11 | th { 12 | background-color: #eee; 13 | } 14 | 15 | tr:first-of-type th { 16 | width: 100px; 17 | } 18 | 19 | tr:first-of-type th:first-of-type { 20 | width: 25px; 21 | } 22 | 23 | td { 24 | border: 1px solid #ccc; 25 | height: 1.5em; 26 | overflow: hidden; 27 | } 28 | -------------------------------------------------------------------------------- /src/examples/src/cells/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 |
{{ c }}
{{ i - 1 }} 12 | 13 |
17 | -------------------------------------------------------------------------------- /src/examples/src/cells/Cell/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { cells, evalCell } from './store.js' 3 | 4 | export default { 5 | props: { 6 | c: Number, 7 | r: Number 8 | }, 9 | setup(props) { 10 | const editing = ref(false) 11 | 12 | function update(e) { 13 | editing.value = false 14 | cells[props.c][props.r] = e.target.value.trim() 15 | } 16 | 17 | return { 18 | cells, 19 | editing, 20 | evalCell, 21 | update 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/examples/src/cells/Cell/options.js: -------------------------------------------------------------------------------- 1 | import { cells, evalCell } from './store.js' 2 | 3 | export default { 4 | props: { 5 | c: Number, 6 | r: Number 7 | }, 8 | data() { 9 | return { 10 | editing: false, 11 | cells 12 | } 13 | }, 14 | methods: { 15 | evalCell, 16 | update(e) { 17 | this.editing = false 18 | cells[this.c][this.r] = e.target.value.trim() 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/examples/src/cells/Cell/style.css: -------------------------------------------------------------------------------- 1 | .cell, .cell input { 2 | height: 1.5em; 3 | line-height: 1.5; 4 | font-size: 15px; 5 | } 6 | 7 | .cell span { 8 | padding: 0 6px; 9 | } 10 | 11 | .cell input { 12 | width: 100%; 13 | box-sizing: border-box; 14 | } -------------------------------------------------------------------------------- /src/examples/src/cells/Cell/template.html: -------------------------------------------------------------------------------- 1 |
2 | 9 | {{ evalCell(cells[c][r]) }} 10 |
11 | -------------------------------------------------------------------------------- /src/examples/src/cells/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#cells -------------------------------------------------------------------------------- /src/examples/src/cells/store.js: -------------------------------------------------------------------------------- 1 | import { reactive } from 'vue' 2 | 3 | const COLS = 5 4 | const ROWS = 20 5 | 6 | export const cells = reactive( 7 | Array.from(Array(COLS).keys()).map((i) => 8 | Array.from(Array(ROWS).keys()).map((i) => '') 9 | ) 10 | ) 11 | 12 | // adapted from https://codesandbox.io/s/jotai-7guis-task7-cells-mzoit?file=/src/atoms.ts 13 | // by @dai-shi 14 | export function evalCell(exp) { 15 | if (!exp.startsWith('=')) { 16 | return exp 17 | } 18 | 19 | // = A1 + B2 ---> get(0,1) + get(1,2) 20 | exp = exp 21 | .slice(1) 22 | .replace( 23 | /\b([A-Z])(\d{1,2})\b/g, 24 | (_, c, r) => `get(${c.charCodeAt(0) - 65},${r})` 25 | ) 26 | 27 | try { 28 | return new Function('get', `return ${exp}`)(getCellValue) 29 | } catch (e) { 30 | return `#ERROR ${e}` 31 | } 32 | } 33 | 34 | function getCellValue(c, r) { 35 | const val = evalCell(cells[c][r]) 36 | const num = Number(val) 37 | return Number.isFinite(num) ? num : val 38 | } 39 | -------------------------------------------------------------------------------- /src/examples/src/circle-drawer/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, shallowReactive, toRaw } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const history = shallowReactive([[]]) 6 | const index = ref(0) 7 | const circles = ref([]) 8 | const selected = ref() 9 | const adjusting = ref(false) 10 | 11 | function onClick({ clientX: x, clientY: y }) { 12 | if (adjusting.value) { 13 | adjusting.value = false 14 | selected.value = null 15 | push() 16 | return 17 | } 18 | 19 | selected.value = [...circles.value].reverse().find(({ cx, cy, r }) => { 20 | const dx = cx - x 21 | const dy = cy - y 22 | return Math.sqrt(dx * dx + dy * dy) <= r 23 | }) 24 | 25 | if (!selected.value) { 26 | circles.value.push({ 27 | cx: x, 28 | cy: y, 29 | r: 50 30 | }) 31 | push() 32 | } 33 | } 34 | 35 | function adjust(circle) { 36 | selected.value = circle 37 | adjusting.value = true 38 | } 39 | 40 | function push() { 41 | history.length = ++index.value 42 | history.push(clone(circles.value)) 43 | console.log(toRaw(history)) 44 | } 45 | 46 | function undo() { 47 | circles.value = clone(history[--index.value]) 48 | } 49 | 50 | function redo() { 51 | circles.value = clone(history[++index.value]) 52 | } 53 | 54 | function clone(circles) { 55 | return circles.map((c) => ({ ...c })) 56 | } 57 | 58 | return { 59 | history, 60 | index, 61 | circles, 62 | selected, 63 | adjusting, 64 | onClick, 65 | adjust, 66 | undo, 67 | redo 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/examples/src/circle-drawer/App/options.js: -------------------------------------------------------------------------------- 1 | function clone(circles) { 2 | return circles.map((c) => ({ ...c })) 3 | } 4 | 5 | export default { 6 | data() { 7 | return { 8 | history: [[]], 9 | index: 0, 10 | circles: [], 11 | selected: null, 12 | adjusting: false 13 | } 14 | }, 15 | methods: { 16 | onClick({ clientX: x, clientY: y }) { 17 | if (this.adjusting) { 18 | this.adjusting = false 19 | this.selected = null 20 | this.push() 21 | return 22 | } 23 | 24 | this.selected = [...this.circles].reverse().find(({ cx, cy, r }) => { 25 | const dx = cx - x 26 | const dy = cy - y 27 | return Math.sqrt(dx * dx + dy * dy) <= r 28 | }) 29 | 30 | if (!this.selected) { 31 | this.circles.push({ 32 | cx: x, 33 | cy: y, 34 | r: 50 35 | }) 36 | this.push() 37 | } 38 | }, 39 | 40 | adjust(circle) { 41 | this.selected = circle 42 | this.adjusting = true 43 | }, 44 | 45 | push() { 46 | this.history.length = ++this.index 47 | this.history.push(clone(this.circles)) 48 | }, 49 | 50 | undo() { 51 | this.circles = clone(this.history[--this.index]) 52 | }, 53 | 54 | redo() { 55 | this.circles = clone(this.history[++this.index]) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/examples/src/circle-drawer/App/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | overflow: hidden; 4 | } 5 | 6 | svg { 7 | width: 100vw; 8 | height: 100vh; 9 | background-color: #eee; 10 | } 11 | 12 | circle { 13 | stroke: #000; 14 | } 15 | 16 | .controls { 17 | position: fixed; 18 | top: 10px; 19 | left: 0; 20 | right: 0; 21 | text-align: center; 22 | } 23 | 24 | .controls button + button { 25 | margin-left: 6px; 26 | } 27 | 28 | .dialog { 29 | position: fixed; 30 | top: calc(50% - 50px); 31 | left: calc(50% - 175px); 32 | background: #fff; 33 | width: 350px; 34 | height: 100px; 35 | padding: 5px 20px; 36 | box-sizing: border-box; 37 | border-radius: 4px; 38 | text-align: center; 39 | box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25); 40 | } 41 | 42 | .dialog input { 43 | display: block; 44 | width: 200px; 45 | margin: 0px auto; 46 | } 47 | 48 | .tip { 49 | text-align: center; 50 | padding: 0 50px; 51 | color: #bbb; 52 | } -------------------------------------------------------------------------------- /src/examples/src/circle-drawer/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | Click on the canvas to draw a circle. Click on a circle to select it. 5 | Right-click on the canvas to adjust the radius of the selected circle. 6 |

7 |
8 | 17 |
18 | 19 |
20 | 21 | 22 |
23 | 24 |
25 |

Adjust radius of circle at ({{ selected.cx }}, {{ selected.cy }})

26 | 27 |
28 | -------------------------------------------------------------------------------- /src/examples/src/circle-drawer/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#circle -------------------------------------------------------------------------------- /src/examples/src/conditionals-and-loops/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const show = ref(true) 6 | const list = ref([1, 2, 3]) 7 | 8 | return { 9 | show, 10 | list 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/examples/src/conditionals-and-loops/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | show: true, 5 | list: [1, 2, 3] 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /src/examples/src/conditionals-and-loops/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 |
  • {{ item }}
  • 8 |
9 |

لیست خالی نیست، اما پنهان است

10 |

لیست خالی است

11 | -------------------------------------------------------------------------------- /src/examples/src/conditionals-and-loops/description.txt: -------------------------------------------------------------------------------- 1 | می‌توانیم محتوا را به طور شرطی یا در یک حلقه 2 | نمایش دهیم v-for و v-if با استفاده از دایرکتیو‌های -------------------------------------------------------------------------------- /src/examples/src/counter/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const count = ref(0) 6 | 7 | return { 8 | count 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/examples/src/counter/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | count: 0 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /src/examples/src/counter/App/template.html: -------------------------------------------------------------------------------- 1 | {{ count }} 2 | -------------------------------------------------------------------------------- /src/examples/src/counter/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#counter -------------------------------------------------------------------------------- /src/examples/src/crud/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, reactive, computed, watch } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const names = reactive(['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman']) 6 | const selected = ref('') 7 | const prefix = ref('') 8 | const first = ref('') 9 | const last = ref('') 10 | 11 | const filteredNames = computed(() => 12 | names.filter((n) => 13 | n.toLowerCase().startsWith(prefix.value.toLowerCase()) 14 | ) 15 | ) 16 | 17 | watch(selected, (name) => { 18 | [last.value, first.value] = name.split(', ') 19 | }) 20 | 21 | function create() { 22 | if (hasValidInput()) { 23 | const fullName = `${last.value}, ${first.value}` 24 | if (!names.includes(fullName)) { 25 | names.push(fullName) 26 | first.value = last.value = '' 27 | } 28 | } 29 | } 30 | 31 | function update() { 32 | if (hasValidInput() && selected.value) { 33 | const i = names.indexOf(selected.value) 34 | names[i] = selected.value = `${last.value}, ${first.value}` 35 | } 36 | } 37 | 38 | function del() { 39 | if (selected.value) { 40 | const i = names.indexOf(selected.value) 41 | names.splice(i, 1) 42 | selected.value = first.value = last.value = '' 43 | } 44 | } 45 | 46 | function hasValidInput() { 47 | return first.value.trim() && last.value.trim() 48 | } 49 | 50 | return { 51 | filteredNames, 52 | selected, 53 | prefix, 54 | first, 55 | last, 56 | create, 57 | update, 58 | del 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/examples/src/crud/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | names: ['Emil, Hans', 'Mustermann, Max', 'Tisch, Roman'], 5 | selected: '', 6 | prefix: '', 7 | first: '', 8 | last: '' 9 | } 10 | }, 11 | computed: { 12 | filteredNames() { 13 | return this.names.filter((n) => 14 | n.toLowerCase().startsWith(this.prefix.toLowerCase()) 15 | ) 16 | } 17 | }, 18 | watch: { 19 | selected(name) { 20 | ;[this.last, this.first] = name.split(', ') 21 | } 22 | }, 23 | methods: { 24 | create() { 25 | if (this.hasValidInput()) { 26 | const fullName = `${this.last}, ${this.first}` 27 | if (!this.names.includes(fullName)) { 28 | this.names.push(fullName) 29 | this.first = this.last = '' 30 | } 31 | } 32 | }, 33 | update() { 34 | if (this.hasValidInput() && this.selected) { 35 | const i = this.names.indexOf(this.selected) 36 | this.names[i] = this.selected = `${this.last}, ${this.first}` 37 | } 38 | }, 39 | del() { 40 | if (this.selected) { 41 | const i = this.names.indexOf(this.selected) 42 | this.names.splice(i, 1) 43 | this.selected = this.first = this.last = '' 44 | } 45 | }, 46 | hasValidInput() { 47 | return this.first.trim() && this.last.trim() 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/examples/src/crud/App/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-size: inherit; 3 | } 4 | 5 | input { 6 | display: block; 7 | margin-bottom: 10px; 8 | } 9 | 10 | select { 11 | float: left; 12 | margin: 0 1em 1em 0; 13 | width: 14em; 14 | } 15 | 16 | .buttons { 17 | clear: both; 18 | } 19 | 20 | button + button { 21 | margin-left: 5px; 22 | } 23 | -------------------------------------------------------------------------------- /src/examples/src/crud/App/template.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 | -------------------------------------------------------------------------------- /src/examples/src/crud/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#crud -------------------------------------------------------------------------------- /src/examples/src/fetching-data/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, watchEffect } from 'vue' 2 | 3 | const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=` 4 | const branches = ['main', 'minor'] 5 | 6 | export default { 7 | setup() { 8 | const currentBranch = ref(branches[0]) 9 | const commits = ref([]) 10 | 11 | watchEffect(async () => { 12 | // این effect بلافاصله اجرا می‌شود و سپس... 13 | // هر زمان که مقدار currentBranch تغییر کند، مجددا اجرا می شود 14 | const url = `${API_URL}${currentBranch.value}` 15 | commits.value = await (await fetch(url)).json() 16 | }) 17 | 18 | function truncate(v) { 19 | const newline = v.indexOf('\n') 20 | return newline > 0 ? v.slice(0, newline) : v 21 | } 22 | 23 | function formatDate(v) { 24 | return v.replace(/T|Z/g, ' ') 25 | } 26 | 27 | return { 28 | branches, 29 | currentBranch, 30 | commits, 31 | truncate, 32 | formatDate 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/examples/src/fetching-data/App/options.js: -------------------------------------------------------------------------------- 1 | const API_URL = `https://api.github.com/repos/vuejs/core/commits?per_page=3&sha=` 2 | 3 | export default { 4 | data: () => ({ 5 | branches: ['main', 'minor'], 6 | currentBranch: 'main', 7 | commits: [] 8 | }), 9 | 10 | created() { 11 | // (init)دریافت داده در زمان شروع 12 | this.fetchData() 13 | }, 14 | 15 | watch: { 16 | // دریافت مجدد هرگاه که برنچ فعلی تغییر کند 17 | currentBranch: 'fetchData' 18 | }, 19 | 20 | methods: { 21 | async fetchData() { 22 | const url = `${API_URL}${this.currentBranch}` 23 | this.commits = await (await fetch(url)).json() 24 | }, 25 | truncate(v) { 26 | const newline = v.indexOf('\n') 27 | return newline > 0 ? v.slice(0, newline) : v 28 | }, 29 | formatDate(v) { 30 | return v.replace(/T|Z/g, ' ') 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/examples/src/fetching-data/App/style.css: -------------------------------------------------------------------------------- 1 | a { 2 | text-decoration: none; 3 | color: #42b883; 4 | } 5 | li { 6 | line-height: 1.5em; 7 | margin-bottom: 20px; 8 | } 9 | .author, 10 | .date { 11 | font-weight: bold; 12 | } 13 | -------------------------------------------------------------------------------- /src/examples/src/fetching-data/App/template.html: -------------------------------------------------------------------------------- 1 |

آخرین کامیت‌های اصلی Vue

2 | 10 |

vuejs/core@{{ currentBranch }}

11 | 21 | -------------------------------------------------------------------------------- /src/examples/src/fetching-data/description.txt: -------------------------------------------------------------------------------- 1 | این مثال اطلاعات آخرین کامیت‌های Vue Core را از API GitHub دریافت می‌کند و آن‌ها را به صورت یک لیست نمایش می‌دهد. 2 | شما می‌توانید بین دو برنچ اصلی جابجا شوید. 3 | -------------------------------------------------------------------------------- /src/examples/src/flight-booker/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const flightType = ref('one-way flight') 6 | const departureDate = ref(dateToString(new Date())) 7 | const returnDate = ref(departureDate.value) 8 | 9 | const isReturn = computed(() => flightType.value === 'return flight') 10 | 11 | const canBook = computed( 12 | () => 13 | !isReturn.value || 14 | stringToDate(returnDate.value) > stringToDate(departureDate.value) 15 | ) 16 | 17 | function book() { 18 | alert( 19 | isReturn.value 20 | ? `You have booked a return flight leaving on ${departureDate.value} and returning on ${returnDate.value}.` 21 | : `You have booked a one-way flight leaving on ${departureDate.value}.` 22 | ) 23 | } 24 | 25 | function stringToDate(str) { 26 | const [y, m, d] = str.split('-') 27 | return new Date(+y, m - 1, +d) 28 | } 29 | 30 | function dateToString(date) { 31 | return ( 32 | date.getFullYear() + 33 | '-' + 34 | pad(date.getMonth() + 1) + 35 | '-' + 36 | pad(date.getDate()) 37 | ) 38 | } 39 | 40 | function pad(n, s = String(n)) { 41 | return s.length < 2 ? `0${s}` : s 42 | } 43 | 44 | return { 45 | flightType, 46 | departureDate, 47 | returnDate, 48 | isReturn, 49 | canBook, 50 | book 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/examples/src/flight-booker/App/options.js: -------------------------------------------------------------------------------- 1 | function stringToDate(str) { 2 | const [y, m, d] = str.split('-') 3 | return new Date(+y, m - 1, +d) 4 | } 5 | 6 | function dateToString(date) { 7 | return ( 8 | date.getFullYear() + 9 | '-' + 10 | pad(date.getMonth() + 1) + 11 | '-' + 12 | pad(date.getDate()) 13 | ) 14 | } 15 | 16 | function pad(n, s = String(n)) { 17 | return s.length < 2 ? `0${s}` : s 18 | } 19 | 20 | export default { 21 | data() { 22 | return { 23 | flightType: 'one-way flight', 24 | departureDate: dateToString(new Date()), 25 | returnDate: dateToString(new Date()) 26 | } 27 | }, 28 | computed: { 29 | isReturn() { 30 | return this.flightType === 'return flight' 31 | }, 32 | canBook() { 33 | return ( 34 | !this.isReturn || 35 | stringToDate(this.returnDate) > stringToDate(this.departureDate) 36 | ) 37 | } 38 | }, 39 | methods: { 40 | book() { 41 | alert( 42 | this.isReturn 43 | ? `You have booked a return flight leaving on ${this.departureDate} and returning on ${this.returnDate}.` 44 | : `You have booked a one-way flight leaving on ${this.departureDate}.` 45 | ) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/examples/src/flight-booker/App/style.css: -------------------------------------------------------------------------------- 1 | select, 2 | input, 3 | button { 4 | display: block; 5 | margin: 0.5em 0; 6 | font-size: 15px; 7 | } 8 | 9 | input[disabled] { 10 | color: #999; 11 | } 12 | 13 | p { 14 | color: red; 15 | } -------------------------------------------------------------------------------- /src/examples/src/flight-booker/App/template.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

{{ canBook ? '' : 'Return date must be after departure date.' }}

12 | -------------------------------------------------------------------------------- /src/examples/src/flight-booker/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#flight -------------------------------------------------------------------------------- /src/examples/src/form-bindings/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const text = ref('Edit me') 6 | const checked = ref(true) 7 | const checkedNames = ref(['Jack']) 8 | const picked = ref('One') 9 | const selected = ref('A') 10 | const multiSelected = ref(['A']) 11 | 12 | return { 13 | text, 14 | checked, 15 | checkedNames, 16 | picked, 17 | selected, 18 | multiSelected 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/examples/src/form-bindings/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | text: 'Edit me', 5 | checked: true, 6 | checkedNames: ['Jack'], 7 | picked: 'One', 8 | selected: 'A', 9 | multiSelected: ['A'] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/examples/src/form-bindings/App/template.html: -------------------------------------------------------------------------------- 1 |

Text Input

2 | 3 |

{{ text }}

4 | 5 |

Checkbox

6 | 7 | 8 | 9 | 13 |

Multi Checkbox

14 | 15 | 16 | 17 | 18 | 19 | 20 |

Checked names: {{ checkedNames }}

21 | 22 |

Radio

23 | 24 | 25 |
26 | 27 | 28 |

Picked: {{ picked }}

29 | 30 |

Select

31 | 37 |

Selected: {{ selected }}

38 | 39 |

Multi Select

40 | 45 |

Selected: {{ multiSelected }}

46 | -------------------------------------------------------------------------------- /src/examples/src/form-bindings/description.txt: -------------------------------------------------------------------------------- 1 | و state بین (two-way binding) می‌توانیم اتصال دو طرفه 2 | ایجاد کنیم v-model ورودی‌های فرم با استفاده از دایرکتیو -------------------------------------------------------------------------------- /src/examples/src/grid/App/composition.js: -------------------------------------------------------------------------------- 1 | import DemoGrid from './Grid.vue' 2 | import { ref } from 'vue' 3 | 4 | export default { 5 | components: { 6 | DemoGrid 7 | }, 8 | setup() { 9 | const searchQuery = ref('') 10 | const gridColumns = ['name', 'power'] 11 | const gridData = [ 12 | { name: 'چاک نوریس', power: Infinity }, 13 | { name: 'بروس لی', power: 9000 }, 14 | { name: 'جکی جان', power: 7000 }, 15 | { name: 'جت لی', power: 8000 } 16 | ] 17 | 18 | return { 19 | searchQuery, 20 | gridColumns, 21 | gridData 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/examples/src/grid/App/options.js: -------------------------------------------------------------------------------- 1 | import DemoGrid from './Grid.vue' 2 | 3 | export default { 4 | components: { 5 | DemoGrid 6 | }, 7 | data: () => ({ 8 | searchQuery: '', 9 | gridColumns: ['name', 'power'], 10 | gridData: [ 11 | { name: 'چاک نوریس', power: Infinity }, 12 | { name: 'بروس لی', power: 9000 }, 13 | { name: 'جکی جان', power: 7000 }, 14 | { name: 'جت لی', power: 8000 } 15 | ] 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/examples/src/grid/App/template.html: -------------------------------------------------------------------------------- 1 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /src/examples/src/grid/Grid/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | 3 | export default { 4 | props: { 5 | data: Array, 6 | columns: Array, 7 | filterKey: String 8 | }, 9 | setup(props) { 10 | const sortKey = ref('') 11 | const sortOrders = ref( 12 | props.columns.reduce((o, key) => ((o[key] = 1), o), {}) 13 | ) 14 | 15 | const filteredData = computed(() => { 16 | let { data, filterKey } = props 17 | if (filterKey) { 18 | filterKey = filterKey.toLowerCase() 19 | data = data.filter((row) => { 20 | return Object.keys(row).some((key) => { 21 | return String(row[key]).toLowerCase().indexOf(filterKey) > -1 22 | }) 23 | }) 24 | } 25 | const key = sortKey.value 26 | if (key) { 27 | const order = sortOrders.value[key] 28 | data = data.slice().sort((a, b) => { 29 | a = a[key] 30 | b = b[key] 31 | return (a === b ? 0 : a > b ? 1 : -1) * order 32 | }) 33 | } 34 | return data 35 | }) 36 | 37 | function sortBy(key) { 38 | sortKey.value = key 39 | sortOrders.value[key] *= -1 40 | } 41 | 42 | function capitalize(str) { 43 | return str.charAt(0).toUpperCase() + str.slice(1) 44 | } 45 | 46 | return { 47 | sortKey, 48 | sortOrders, 49 | filteredData, 50 | sortBy, 51 | capitalize 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/examples/src/grid/Grid/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | data: Array, 4 | columns: Array, 5 | filterKey: String 6 | }, 7 | data() { 8 | return { 9 | sortKey: '', 10 | sortOrders: this.columns.reduce((o, key) => ((o[key] = 1), o), {}) 11 | } 12 | }, 13 | computed: { 14 | filteredData() { 15 | const sortKey = this.sortKey 16 | const filterKey = this.filterKey && this.filterKey.toLowerCase() 17 | const order = this.sortOrders[sortKey] || 1 18 | let data = this.data 19 | if (filterKey) { 20 | data = data.filter((row) => { 21 | return Object.keys(row).some((key) => { 22 | return String(row[key]).toLowerCase().indexOf(filterKey) > -1 23 | }) 24 | }) 25 | } 26 | if (sortKey) { 27 | data = data.slice().sort((a, b) => { 28 | a = a[sortKey] 29 | b = b[sortKey] 30 | return (a === b ? 0 : a > b ? 1 : -1) * order 31 | }) 32 | } 33 | return data 34 | } 35 | }, 36 | methods: { 37 | sortBy(key) { 38 | this.sortKey = key 39 | this.sortOrders[key] = this.sortOrders[key] * -1 40 | }, 41 | capitalize(str) { 42 | return str.charAt(0).toUpperCase() + str.slice(1) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/examples/src/grid/Grid/style.css: -------------------------------------------------------------------------------- 1 | table { 2 | border: 2px solid #42b983; 3 | border-radius: 3px; 4 | background-color: #fff; 5 | } 6 | 7 | th { 8 | background-color: #42b983; 9 | color: rgba(255, 255, 255, 0.66); 10 | cursor: pointer; 11 | user-select: none; 12 | } 13 | 14 | td { 15 | background-color: #f9f9f9; 16 | } 17 | 18 | th, 19 | td { 20 | min-width: 120px; 21 | padding: 10px 20px; 22 | } 23 | 24 | th.active { 25 | color: #fff; 26 | } 27 | 28 | th.active .arrow { 29 | opacity: 1; 30 | } 31 | 32 | .arrow { 33 | display: inline-block; 34 | vertical-align: middle; 35 | width: 0; 36 | height: 0; 37 | margin-left: 5px; 38 | opacity: 0.66; 39 | } 40 | 41 | .arrow.asc { 42 | border-left: 4px solid transparent; 43 | border-right: 4px solid transparent; 44 | border-bottom: 4px solid #fff; 45 | } 46 | 47 | .arrow.dsc { 48 | border-left: 4px solid transparent; 49 | border-right: 4px solid transparent; 50 | border-top: 4px solid #fff; 51 | } 52 | -------------------------------------------------------------------------------- /src/examples/src/grid/Grid/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 |
7 | {{ capitalize(key) }} 8 | 9 | 10 |
16 | {{entry[key]}} 17 |
21 |

هیج نتیجه ای یافت نشد.

-------------------------------------------------------------------------------- /src/examples/src/grid/description.txt: -------------------------------------------------------------------------------- 1 | بیایید یک مثال از ایجاد یک کامپوننت گرید با قابلیت استفاده مجدد و استفاده از آن با داده‌های خارجی را بررسی کنیم. 2 | 3 | -------------------------------------------------------------------------------- /src/examples/src/handling-input/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const message = ref('سلام دنیا!') 6 | 7 | function reverseMessage() { 8 | // تغییر دهید یا به آن دسترسی پیدا کنید .value را از طریق ref مقدار 9 | message.value = message.value.split('').reverse().join('') 10 | } 11 | 12 | function notify() { 13 | alert('جلوگیری از ناوبری صورت گرفت') 14 | } 15 | 16 | return { 17 | message, 18 | reverseMessage, 19 | notify 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/examples/src/handling-input/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | message: 'سلام دنیا!' 5 | } 6 | }, 7 | methods: { 8 | reverseMessage() { 9 | this.message = this.message.split('').reverse().join('') 10 | }, 11 | notify() { 12 | alert('جلوگیری از ناوبری صورت گرفت') 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/examples/src/handling-input/App/style.css: -------------------------------------------------------------------------------- 1 | button, a { 2 | display: block; 3 | margin-bottom: 1em; 4 | } 5 | button, a, h1 { 6 | direction: rtl; 7 | margin-left: auto; 8 | } 9 | -------------------------------------------------------------------------------- /src/examples/src/handling-input/App/template.html: -------------------------------------------------------------------------------- 1 | 5 |

{{ message }}

6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | پیوندی با ()e.preventDefault 22 | 23 | -------------------------------------------------------------------------------- /src/examples/src/handling-input/description.txt: -------------------------------------------------------------------------------- 1 | این مثال نحوه برخورد با ورودی کاربر با استفاده از دایرکتیو v-on را نشان می‌دهد. -------------------------------------------------------------------------------- /src/examples/src/hello-world/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | // است reactive یک منبع داده "ref" 6 | // که یک مقدار را ذخیره می‌کند 7 | // از نظر فنی، نیازی نیست که رشته را 8 | // بپیچیم ref() با استفاده از 9 | // به منظور نمایش آن، اما در ادامه خواهیم دید 10 | // که چرا این کار لازم است 11 | // اگر قصد داشته باشیم مقدار آن را تغییر دهیم 12 | const message = ref('!سلام دنیا') 13 | 14 | return { 15 | message 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/examples/src/hello-world/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | message: '!سلام دنیا' 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/examples/src/hello-world/App/template.html: -------------------------------------------------------------------------------- 1 |

{{ message }}

-------------------------------------------------------------------------------- /src/examples/src/hello-world/description.txt: -------------------------------------------------------------------------------- 1 | !به جهان سلام کنید Vue با استفاده از -------------------------------------------------------------------------------- /src/examples/src/list-transition/App/composition.js: -------------------------------------------------------------------------------- 1 | import { shuffle as _shuffle } from 'lodash-es' 2 | import { ref } from 'vue' 3 | 4 | export default { 5 | setup() { 6 | const getInitialItems = () => [1, 2, 3, 4, 5] 7 | const items = ref(getInitialItems()) 8 | let id = items.value.length + 1 9 | 10 | function insert() { 11 | const i = Math.round(Math.random() * items.value.length) 12 | items.value.splice(i, 0, id++) 13 | } 14 | 15 | function reset() { 16 | items.value = getInitialItems() 17 | id = items.value.length + 1 18 | } 19 | 20 | function shuffle() { 21 | items.value = _shuffle(items.value) 22 | } 23 | 24 | function remove(item) { 25 | const i = items.value.indexOf(item) 26 | if (i > -1) { 27 | items.value.splice(i, 1) 28 | } 29 | } 30 | 31 | return { 32 | items, 33 | insert, 34 | reset, 35 | shuffle, 36 | remove 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/examples/src/list-transition/App/options.js: -------------------------------------------------------------------------------- 1 | import { shuffle } from 'lodash-es' 2 | 3 | const getInitialItems = () => [1, 2, 3, 4, 5] 4 | let id = getInitialItems().length + 1 5 | 6 | export default { 7 | data() { 8 | return { 9 | items: getInitialItems() 10 | } 11 | }, 12 | methods: { 13 | insert() { 14 | const i = Math.round(Math.random() * this.items.length) 15 | this.items.splice(i, 0, id++) 16 | }, 17 | reset() { 18 | this.items = getInitialItems() 19 | id = getInitialItems().length + 1 20 | }, 21 | shuffle() { 22 | this.items = shuffle(this.items) 23 | }, 24 | remove(item) { 25 | const i = this.items.indexOf(item) 26 | if (i > -1) { 27 | this.items.splice(i, 1) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/examples/src/list-transition/App/style.css: -------------------------------------------------------------------------------- 1 | .container { 2 | position: relative; 3 | padding: 0; 4 | list-style-type: none; 5 | } 6 | 7 | .item { 8 | width: 100%; 9 | height: 30px; 10 | background-color: #f3f3f3; 11 | border: 1px solid #666; 12 | box-sizing: border-box; 13 | } 14 | 15 | /* 1. ترنزیشن را تعریف کنید */ 16 | .fade-move, 17 | .fade-enter-active, 18 | .fade-leave-active { 19 | transition: all 0.5s cubic-bezier(0.55, 0, 0.1, 1); 20 | } 21 | 22 | /* 2. را تعریف کنید enter from و leave to state*/ 23 | .fade-enter-from, 24 | .fade-leave-to { 25 | opacity: 0; 26 | transform: scaleY(0.01) translate(30px, 0); 27 | } 28 | 29 | /* 3. اطمینان حاصل کنید که موارد در حال خروج از جریان چیدمان خارج شده‌اند 30 | .تا انیمیشن های درحال حرکت بتوانند به درستی محاسبه شوند */ 31 | .fade-leave-active { 32 | position: absolute; 33 | } 34 | -------------------------------------------------------------------------------- /src/examples/src/list-transition/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
  • 7 | {{ item }} 8 | 9 |
  • 10 |
    11 | -------------------------------------------------------------------------------- /src/examples/src/list-transition/description.txt: -------------------------------------------------------------------------------- 1 | .نهادینه‌شده با FLIP ترنزیشن‌های لیست 2 | https://aerotwist.com/blog/flip-your-animations/ -------------------------------------------------------------------------------- /src/examples/src/list-transition/import-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "lodash-es": "https://cdn.jsdelivr.net/npm/lodash-es/+esm" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/examples/src/markdown/App/composition.js: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked' 2 | import { debounce } from 'lodash-es' 3 | import { ref, computed } from 'vue' 4 | 5 | export default { 6 | setup() { 7 | const input = ref('# سلام') 8 | 9 | const output = computed(() => marked(input.value)) 10 | 11 | const update = debounce((e) => { 12 | input.value = e.target.value 13 | }, 100) 14 | 15 | return { 16 | input, 17 | output, 18 | update 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/examples/src/markdown/App/options.js: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked' 2 | import { debounce } from 'lodash-es' 3 | 4 | export default { 5 | data: () => ({ 6 | input: '# سلام' 7 | }), 8 | computed: { 9 | output() { 10 | return marked(this.input) 11 | } 12 | }, 13 | methods: { 14 | update: debounce(function (e) { 15 | this.input = e.target.value 16 | }, 100) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/examples/src/markdown/App/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | .editor { 6 | height: 100vh; 7 | display: flex; 8 | } 9 | 10 | .input, 11 | .output { 12 | overflow: auto; 13 | width: 50%; 14 | height: 100%; 15 | box-sizing: border-box; 16 | padding: 0 20px; 17 | } 18 | 19 | .input { 20 | border: none; 21 | border-right: 1px solid #ccc; 22 | resize: none; 23 | outline: none; 24 | background-color: #f6f6f6; 25 | font-size: 14px; 26 | font-family: 'Monaco', courier, monospace; 27 | padding: 20px; 28 | } 29 | 30 | code { 31 | color: #f66; 32 | } 33 | -------------------------------------------------------------------------------- /src/examples/src/markdown/App/template.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 |
    4 |
    5 | -------------------------------------------------------------------------------- /src/examples/src/markdown/description.txt: -------------------------------------------------------------------------------- 1 | (markdown) یک ویرایشگر ساده مارک‌ داون -------------------------------------------------------------------------------- /src/examples/src/markdown/import-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "marked": "https://cdn.jsdelivr.net/npm/marked/+esm", 4 | "lodash-es": "https://cdn.jsdelivr.net/npm/lodash-es/+esm" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/examples/src/modal/App/composition.js: -------------------------------------------------------------------------------- 1 | import Modal from './Modal.vue' 2 | import { ref } from 'vue' 3 | 4 | export default { 5 | components: { 6 | Modal 7 | }, 8 | setup() { 9 | const showModal = ref(false) 10 | 11 | return { 12 | showModal 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/examples/src/modal/App/options.js: -------------------------------------------------------------------------------- 1 | import Modal from './Modal.vue' 2 | 3 | export default { 4 | components: { 5 | Modal 6 | }, 7 | data() { 8 | return { 9 | showModal: false 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/examples/src/modal/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/examples/src/modal/Modal/composition.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | show: Boolean 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/examples/src/modal/Modal/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | show: Boolean 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/examples/src/modal/Modal/style.css: -------------------------------------------------------------------------------- 1 | .modal-mask { 2 | position: fixed; 3 | z-index: 9998; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | background-color: rgba(0, 0, 0, 0.5); 9 | display: flex; 10 | transition: opacity 0.3s ease; 11 | } 12 | 13 | .modal-container { 14 | width: 300px; 15 | margin: auto; 16 | padding: 20px 30px; 17 | background-color: #fff; 18 | border-radius: 2px; 19 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33); 20 | transition: all 0.3s ease; 21 | } 22 | 23 | .modal-header h3 { 24 | margin-top: 0; 25 | color: #42b983; 26 | } 27 | 28 | .modal-body { 29 | margin: 20px 0; 30 | } 31 | 32 | .modal-default-button { 33 | float: right; 34 | } 35 | 36 | 37 | .modal-enter-from { 38 | opacity: 0; 39 | } 40 | 41 | .modal-leave-to { 42 | opacity: 0; 43 | } 44 | 45 | .modal-enter-from .modal-container, 46 | .modal-leave-to .modal-container { 47 | -webkit-transform: scale(1.1); 48 | transform: scale(1.1); 49 | } 50 | -------------------------------------------------------------------------------- /src/examples/src/modal/Modal/template.html: -------------------------------------------------------------------------------- 1 | 2 | 23 | 24 | -------------------------------------------------------------------------------- /src/examples/src/modal/description.txt: -------------------------------------------------------------------------------- 1 | .CSS کامپوننت مدال با اسلات ها و ترنزیشن های قابل شخصی‌سازی 2 | -------------------------------------------------------------------------------- /src/examples/src/simple-component/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import TodoItem from './TodoItem.vue' 3 | 4 | export default { 5 | components: { 6 | TodoItem 7 | }, 8 | setup() { 9 | const groceryList = ref([ 10 | { id: 0, text: 'Vegetables' }, 11 | { id: 1, text: 'Cheese' }, 12 | { id: 2, text: 'Whatever else humans are supposed to eat' } 13 | ]) 14 | 15 | return { 16 | groceryList 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/examples/src/simple-component/App/options.js: -------------------------------------------------------------------------------- 1 | import TodoItem from './TodoItem.vue' 2 | 3 | export default { 4 | components: { 5 | TodoItem 6 | }, 7 | data() { 8 | return { 9 | groceryList: [ 10 | { id: 0, text: 'Vegetables' }, 11 | { id: 1, text: 'Cheese' }, 12 | { id: 2, text: 'Whatever else humans are supposed to eat' } 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/examples/src/simple-component/App/template.html: -------------------------------------------------------------------------------- 1 |
      2 | 8 | 13 |
    14 | -------------------------------------------------------------------------------- /src/examples/src/simple-component/TodoItem/composition.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | todo: Object 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/examples/src/simple-component/TodoItem/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | todo: Object 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/examples/src/simple-component/TodoItem/template.html: -------------------------------------------------------------------------------- 1 |
  • {{ todo.text }}
  • -------------------------------------------------------------------------------- /src/examples/src/simple-component/description.txt: -------------------------------------------------------------------------------- 1 | در اینجا ما ساده‌ترین کامپوننت ممکن را نشان می‌دهیم 2 | را می‌پذیرد و آن را رندر می‌کند prop که یک 3 | اطلاعات بیشتر در مورد کامپوننت‌ها در راهنما -------------------------------------------------------------------------------- /src/examples/src/svg/App/composition.js: -------------------------------------------------------------------------------- 1 | import PolyGraph from './PolyGraph.vue' 2 | import { ref, reactive } from 'vue' 3 | 4 | export default { 5 | components: { 6 | PolyGraph 7 | }, 8 | setup() { 9 | const newLabel = ref('') 10 | const stats = reactive([ 11 | { label: 'A', value: 100 }, 12 | { label: 'B', value: 100 }, 13 | { label: 'C', value: 100 }, 14 | { label: 'D', value: 100 }, 15 | { label: 'E', value: 100 }, 16 | { label: 'F', value: 100 } 17 | ]) 18 | 19 | function add(e) { 20 | e.preventDefault() 21 | if (!newLabel.value) return 22 | stats.push({ 23 | label: newLabel.value, 24 | value: 100 25 | }) 26 | newLabel.value = '' 27 | } 28 | 29 | function remove(stat) { 30 | if (stats.length > 3) { 31 | stats.splice(stats.indexOf(stat), 1) 32 | } else { 33 | alert("نمی‌توانید بیشتر حذف کنید!") 34 | } 35 | } 36 | 37 | return { 38 | newLabel, 39 | stats, 40 | add, 41 | remove 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/examples/src/svg/App/options.js: -------------------------------------------------------------------------------- 1 | import PolyGraph from './PolyGraph.vue' 2 | 3 | export default { 4 | components: { 5 | PolyGraph 6 | }, 7 | data: () => ({ 8 | newLabel: '', 9 | stats: [ 10 | { label: 'A', value: 100 }, 11 | { label: 'B', value: 100 }, 12 | { label: 'C', value: 100 }, 13 | { label: 'D', value: 100 }, 14 | { label: 'E', value: 100 }, 15 | { label: 'F', value: 100 } 16 | ] 17 | }), 18 | methods: { 19 | add(e) { 20 | e.preventDefault() 21 | if (!this.newLabel) return 22 | this.stats.push({ 23 | label: this.newLabel, 24 | value: 100 25 | }) 26 | this.newLabel = '' 27 | }, 28 | remove(stat) { 29 | if (this.stats.length > 3) { 30 | this.stats.splice(this.stats.indexOf(stat), 1) 31 | } else { 32 | alert("نمی‌توانید بیشتر حذف کنید!") 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/examples/src/svg/App/style.css: -------------------------------------------------------------------------------- 1 | polygon { 2 | fill: #42b983; 3 | opacity: 0.75; 4 | } 5 | 6 | circle { 7 | fill: transparent; 8 | stroke: #999; 9 | } 10 | 11 | text { 12 | font-size: 10px; 13 | fill: #666; 14 | } 15 | 16 | label { 17 | display: inline-block; 18 | margin-left: 10px; 19 | width: 20px; 20 | } 21 | 22 | #raw { 23 | position: absolute; 24 | top: 0; 25 | left: 300px; 26 | } 27 | -------------------------------------------------------------------------------- /src/examples/src/svg/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
    7 | 8 | 9 | {{stat.value}} 10 | 11 |
    12 | 13 |
    14 | 15 | 16 |
    17 | 18 |
    {{ stats }}
    19 | -------------------------------------------------------------------------------- /src/examples/src/svg/AxisLabel/composition.js: -------------------------------------------------------------------------------- 1 | import { computed } from 'vue' 2 | import { valueToPoint } from './util.js' 3 | 4 | export default { 5 | props: { 6 | stat: Object, 7 | index: Number, 8 | total: Number 9 | }, 10 | setup(props) { 11 | const point = computed(() => 12 | valueToPoint(+props.stat.value + 10, props.index, props.total) 13 | ) 14 | 15 | return { 16 | point 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/examples/src/svg/AxisLabel/options.js: -------------------------------------------------------------------------------- 1 | import { valueToPoint } from './util.js' 2 | 3 | export default { 4 | props: { 5 | stat: Object, 6 | index: Number, 7 | total: Number 8 | }, 9 | computed: { 10 | point: function () { 11 | return valueToPoint(+this.stat.value + 10, this.index, this.total) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/examples/src/svg/AxisLabel/template.html: -------------------------------------------------------------------------------- 1 | {{stat.label}} 2 | -------------------------------------------------------------------------------- /src/examples/src/svg/PolyGraph/composition.js: -------------------------------------------------------------------------------- 1 | import AxisLabel from './AxisLabel.vue' 2 | import { computed } from 'vue' 3 | import { valueToPoint } from './util.js' 4 | 5 | export default { 6 | components: { 7 | AxisLabel 8 | }, 9 | props: { 10 | stats: Array 11 | }, 12 | setup(props) { 13 | const points = computed(() => { 14 | const total = props.stats.length 15 | return props.stats 16 | .map((stat, i) => { 17 | const { x, y } = valueToPoint(stat.value, i, total) 18 | return `${x},${y}` 19 | }) 20 | .join(' ') 21 | }) 22 | 23 | return { 24 | points 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/examples/src/svg/PolyGraph/options.js: -------------------------------------------------------------------------------- 1 | import AxisLabel from './AxisLabel.vue' 2 | import { valueToPoint } from './util.js' 3 | 4 | export default { 5 | components: { 6 | AxisLabel 7 | }, 8 | props: { 9 | stats: Array 10 | }, 11 | computed: { 12 | // computed یک پراپرتی برای نقاط چند ضلعی 13 | points() { 14 | const total = this.stats.length 15 | return this.stats 16 | .map((stat, i) => { 17 | const { x, y } = valueToPoint(stat.value, i, total) 18 | return `${x},${y}` 19 | }) 20 | .join(' ') 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/examples/src/svg/PolyGraph/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/examples/src/svg/description.txt: -------------------------------------------------------------------------------- 1 | SVG یک نمودار -------------------------------------------------------------------------------- /src/examples/src/svg/util.js: -------------------------------------------------------------------------------- 1 | export function valueToPoint(value, index, total) { 2 | const x = 0 3 | const y = -value * 0.8 4 | const angle = ((Math.PI * 2) / total) * index 5 | const cos = Math.cos(angle) 6 | const sin = Math.sin(angle) 7 | const tx = x * cos - y * sin + 100 8 | const ty = x * sin + y * cos + 100 9 | return { 10 | x: tx, 11 | y: ty 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/examples/src/temperature-converter/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const c = ref(0) 6 | const f = ref(32) 7 | 8 | function setC(e, v = +e.target.value) { 9 | c.value = v 10 | f.value = v * (9 / 5) + 32 11 | } 12 | 13 | function setF(e, v = +e.target.value) { 14 | f.value = v 15 | c.value = (v - 32) * (5 / 9) 16 | } 17 | 18 | return { 19 | c, 20 | f, 21 | setC, 22 | setF 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/examples/src/temperature-converter/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | c: 0, 5 | f: 32 6 | } 7 | }, 8 | methods: { 9 | setC(e, c = +e.target.value) { 10 | this.c = c 11 | this.f = c * (9 / 5) + 32 12 | }, 13 | setF(e, f = +e.target.value) { 14 | this.f = f 15 | this.c = (f - 32) * (5 / 9) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/examples/src/temperature-converter/App/template.html: -------------------------------------------------------------------------------- 1 | Celsius = 2 | Fahrenheit 3 | -------------------------------------------------------------------------------- /src/examples/src/temperature-converter/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#temp -------------------------------------------------------------------------------- /src/examples/src/timer/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, onUnmounted, computed } from 'vue' 2 | export default { 3 | setup() { 4 | const duration = ref(15 * 1000) 5 | const elapsed = ref(0) 6 | 7 | let lastTime 8 | let handle 9 | 10 | const update = () => { 11 | elapsed.value = performance.now() - lastTime 12 | if (elapsed.value >= duration.value) { 13 | cancelAnimationFrame(handle) 14 | } else { 15 | handle = requestAnimationFrame(update) 16 | } 17 | } 18 | 19 | const reset = () => { 20 | elapsed.value = 0 21 | lastTime = performance.now() 22 | update() 23 | } 24 | 25 | const progressRate = computed(() => 26 | Math.min(elapsed.value / duration.value, 1) 27 | ) 28 | 29 | reset() 30 | 31 | onUnmounted(() => { 32 | cancelAnimationFrame(handle) 33 | }) 34 | return { 35 | duration, 36 | elapsed, 37 | progressRate, 38 | reset 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/examples/src/timer/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | duration: 15 * 1000, 5 | elapsed: 0 6 | } 7 | }, 8 | created() { 9 | this.reset() 10 | }, 11 | unmounted() { 12 | cancelAnimationFrame(this.handle) 13 | }, 14 | computed: { 15 | progressRate() { 16 | return Math.min(this.elapsed / this.duration, 1) 17 | } 18 | }, 19 | methods: { 20 | update() { 21 | this.elapsed = performance.now() - this.lastTime 22 | if (this.elapsed >= this.duration) { 23 | cancelAnimationFrame(this.handle) 24 | } else { 25 | this.handle = requestAnimationFrame(this.update) 26 | } 27 | }, 28 | reset() { 29 | this.elapsed = 0 30 | this.lastTime = performance.now() 31 | this.update() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/examples/src/timer/App/style.css: -------------------------------------------------------------------------------- 1 | .elapsed-container { 2 | width: 300px; 3 | } 4 | 5 | .elapsed-bar { 6 | background-color: red; 7 | height: 10px; 8 | } -------------------------------------------------------------------------------- /src/examples/src/timer/App/template.html: -------------------------------------------------------------------------------- 1 | 4 | 5 |
    {{ (elapsed / 1000).toFixed(1) }}s
    6 | 7 |
    8 | Duration: 9 | {{ (duration / 1000).toFixed(1) }}s 10 |
    11 | 12 | -------------------------------------------------------------------------------- /src/examples/src/timer/description.txt: -------------------------------------------------------------------------------- 1 | https://eugenkiss.github.io/7guis/tasks/#timer -------------------------------------------------------------------------------- /src/examples/src/tree/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import TreeItem from './TreeItem.vue' 3 | 4 | export default { 5 | components: { 6 | TreeItem 7 | }, 8 | setup() { 9 | const treeData = ref({ 10 | name: 'درخت من', 11 | children: [ 12 | { name: 'سلام' }, 13 | { name: 'دنیا' }, 14 | { 15 | name: 'پوشه فرزند', 16 | children: [ 17 | { 18 | name: 'پوشه فرزند', 19 | children: [{ name: 'سلام' }, { name: 'دنیا' }] 20 | }, 21 | { name: 'سلام' }, 22 | { name: 'دنیا' }, 23 | { 24 | name: 'پوشه فرزند', 25 | children: [{ name: 'سلام' }, { name: 'دنیا' }] 26 | } 27 | ] 28 | } 29 | ] 30 | }) 31 | 32 | return { 33 | treeData 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/examples/src/tree/App/options.js: -------------------------------------------------------------------------------- 1 | import TreeItem from './TreeItem.vue' 2 | 3 | const treeData = { 4 | name: 'درخت من', 5 | children: [ 6 | { name: 'سلام' }, 7 | { name: 'دنیا' }, 8 | { 9 | name: 'پوشه فرزند', 10 | children: [ 11 | { 12 | name: 'پوشه فرزند', 13 | children: [{ name: 'سلام' }, { name: 'دنیا' }] 14 | }, 15 | { name: 'سلام' }, 16 | { name: 'دنیا' }, 17 | { 18 | name: 'پوشه فرزند', 19 | children: [{ name: 'سلام' }, { name: 'دنیا' }] 20 | } 21 | ] 22 | } 23 | ] 24 | } 25 | 26 | export default { 27 | components: { 28 | TreeItem 29 | }, 30 | data() { 31 | return { 32 | treeData 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/examples/src/tree/App/style.css: -------------------------------------------------------------------------------- 1 | .item { 2 | cursor: pointer; 3 | line-height: 1.5; 4 | } 5 | .bold { 6 | font-weight: bold; 7 | } -------------------------------------------------------------------------------- /src/examples/src/tree/App/template.html: -------------------------------------------------------------------------------- 1 |
      2 | 3 |
    4 | -------------------------------------------------------------------------------- /src/examples/src/tree/TreeItem/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | 3 | export default { 4 | name: 'TreeItem', // این آیتم برای رندر بازگشتی کامپوننت ضروری است. 5 | props: { 6 | model: Object 7 | }, 8 | setup(props) { 9 | const isOpen = ref(false) 10 | const isFolder = computed(() => { 11 | return props.model.children && props.model.children.length 12 | }) 13 | 14 | function toggle() { 15 | isOpen.value = !isOpen.value 16 | } 17 | 18 | function changeType() { 19 | if (!isFolder.value) { 20 | props.model.children = [] 21 | addChild() 22 | isOpen.value = true 23 | } 24 | } 25 | 26 | function addChild() { 27 | props.model.children.push({ name: 'مورد جدید' }) 28 | } 29 | 30 | return { 31 | isOpen, 32 | isFolder, 33 | toggle, 34 | changeType, 35 | addChild 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/examples/src/tree/TreeItem/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'TreeItem', // این آیتم برای رندر بازگشتی کامپوننت ضروری است. 3 | props: { 4 | model: Object 5 | }, 6 | data() { 7 | return { 8 | isOpen: false 9 | } 10 | }, 11 | computed: { 12 | isFolder() { 13 | return this.model.children && this.model.children.length 14 | } 15 | }, 16 | methods: { 17 | toggle() { 18 | if (this.isFolder) { 19 | this.isOpen = !this.isOpen 20 | } 21 | }, 22 | changeType() { 23 | if (!this.isFolder) { 24 | this.model.children = [] 25 | this.addChild() 26 | this.isOpen = true 27 | } 28 | }, 29 | addChild() { 30 | this.model.children.push({ 31 | name: 'مورد جدید' 32 | }) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/examples/src/tree/TreeItem/template.html: -------------------------------------------------------------------------------- 1 |
  • 2 |
    6 | {{ model.name }} 7 | [{{ isOpen ? '-' : '+' }}] 8 |
    9 |
      10 | 14 | 18 | 19 |
    • +
    • 20 |
    21 |
  • -------------------------------------------------------------------------------- /src/examples/src/tree/description.txt: -------------------------------------------------------------------------------- 1 | یک کامپوننت درختی تو در تو که به صورت بازگشتی خودش را رندر می کند. 2 | می‌توانید بر روی یک گزینه دوبار کلیک کنید تا آن را به یک پوشه تبدیل کنید. -------------------------------------------------------------------------------- /src/guide/best-practices/images/AccessibilityChromeDeveloperTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/best-practices/images/AccessibilityChromeDeveloperTools.png -------------------------------------------------------------------------------- /src/guide/best-practices/images/AccessibleARIAdescribedby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/best-practices/images/AccessibleARIAdescribedby.png -------------------------------------------------------------------------------- /src/guide/best-practices/images/AccessibleARIAlabelDevTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/best-practices/images/AccessibleARIAlabelDevTools.png -------------------------------------------------------------------------------- /src/guide/best-practices/images/AccessibleARIAlabelledbyDevTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/best-practices/images/AccessibleARIAlabelledbyDevTools.png -------------------------------------------------------------------------------- /src/guide/best-practices/images/AccessibleLabelChromeDevTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/best-practices/images/AccessibleLabelChromeDevTools.png -------------------------------------------------------------------------------- /src/guide/best-practices/images/AccessiblePlaceholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/best-practices/images/AccessiblePlaceholder.png -------------------------------------------------------------------------------- /src/guide/built-ins/images/transition-classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/built-ins/images/transition-classes.png -------------------------------------------------------------------------------- /src/guide/built-ins/keep-alive-demos/CompA.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /src/guide/built-ins/keep-alive-demos/CompB.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /src/guide/built-ins/keep-alive-demos/SwitchComponent.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 25 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/Basic.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 26 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/BetweenComponents.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/BetweenElements.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 63 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/CssAnimation.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | 17 | 36 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/JsHooks.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 56 | 57 | 66 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/ListBasic.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 31 | 32 | 43 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/ListMove.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 45 | 46 | 65 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/ListStagger.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 63 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/NestedTransitions.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | 17 | 66 | -------------------------------------------------------------------------------- /src/guide/built-ins/transition-demos/SlideFade.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 30 | -------------------------------------------------------------------------------- /src/guide/components/images/named-slots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/components/images/named-slots.png -------------------------------------------------------------------------------- /src/guide/components/images/prop-drilling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/components/images/prop-drilling.png -------------------------------------------------------------------------------- /src/guide/components/images/provide-inject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/components/images/provide-inject.png -------------------------------------------------------------------------------- /src/guide/components/images/slots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/components/images/slots.png -------------------------------------------------------------------------------- /src/guide/essentials/images/components.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/essentials/images/components.png -------------------------------------------------------------------------------- /src/guide/essentials/images/directive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/essentials/images/directive.png -------------------------------------------------------------------------------- /src/guide/essentials/images/lifecycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/essentials/images/lifecycle.png -------------------------------------------------------------------------------- /src/guide/extras/demos/AnimateWatcher.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 25 | 31 | -------------------------------------------------------------------------------- /src/guide/extras/demos/Colors.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 29 | -------------------------------------------------------------------------------- /src/guide/extras/demos/DisabledButton.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 25 | 54 | -------------------------------------------------------------------------------- /src/guide/extras/demos/SpreadSheet.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 26 | 27 | 47 | -------------------------------------------------------------------------------- /src/guide/extras/demos/SpreadSheetCell.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 30 | 31 | 54 | -------------------------------------------------------------------------------- /src/guide/extras/demos/spreadSheetStore.js: -------------------------------------------------------------------------------- 1 | import { reactive } from 'vue' 2 | 3 | const COLS = 3 4 | const ROWS = 3 5 | 6 | export const cells = reactive( 7 | Array.from(Array(COLS).keys()).map((i) => 8 | Array.from(Array(ROWS).keys()).map((i) => '') 9 | ) 10 | ) 11 | 12 | // initial state for demo 13 | cells[0][0] = '1' 14 | cells[0][1] = '2' 15 | cells[0][2] = '= A0 + A1' 16 | 17 | // adapted from https://codesandbox.io/s/jotai-7guis-task7-cells-mzoit?file=/src/atoms.ts 18 | // by @dai-shi 19 | export function evalCell(exp) { 20 | if (!exp.startsWith('=')) { 21 | return exp 22 | } 23 | 24 | // = A1 + B2 ---> get(0,1) + get(1,2) 25 | exp = exp 26 | .slice(1) 27 | .replace( 28 | /\b([A-Z])(\d{1,2})\b/g, 29 | (_, c, r) => `get(${c.charCodeAt(0) - 65},${r})` 30 | ) 31 | 32 | try { 33 | return new Function('get', `return ${exp}`)(getCellValue) 34 | } catch (e) { 35 | return `#ERROR ${e}` 36 | } 37 | } 38 | 39 | function getCellValue(c, r) { 40 | const val = evalCell(cells[c][r]) 41 | const num = Number(val) 42 | return Number.isFinite(num) ? num : val 43 | } 44 | -------------------------------------------------------------------------------- /src/guide/extras/images/composition-api-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/extras/images/composition-api-after.png -------------------------------------------------------------------------------- /src/guide/extras/images/options-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/extras/images/options-api.png -------------------------------------------------------------------------------- /src/guide/extras/images/render-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/extras/images/render-pipeline.png -------------------------------------------------------------------------------- /src/guide/reusability/mouse.js: -------------------------------------------------------------------------------- 1 | import { ref, onMounted, onUnmounted } from 'vue' 2 | 3 | export function useMouse() { 4 | const x = ref(0) 5 | const y = ref(0) 6 | 7 | function update(event) { 8 | x.value = event.pageX 9 | y.value = event.pageY 10 | } 11 | 12 | onMounted(() => window.addEventListener('mousemove', update)) 13 | onUnmounted(() => window.removeEventListener('mousemove', update)) 14 | 15 | return { x, y } 16 | } 17 | -------------------------------------------------------------------------------- /src/guide/scaling-up/images/devtools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/scaling-up/images/devtools.png -------------------------------------------------------------------------------- /src/guide/scaling-up/images/state-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/guide/scaling-up/images/state-flow.png -------------------------------------------------------------------------------- /src/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | title: Vue.js - فریمورک پیش‌رونده جاوا اسکریپت 4 | --- 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/partners/[partnerId].md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | footer: false 4 | --- 5 | 6 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/partners/[partnerId].paths.ts: -------------------------------------------------------------------------------- 1 | import partners from './partners.json' 2 | import { normalizeName } from './components/utils' 3 | 4 | export default { 5 | paths: partners.map((p) => { 6 | return { 7 | params: { 8 | partnerId: normalizeName(p.name) 9 | } 10 | } 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/partners/all.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | footer: false 4 | --- 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/partners/components/PartnerAll.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 35 | 36 | 60 | -------------------------------------------------------------------------------- /src/partners/components/PartnerHero.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | -------------------------------------------------------------------------------- /src/partners/components/PartnerJoin.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /src/partners/components/PartnerLanding.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | -------------------------------------------------------------------------------- /src/partners/components/PartnerList.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 25 | -------------------------------------------------------------------------------- /src/partners/components/PartnerLocation.vue: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /src/partners/components/type.ts: -------------------------------------------------------------------------------- 1 | export interface Partner { 2 | name: string 3 | logo: string 4 | hero?: string 5 | flipLogo?: boolean 6 | intro: string 7 | description: string[] 8 | proficiencies: string[] 9 | region: string[] 10 | location: string[] 11 | website: { 12 | text: string 13 | url: string 14 | } 15 | contact?: string 16 | contactPage?: string; 17 | hiring?: string 18 | platinum?: boolean 19 | } 20 | -------------------------------------------------------------------------------- /src/partners/components/utils.ts: -------------------------------------------------------------------------------- 1 | export function track() { 2 | fathom.trackGoal('TTDUIE6G', 0) 3 | } 4 | 5 | export function normalizeName(name: string) { 6 | return name.toLowerCase().replace(/\s+/g, '') 7 | } 8 | 9 | export function getHero(img: string | undefined, name: string) { 10 | return `/images/partners/${img || `${normalizeName(name)}-hero.jpg`}` 11 | } 12 | 13 | export function getLogo(img: string, flip = false) { 14 | if (flip) img = img.replace(/(\.\w+$)/, '-dark$1') 15 | return `/images/partners/${img}` 16 | } 17 | -------------------------------------------------------------------------------- /src/partners/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | footer: false 4 | title: Vue شرکای 5 | --- 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/public/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | X-Frame-Options: ALLOW-FROM https://staging.certification.vuejs.org https://certification.vuejs.org https://certificates.dev https://staging.certificates.dev https://alemira.com https://*.alemira.com 3 | Content-Security-Policy: frame-ancestors https://staging.certification.vuejs.org https://certification.vuejs.org https://certificates.dev https://staging.certificates.dev https://alemira.com https://*.alemira.com 4 | 5 | /assets/* 6 | cache-control: max-age=31536000 7 | cache-control: immutable 8 | -------------------------------------------------------------------------------- /src/public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/logo.png -------------------------------------------------------------------------------- /src/public/images/partners/64robots-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/64robots-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/curotec-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/curotec-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/curotec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/curotec.png -------------------------------------------------------------------------------- /src/public/images/partners/epicmax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/epicmax.png -------------------------------------------------------------------------------- /src/public/images/partners/herodevs-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/herodevs-dark.png -------------------------------------------------------------------------------- /src/public/images/partners/herodevs-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/herodevs-hero.png -------------------------------------------------------------------------------- /src/public/images/partners/herodevs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/herodevs.png -------------------------------------------------------------------------------- /src/public/images/partners/jump24-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/public/images/partners/jump24-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/jump24-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/jump24.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/public/images/partners/monterail-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/monterail-dark.png -------------------------------------------------------------------------------- /src/public/images/partners/monterail-hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/monterail-hero.png -------------------------------------------------------------------------------- /src/public/images/partners/monterail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/monterail.png -------------------------------------------------------------------------------- /src/public/images/partners/passionatepeople-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/passionatepeople-dark.png -------------------------------------------------------------------------------- /src/public/images/partners/passionatepeople-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/passionatepeople-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/passionatepeople.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/passionatepeople.png -------------------------------------------------------------------------------- /src/public/images/partners/proxify-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/proxify-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/redberry-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/redberry-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/redberry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/redberry.png -------------------------------------------------------------------------------- /src/public/images/partners/tighten-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/tighten-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/vehikl-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/vehikl-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/webreinvent-hero.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/webreinvent-hero.jpg -------------------------------------------------------------------------------- /src/public/images/partners/webreinvent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/partners/webreinvent.png -------------------------------------------------------------------------------- /src/public/images/paypal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/images/paypal.png -------------------------------------------------------------------------------- /src/public/logo-uwu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vuejs-translations/docs-fa/d65d2747eb798acecd152f78c6bd55749d3bb242/src/public/logo-uwu.png -------------------------------------------------------------------------------- /src/public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/public/service-worker.js: -------------------------------------------------------------------------------- 1 | // force clearing previous service worker 2 | self.addEventListener('install', function (e) { 3 | self.skipWaiting() 4 | }) 5 | 6 | self.addEventListener('activate', function (e) { 7 | self.registration 8 | .unregister() 9 | .then(function () { 10 | return self.clients.matchAll() 11 | }) 12 | .then(function (clients) { 13 | clients.forEach((client) => client.navigate(client.url)) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /src/tutorial/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | page: true 3 | title: آموزش 4 | sidebar: false 5 | aside: false 6 | footer: false 7 | returnToTop: false 8 | --- 9 | 10 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/tutorial/src/step-1/App/template.html: -------------------------------------------------------------------------------- 1 |

    ! سلام دنیا

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-10/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const todoId = ref(1) 6 | const todoData = ref(null) 7 | 8 | async function fetchData() { 9 | todoData.value = null 10 | const res = await fetch( 11 | `https://jsonplaceholder.typicode.com/todos/${todoId.value}` 12 | ) 13 | todoData.value = await res.json() 14 | } 15 | 16 | fetchData() 17 | 18 | return { 19 | todoId, 20 | todoData 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/tutorial/src/step-10/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | todoId: 1, 5 | todoData: null 6 | } 7 | }, 8 | methods: { 9 | async fetchData() { 10 | this.todoData = null 11 | const res = await fetch( 12 | `https://jsonplaceholder.typicode.com/todos/${this.todoId}` 13 | ) 14 | this.todoData = await res.json() 15 | } 16 | }, 17 | mounted() { 18 | this.fetchData() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/tutorial/src/step-10/App/template.html: -------------------------------------------------------------------------------- 1 |

    Todo id: {{ todoId }}

    2 | 3 |

    Loading...

    4 |
    {{ todoData }}
    5 | -------------------------------------------------------------------------------- /src/tutorial/src/step-10/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const todoId = ref(1) 6 | const todoData = ref(null) 7 | 8 | async function fetchData() { 9 | todoData.value = null 10 | const res = await fetch( 11 | `https://jsonplaceholder.typicode.com/todos/${todoId.value}` 12 | ) 13 | todoData.value = await res.json() 14 | } 15 | 16 | fetchData() 17 | 18 | watch(todoId, fetchData) 19 | 20 | return { 21 | todoId, 22 | todoData 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/tutorial/src/step-10/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | todoId: 1, 5 | todoData: null 6 | } 7 | }, 8 | methods: { 9 | async fetchData() { 10 | this.todoData = null 11 | const res = await fetch( 12 | `https://jsonplaceholder.typicode.com/todos/${this.todoId}` 13 | ) 14 | this.todoData = await res.json() 15 | } 16 | }, 17 | mounted() { 18 | this.fetchData() 19 | }, 20 | watch: { 21 | todoId() { 22 | this.fetchData() 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/App/composition.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // register child component 3 | } 4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // register child component 3 | } 4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/ChildComp/template.html: -------------------------------------------------------------------------------- 1 |

    A Child Component!

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import ChildComp from './ChildComp.vue' 2 | 3 | export default { 4 | components: { 5 | ChildComp 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | import ChildComp from './ChildComp.vue' 2 | 3 | export default { 4 | components: { 5 | ChildComp 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tutorial/src/step-11/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import ChildComp from './ChildComp.vue' 3 | 4 | export default { 5 | components: { 6 | ChildComp 7 | }, 8 | setup() { 9 | const greeting = ref('Hello from parent') 10 | 11 | return { 12 | greeting 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/App/options.js: -------------------------------------------------------------------------------- 1 | import ChildComp from './ChildComp.vue' 2 | 3 | export default { 4 | components: { 5 | ChildComp 6 | }, 7 | data() { 8 | return { 9 | greeting: 'Hello from parent' 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/ChildComp/composition.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | msg: String 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/ChildComp/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | props: { 3 | msg: String 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/ChildComp/template.html: -------------------------------------------------------------------------------- 1 |

    {{ msg || 'No props passed yet' }}

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-12/description.md: -------------------------------------------------------------------------------- 1 | # Props {#props} 2 | 3 | کامپوننت فرزند می‌تواند ورودی از والد را از طریق props دریافت کند. ابتدا باید **props** دریافتی خود را اعلام کند: 4 | 5 |
    6 |
    7 | 8 | ```vue 9 | 10 | 15 | ``` 16 | 17 | توجه داشته باشید `defineProps()‎` یک ماکرو زمان کامپایل است و نیازی به import شدن ندارد. هنگامی که تعریف شد `msg` می‌تواند در تمپلیت کامپوننت فرزند استفاده شود. همچنین می‌توان با استفاده از آبجکت برگردانده شده از `defineProps()‎` به آن در جاوااسکریپت دسترسی پیدا کرد. 18 | 19 |
    20 | 21 |
    22 | 23 | ```js 24 | // in child component 25 | export default { 26 | props: { 27 | msg: String 28 | }, 29 | setup(props) { 30 | // access props.msg 31 | } 32 | } 33 | ``` 34 | 35 | هنگامی که تعریف شد، پِراپ `msg` روی `this` قرار گرفته و می‌تواند در تمپلیت کامپوننت فرزند استفاده شود. propهای دریافت شده به عنوان اولین آرگومان به `setup()‎` پاس داده می‌شوند. 36 | 37 |
    38 | 39 |
    40 | 41 |
    42 | 43 | ```js 44 | // in child component 45 | export default { 46 | props: { 47 | msg: String 48 | } 49 | } 50 | ``` 51 | 52 | هنگامی که تعریف شد، پِراپ `msg` روی `this` قرار گرفته و می‌تواند در قالب کامپوننت فرزند استفاده شود. 53 | 54 |
    55 | 56 | والد می‌تواند prop را مانند اتریبیوت‌ها به فرزند پاس دهد. برای پاس دادن یک داده داینامیک، می‌توانیم از سینتکس `v-bind` هم استفاده کنیم: 57 | 58 |
    59 | 60 | ```vue-html 61 | 62 | ``` 63 | 64 |
    65 |
    66 | 67 | ```vue-html 68 | 69 | ``` 70 | 71 |
    72 | 73 | حالا خودتان در ادیتور امتحان کنید. 74 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import ChildComp from './ChildComp.vue' 3 | 4 | export default { 5 | components: { 6 | ChildComp 7 | }, 8 | setup() { 9 | const childMsg = ref('No child msg yet') 10 | 11 | return { 12 | childMsg 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/App/options.js: -------------------------------------------------------------------------------- 1 | import ChildComp from './ChildComp.vue' 2 | 3 | export default { 4 | components: { 5 | ChildComp 6 | }, 7 | data() { 8 | return { 9 | childMsg: 'No child msg yet' 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/App/template.html: -------------------------------------------------------------------------------- 1 | 2 |

    {{ childMsg }}

    3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/ChildComp/composition.js: -------------------------------------------------------------------------------- 1 | export default { 2 | emits: ['response'], 3 | setup(props, { emit }) { 4 | emit('response', 'hello from child') 5 | return {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/ChildComp/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | emits: ['response'], 3 | created() { 4 | this.$emit('response', 'hello from child') 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/ChildComp/template.html: -------------------------------------------------------------------------------- 1 |

    Child component

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | 2 |

    {{ childMsg }}

    3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-13/description.md: -------------------------------------------------------------------------------- 1 | # Emits {#emits} 2 | 3 | علاوه بر دریافت props یک کامپوننت فرزند همچنین می‌تواند رویدادهایی را به والد ارسال کند: 4 | 5 |
    6 |
    7 | 8 | ```vue 9 | 16 | ``` 17 | 18 |
    19 | 20 |
    21 | 22 | ```js 23 | export default { 24 | // شده emit تعریف رویدادهای 25 | emits: ['response'], 26 | setup(props, { emit }) { 27 | // با آرگومان emit 28 | emit('response', 'hello from child') 29 | } 30 | } 31 | ``` 32 | 33 |
    34 | 35 |
    36 | 37 |
    38 | 39 | ```js 40 | export default { 41 | // شده emit تعریف رویدادهای 42 | emits: ['response'], 43 | created() { 44 | // با آرگومان emit 45 | this.$emit('response', 'hello from child') 46 | } 47 | } 48 | ``` 49 | 50 |
    51 | 52 | اولین آرگومان `this.$emit()‎``emit()‎` نام رویداد است. هر آرگومان اضافی به listener رویداد پاس داده می‌شود. 53 | 54 | والد می‌تواند با استفاده از `v-on` به رویدادهای ارسال شده از فرزند گوش دهد - در اینجا handler آرگومان اضافی را از تابع emit فرزند دریافت کرده و آن را به state محلی `childMsg` اختصاص می‌دهد: 55 | 56 |
    57 | 58 | ```vue-html 59 | 60 | ``` 61 | 62 |
    63 |
    64 | 65 | ```vue-html 66 | 67 | ``` 68 | 69 |
    70 | 71 | حالا خودتان در ادیتور امتحان کنید. 72 | -------------------------------------------------------------------------------- /src/tutorial/src/step-14/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import ChildComp from './ChildComp.vue' 3 | 4 | export default { 5 | components: { 6 | ChildComp 7 | }, 8 | setup() { 9 | const msg = ref('from parent') 10 | 11 | return { 12 | msg 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/tutorial/src/step-14/App/options.js: -------------------------------------------------------------------------------- 1 | import ChildComp from './ChildComp.vue' 2 | 3 | export default { 4 | components: { 5 | ChildComp 6 | }, 7 | data() { 8 | return { 9 | msg: 'from parent' 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-14/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-14/ChildComp/template.html: -------------------------------------------------------------------------------- 1 | Fallback content 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-14/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | Message: {{ msg }} 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-14/description.md: -------------------------------------------------------------------------------- 1 | # Slots {#slots} 2 | 3 | علاوه بر اینکه می‌توان داده‌ها را از طریق props به فرزند ارسال کرد، کامپوننت والد همچنین می‌تواند بخشی از تمپلیت را به فرزند از طریق **slots (اسلات‌ها)** ارسال کند: 4 | 5 |
    6 | 7 | ```vue-html 8 | 9 | This is some slot content! 10 | 11 | ``` 12 | 13 |
    14 |
    15 | 16 | ```vue-html 17 | 18 | This is some slot content! 19 | 20 | ``` 21 | 22 |
    23 | 24 | کامپوننت فرزند می‌تواند محتوای slot آمده از والد را با استفاده از عنصر `` به عنوان نقطه‌خروج (outlet) نمایش دهد: 25 | 26 |
    27 | 28 | ```vue-html 29 | 30 | 31 | ``` 32 | 33 |
    34 |
    35 | 36 | ```vue-html 37 | 38 | 39 | ``` 40 | 41 |
    42 | 43 | محتوای داخل `` به عنوان محتوای "جایگزین" (fallback) تلقی می‌شود: اگر والد محتوای اسلاتی ارسال نکرده باشد، این محتوا نمایش داده خواهد شد. 44 | 45 | ```vue-html 46 | Fallback content 47 | ``` 48 | 49 | در حال حاضر ما هیچ محتوایی تحت اسلات به `` ارسال نمی‌کنیم، بنابراین شما باید محتوای جایگزین را ببینید. بیایید محتوای از جنس slot را به فرزند ارائه دهیم و در عین حال از state تعریف شده `msg` والد استفاده کنیم. 50 | -------------------------------------------------------------------------------- /src/tutorial/src/step-15/App/composition.js: -------------------------------------------------------------------------------- 1 | import JSConfetti from 'js-confetti' 2 | 3 | const confetti = new JSConfetti() 4 | 5 | export default { 6 | setup() { 7 | function showConfetti() { 8 | confetti.addConfetti() 9 | } 10 | 11 | showConfetti() 12 | 13 | return { 14 | showConfetti 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/tutorial/src/step-15/App/options.js: -------------------------------------------------------------------------------- 1 | import JSConfetti from 'js-confetti' 2 | 3 | const confetti = new JSConfetti() 4 | 5 | export default { 6 | mounted() { 7 | this.showConfetti() 8 | }, 9 | methods: { 10 | showConfetti() { 11 | confetti.addConfetti() 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/tutorial/src/step-15/App/style.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | text-align: center; 3 | cursor: pointer; 4 | margin-top: 3em; 5 | } 6 | 7 | p { 8 | text-align: center; 9 | } -------------------------------------------------------------------------------- /src/tutorial/src/step-15/App/template.html: -------------------------------------------------------------------------------- 1 |

    🎉 Congratulations!

    2 |

    تبریک به شما، تبریک به خودم، تبریک به همه

    3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-15/description.md: -------------------------------------------------------------------------------- 1 | # You Did It {#you-did-it} 2 | 3 | شما آموزش را تمام کرده‌اید! 4 | 5 | در این نقطه، شما باید دانش ابتدایی خوبی از نحوه کار با Vue داشته باشید. با این حال، ما برای افزایش سرعت بسیاری از موارد را پوشش دادیم و جزئیات را نادیده گرفتیم، بنابراین حتماً به یادگیری ادامه دهید! برای مرحله بعدی، می‌توانید: 6 | 7 | - با استفاده از [شروع سریع](/guide/quick-start)، یک پروژه Vue واقعی را بر روی دستگاه خود ستاپ کنید. 8 | 9 | - به [راهنمای اصلی](/guide/essentials/application) سر بزیند، که تمام موضوعاتی که تا به اینجا یاد گرفتیم را با جزئیات بیشتر و موارد دیگر آموزش می‌دهد. 10 | 11 | - به مثال‌های عملی بیشتری در [مثال‌ها](/examples/) مراجعه کنید. 12 | 13 | ما نمی‌توانیم صبر کنیم تا ببینیم در آینده چه چیزی خواهید ساخت! 14 | -------------------------------------------------------------------------------- /src/tutorial/src/step-15/import-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "js-confetti": "https://cdn.jsdelivr.net/npm/js-confetti/+esm" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/tutorial/src/step-2/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | // منطق کامپوننت 6 | // تعریف کنید reactive چند 7 | 8 | return { 9 | // درمعرض تمپلیت قرار می‌گیرد 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-2/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // آپشن‌های کامپوننت 3 | // تعریف کنید reactive های state 4 | } 5 | -------------------------------------------------------------------------------- /src/tutorial/src/step-2/App/template.html: -------------------------------------------------------------------------------- 1 |

    Make me dynamic!

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-2/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { reactive, ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const counter = reactive({ count: 0 }) 6 | const message = ref('Hello World!') 7 | 8 | return { 9 | counter, 10 | message 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/tutorial/src/step-2/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | message: 'Hello World!', 5 | counter: { 6 | count: 0 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/tutorial/src/step-2/_hint/App/template.html: -------------------------------------------------------------------------------- 1 |

    {{ message }}

    2 |

    Count is: {{ counter.count }}

    3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-3/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const titleClass = ref('title') 6 | 7 | return { 8 | titleClass 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/tutorial/src/step-3/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | titleClass: 'title' 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tutorial/src/step-3/App/style.css: -------------------------------------------------------------------------------- 1 | .title { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-3/App/template.html: -------------------------------------------------------------------------------- 1 |

    Make me red

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-3/_hint/App/template.html: -------------------------------------------------------------------------------- 1 |

    Make me red

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-3/description.md: -------------------------------------------------------------------------------- 1 | # Attribute Bindings {#attribute-bindings} 2 | 3 | در Vue، از علامت `{{}}` فقط برای نمایش متن استفاده می‌شود. برای اتصال دادن یک اتریبیوت HTML به یک مقدار پویا (مثل یک متغیر)، از دستورالعمل `v-bind` استفاده می‌کنیم: 4 | 5 | ```vue-html 6 |
    7 | ``` 8 | 9 | یک directive صفت ویژه‌ای است که با پیشوند `v-‎` شروع می‌شود. آنها بخشی از سینتکس تمپلیت Vue هستند. مشابه `{{}}`، مقادیر directive عبارت‌های جاوااسکریپت هستند که به state کامپوننت‌ها دسترسی دارند. جزئیات کامل `v-bind` و سینتکس directive ها در راهنما - Template Syntax . 10 | 11 | قسمت بعد از دونقطه (`‎:id`) به عنوان «آرگومان» directive شناخته می‌شود. در اینجا، خاصیت `id` عنصر (اینجا div) با خاصیت `dynamicId` از state کامپوننت Vue همگام‌سازی خواهد شد. 12 | 13 | از آنجا که `v-bind` به طور مکرر استفاده می‌شود، سینتکس مختصر خود را دارد: 14 | 15 | ```vue-html 16 |
    17 | ``` 18 | 19 | اکنون، سعی کنید یک اتصال (binding) `class` پویا به `

    ` اضافه کنید، با استفاده از خاصیت dataref `titleClass` به عنوان مقدار آن. اگر به درستی متصل شده باشد، متن قرمز می‌شود. 20 | -------------------------------------------------------------------------------- /src/tutorial/src/step-4/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const count = ref(0) 6 | 7 | return { 8 | count 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/tutorial/src/step-4/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | count: 0 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tutorial/src/step-4/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-4/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const count = ref(0) 6 | 7 | function increment() { 8 | count.value++ 9 | } 10 | 11 | return { 12 | count, 13 | increment 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tutorial/src/step-4/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | count: 0 5 | } 6 | }, 7 | methods: { 8 | increment() { 9 | this.count++ 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-4/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const text = ref('') 6 | 7 | function onInput(e) { 8 | text.value = e.target.value 9 | } 10 | 11 | return { 12 | text, 13 | onInput 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | text: '' 5 | } 6 | }, 7 | methods: { 8 | onInput(e) { 9 | this.text = e.target.value 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/App/template.html: -------------------------------------------------------------------------------- 1 | 2 |

    {{ text }}

    3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const text = ref('') 6 | 7 | return { 8 | text 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | text: '' 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | 2 |

    {{ text }}

    3 | -------------------------------------------------------------------------------- /src/tutorial/src/step-5/description.md: -------------------------------------------------------------------------------- 1 | # Form Bindings {#form-bindings} 2 | 3 | با استفاده همزمان از `v-bind` و `v-on`، می‌توانیم یک ارتباط دوطرفه روی عناصر ورودی فرم ایجاد کنیم: 4 | 5 | ```vue-html 6 | 7 | ``` 8 | 9 |
    10 | 11 | ```js 12 | methods: { 13 | onInput(e) { 14 | // را DOM یک هندلر رویداد v-on 15 | // به عنوان آرگومان دریافت می‌کند 16 | this.text = e.target.value 17 | } 18 | } 19 | ``` 20 | 21 |
    22 | 23 |
    24 | 25 | ```js 26 | function onInput(e) { 27 | // را DOM یک هندلر رویداد v-on 28 | // به عنوان آرگومان دریافت می‌کند 29 | text.value = e.target.value 30 | } 31 | ``` 32 | 33 |
    34 | 35 | در input box تایپ کنید - همزمان با تایپ شما، باید متن در `

    ` را به‌روز شده به شکل لایو ببینید. 36 | 37 | برای ساده کردن ارتباط دوطرفه، Vue یک دایرکتیو به نام `v-model` ارائه می‌دهد که در واقع نوشتار ساده‌تری برای بالا است: 38 | 39 | ```vue-html 40 | 41 | ``` 42 | 43 | `v-model` به صورت خودکار مقدار `` را با state متصل شده همگام‌سازی می‌کند، بنابراین دیگر نیازی به استفاده از یک هندلر رویداد برای این کار نداریم. 44 | 45 | `v-model` نه تنها روی inputهای متنی، بلکه روی سایر انواع input مثل چک باکس‌ها، رادیو باتن‌ها و سلکت‌ها هم کار می‌کند(checkboxes, radio-buttons, select-dropdowns). ما جزئیات بیشتری را در راهنما - Form Bindings پوشش داده‌ایم. 46 | 47 | حالا سعی کنید کد را بازنویسی کنید تا از `v-model` استفاده کند. 48 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const awesome = ref(true) 6 | 7 | function toggle() { 8 | // ... 9 | } 10 | 11 | return { 12 | awesome, 13 | toggle 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | awesome: true 5 | } 6 | }, 7 | methods: { 8 | toggle() { 9 | // ... 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/App/template.html: -------------------------------------------------------------------------------- 1 | 2 |

    Vue is awesome!

    3 |

    Oh no 😢

    4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const awesome = ref(true) 6 | 7 | function toggle() { 8 | awesome.value = !awesome.value 9 | } 10 | 11 | return { 12 | awesome, 13 | toggle 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | data() { 3 | return { 4 | awesome: true 5 | } 6 | }, 7 | methods: { 8 | toggle() { 9 | this.awesome = !this.awesome 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/_hint/App/template.html: -------------------------------------------------------------------------------- 1 | 2 |

    Vue is awesome!

    3 |

    Oh no 😢

    4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-6/description.md: -------------------------------------------------------------------------------- 1 | # Conditional Rendering {#conditional-rendering} 2 | 3 | می‌توانیم از دایرکتیوی به نام `v-if` برای نمایش یک عنصر به صورت شرطی استفاده کنیم: 4 | 5 | ```vue-html 6 |

    Vue is awesome!

    7 | ``` 8 | 9 | این `

    ` فقط در صورتی نمایش داده می‌شود که مقدار `awesome` حاوی یک مقدار [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) باشد. اگر مقدار `awesome` به یک مقدار [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy) تغییر کند، از DOM حذف می‌شود. 10 | 11 | همچنین می‌توانیم از `v-else` و `v-else-if` برای نشان دادن شاخه‌های دیگر شرط استفاده کنیم: 12 | 13 | ```vue-html 14 |

    Vue is awesome!

    15 |

    Oh no 😢

    16 | ``` 17 | 18 | در حال حاضر نمونه کد، همزمان هر دو `

    ` را نمایش می‌دهد و دکمه هیچ عملی انجام نمی‌دهد. برای اینکه بتوانیم از دکمه برای تغییر میان آن‌ها استفاده کنیم، باید `v-if` و `v-else` را به آن‌ها اضافه کنیم و متد `toggle()‎` را پیاده‌سازی کنیم. 19 | 20 | جزئیات بیشتر در مورد `v-if`: راهنما - Conditional Rendering 21 | -------------------------------------------------------------------------------- /src/tutorial/src/step-7/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | // give each todo a unique id 6 | let id = 0 7 | 8 | const newTodo = ref('') 9 | const todos = ref([ 10 | { id: id++, text: 'Learn HTML' }, 11 | { id: id++, text: 'Learn JavaScript' }, 12 | { id: id++, text: 'Learn Vue' } 13 | ]) 14 | 15 | function addTodo() { 16 | // ... 17 | newTodo.value = '' 18 | } 19 | 20 | function removeTodo(todo) { 21 | // ... 22 | } 23 | 24 | return { 25 | newTodo, 26 | todos, 27 | addTodo, 28 | removeTodo 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/tutorial/src/step-7/App/options.js: -------------------------------------------------------------------------------- 1 | // give each todo a unique id 2 | let id = 0 3 | 4 | export default { 5 | data() { 6 | return { 7 | newTodo: '', 8 | todos: [ 9 | { id: id++, text: 'Learn HTML' }, 10 | { id: id++, text: 'Learn JavaScript' }, 11 | { id: id++, text: 'Learn Vue' } 12 | ] 13 | } 14 | }, 15 | methods: { 16 | addTodo() { 17 | // ... 18 | this.newTodo = '' 19 | }, 20 | removeTodo(todo) { 21 | // ... 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/tutorial/src/step-7/App/template.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |
      6 |
    • 7 | {{ todo.text }} 8 | 9 |
    • 10 |
    11 | -------------------------------------------------------------------------------- /src/tutorial/src/step-7/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | // give each todo a unique id 6 | let id = 0 7 | 8 | const newTodo = ref('') 9 | const todos = ref([ 10 | { id: id++, text: 'Learn HTML' }, 11 | { id: id++, text: 'Learn JavaScript' }, 12 | { id: id++, text: 'Learn Vue' } 13 | ]) 14 | 15 | function addTodo() { 16 | todos.value.push({ id: id++, text: newTodo.value }) 17 | newTodo.value = '' 18 | } 19 | 20 | function removeTodo(todo) { 21 | todos.value = todos.value.filter((t) => t !== todo) 22 | } 23 | 24 | return { 25 | newTodo, 26 | todos, 27 | addTodo, 28 | removeTodo 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/tutorial/src/step-7/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | // give each todo a unique id 2 | let id = 0 3 | 4 | export default { 5 | data() { 6 | return { 7 | newTodo: '', 8 | todos: [ 9 | { id: id++, text: 'Learn HTML' }, 10 | { id: id++, text: 'Learn JavaScript' }, 11 | { id: id++, text: 'Learn Vue' } 12 | ] 13 | } 14 | }, 15 | methods: { 16 | addTodo() { 17 | this.todos.push({ id: id++, text: this.newTodo }) 18 | this.newTodo = '' 19 | }, 20 | removeTodo(todo) { 21 | this.todos = this.todos.filter((t) => t !== todo) 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | let id = 0 6 | 7 | const newTodo = ref('') 8 | const hideCompleted = ref(false) 9 | const todos = ref([ 10 | { id: id++, text: 'Learn HTML', done: true }, 11 | { id: id++, text: 'Learn JavaScript', done: true }, 12 | { id: id++, text: 'Learn Vue', done: false } 13 | ]) 14 | 15 | function addTodo() { 16 | todos.value.push({ id: id++, text: newTodo.value, done: false }) 17 | newTodo.value = '' 18 | } 19 | 20 | function removeTodo(todo) { 21 | todos.value = todos.value.filter((t) => t !== todo) 22 | } 23 | 24 | return { 25 | newTodo, 26 | hideCompleted, 27 | todos, 28 | addTodo, 29 | removeTodo 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/App/options.js: -------------------------------------------------------------------------------- 1 | let id = 0 2 | 3 | export default { 4 | data() { 5 | return { 6 | newTodo: '', 7 | hideCompleted: false, 8 | todos: [ 9 | { id: id++, text: 'Learn HTML', done: true }, 10 | { id: id++, text: 'Learn JavaScript', done: true }, 11 | { id: id++, text: 'Learn Vue', done: false } 12 | ] 13 | } 14 | }, 15 | computed: { 16 | // ... 17 | }, 18 | methods: { 19 | addTodo() { 20 | this.todos.push({ id: id++, text: this.newTodo, done: false }) 21 | this.newTodo = '' 22 | }, 23 | removeTodo(todo) { 24 | this.todos = this.todos.filter((t) => t !== todo) 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/App/style.css: -------------------------------------------------------------------------------- 1 | .done { 2 | text-decoration: line-through; 3 | } 4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/App/template.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |
      6 |
    • 7 | 8 | {{ todo.text }} 9 | 10 |
    • 11 |
    12 | 15 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | let id = 0 6 | 7 | const newTodo = ref('') 8 | const hideCompleted = ref(false) 9 | const todos = ref([ 10 | { id: id++, text: 'Learn HTML', done: true }, 11 | { id: id++, text: 'Learn JavaScript', done: true }, 12 | { id: id++, text: 'Learn Vue', done: false } 13 | ]) 14 | 15 | const filteredTodos = computed(() => { 16 | return hideCompleted.value 17 | ? todos.value.filter((t) => !t.done) 18 | : todos.value 19 | }) 20 | 21 | function addTodo() { 22 | todos.value.push({ id: id++, text: newTodo.value, done: false }) 23 | newTodo.value = '' 24 | } 25 | 26 | function removeTodo(todo) { 27 | todos.value = todos.value.filter((t) => t !== todo) 28 | } 29 | 30 | return { 31 | newTodo, 32 | hideCompleted, 33 | todos, 34 | filteredTodos, 35 | addTodo, 36 | removeTodo 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | let id = 0 2 | 3 | export default { 4 | data() { 5 | return { 6 | newTodo: '', 7 | hideCompleted: false, 8 | todos: [ 9 | { id: id++, text: 'Learn HTML', done: true }, 10 | { id: id++, text: 'Learn JavaScript', done: true }, 11 | { id: id++, text: 'Learn Vue', done: false } 12 | ] 13 | } 14 | }, 15 | computed: { 16 | filteredTodos() { 17 | return this.hideCompleted 18 | ? this.todos.filter((t) => !t.done) 19 | : this.todos 20 | } 21 | }, 22 | methods: { 23 | addTodo() { 24 | this.todos.push({ id: id++, text: this.newTodo, done: false }) 25 | this.newTodo = '' 26 | }, 27 | removeTodo(todo) { 28 | this.todos = this.todos.filter((t) => t !== todo) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/tutorial/src/step-8/_hint/App/template.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |
      6 |
    • 7 | 8 | {{ todo.text }} 9 | 10 |
    • 11 |
    12 | 15 | -------------------------------------------------------------------------------- /src/tutorial/src/step-9/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const pElementRef = ref(null) 6 | 7 | return { 8 | pElementRef 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/tutorial/src/step-9/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // ... 3 | } 4 | -------------------------------------------------------------------------------- /src/tutorial/src/step-9/App/template.html: -------------------------------------------------------------------------------- 1 |

    Hello

    2 | -------------------------------------------------------------------------------- /src/tutorial/src/step-9/_hint/App/composition.js: -------------------------------------------------------------------------------- 1 | import { ref, onMounted } from 'vue' 2 | 3 | export default { 4 | setup() { 5 | const pElementRef = ref(null) 6 | 7 | onMounted(() => { 8 | pElementRef.value.textContent = 'mounted!' 9 | }) 10 | 11 | return { 12 | pElementRef 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/tutorial/src/step-9/_hint/App/options.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mounted() { 3 | this.$refs.pElementRef.textContent = 'mounted!' 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/tutorial/tutorial.data.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { createMarkdownRenderer } from 'vitepress' 3 | import { readExamples, ExampleData } from '../examples/examples.data' 4 | 5 | export declare const data: Record 6 | 7 | export default { 8 | watch: './src/**', 9 | async load() { 10 | const md = await createMarkdownRenderer(process.cwd(), { 11 | theme: 'github-dark', 12 | }, '/') 13 | const files = readExamples(path.resolve(__dirname, './src')) 14 | for (const step in files) { 15 | const stepFiles = files[step] 16 | const desc = stepFiles['description.md'] as string 17 | if (desc) { 18 | stepFiles['description.md'] = md.render(desc) 19 | } 20 | } 21 | return files 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "esnext", 5 | "module": "esnext", 6 | "moduleResolution": "bundler", 7 | "esModuleInterop": true, 8 | "resolveJsonModule": true, 9 | "allowJs": true, 10 | "strict": true, 11 | "noUnusedLocals": true, 12 | "skipLibCheck": true, 13 | "jsx": "preserve", 14 | "baseUrl": ".", 15 | "paths": { 16 | "@theme/*": [".vitepress/theme/*"] 17 | } 18 | }, 19 | "include": ["env.d.ts", "src/**/*", ".vitepress/**/*"] 20 | } 21 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://openapi.vercel.sh/vercel.json", 3 | "headers": [ 4 | { 5 | "source": "/assets/(.*)", 6 | "headers": [ 7 | { 8 | "key": "Cache-Control", 9 | "value": "max-age=31536000, immutable" 10 | } 11 | ] 12 | }, 13 | { 14 | "source": "/(.*).png", 15 | "headers": [ 16 | { 17 | "key": "Cache-Control", 18 | "value": "max-age=604800, immutable" 19 | } 20 | ] 21 | } 22 | ], 23 | "rewrites": [ 24 | { 25 | "source": "/:path*", 26 | "destination": "/:path*.html" 27 | } 28 | ] 29 | } 30 | --------------------------------------------------------------------------------