feat: landing page + smart components
This commit is contained in:
18
assets/js/getLinkTag.js
Normal file
18
assets/js/getLinkTag.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const DEFAULT_TAG = "button"
|
||||
const ANCHOR_TAG = "a"
|
||||
const FRAMEWORK_LINK = "NuxtLink" // or "router-link", "g-link"...
|
||||
|
||||
const getLinkTag = ({ to, blank }) => {
|
||||
if (!to) {
|
||||
return DEFAULT_TAG
|
||||
} else if (blank) {
|
||||
return ANCHOR_TAG
|
||||
} else if (/^\/(?!\/).*$/.test(to)) {
|
||||
// regex101.com/r/LU1iFL/1
|
||||
return FRAMEWORK_LINK
|
||||
} else {
|
||||
return ANCHOR_TAG
|
||||
}
|
||||
}
|
||||
|
||||
export { getLinkTag as default, ANCHOR_TAG, FRAMEWORK_LINK }
|
||||
@@ -149,6 +149,46 @@ hr {
|
||||
@apply my-4;
|
||||
}
|
||||
|
||||
.tippy-popper {
|
||||
.tooltip-theme {
|
||||
@apply bg-secondary;
|
||||
@apply text-primaryLight;
|
||||
@apply text-xs;
|
||||
@apply font-semibold;
|
||||
@apply px-2;
|
||||
@apply px-4;
|
||||
@apply shadow;
|
||||
}
|
||||
|
||||
.popover-theme {
|
||||
@apply bg-primaryLight;
|
||||
@apply text-secondary;
|
||||
@apply p-2;
|
||||
@apply shadow-md;
|
||||
}
|
||||
|
||||
&[x-placement^="top"] .tippy-tooltip .tippy-arrow {
|
||||
border-top-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
&[x-placement^="bottom"] .tippy-tooltip .tippy-arrow {
|
||||
border-bottom-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
&[x-placement^="left"] .tippy-tooltip .tippy-arrow {
|
||||
border-left-color: var(--color-primary-light);
|
||||
}
|
||||
|
||||
&[x-placement^="right"] .tippy-tooltip .tippy-arrow {
|
||||
border-right-color: var(--color-primary-light);
|
||||
}
|
||||
}
|
||||
|
||||
[arrow] > div {
|
||||
@apply flex;
|
||||
@apply w-full;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
@apply z-50;
|
||||
@apply outline-none;
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
|
||||
@mixin lightTheme {
|
||||
// Background color
|
||||
--primary-color: rgba(255, 255, 255, 1);
|
||||
--primary-color: theme("colors.white");
|
||||
// Light Background color
|
||||
--primary-light-color: theme("colors.gray.200");
|
||||
--primary-light-color: theme("colors.gray.50");
|
||||
// Dark Background color
|
||||
--primary-dark-color: rgba(0, 0, 0, 0.02);
|
||||
--primary-dark-color: theme("colors.gray.100");
|
||||
// Text color
|
||||
--secondary-color: theme("colors.gray.600");
|
||||
// Light Text color
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<header class="flex container items-center justify-between p-2 flex-1">
|
||||
<div class="flex items-center relative ml-4">
|
||||
<div class="flex items-center relative ml-2">
|
||||
<nuxt-link :to="localePath('index')">
|
||||
<h1 class="heading logo">Hoppscotch</h1>
|
||||
</nuxt-link>
|
||||
<AppGitHubStarButton class="ml-8" />
|
||||
</div>
|
||||
<div class="flex">
|
||||
<button
|
||||
|
||||
125
components/button/Primary.vue
Normal file
125
components/button/Primary.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<SmartLink
|
||||
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
||||
:exact="exact"
|
||||
:blank="blank"
|
||||
class="
|
||||
inline-flex
|
||||
items-center
|
||||
justify-center
|
||||
py-2
|
||||
font-semibold
|
||||
transition
|
||||
focus:outline-none
|
||||
"
|
||||
:class="[
|
||||
color
|
||||
? `text-${color}-800 bg-${color}-200 hover:text-${color}-900 hover:bg-${color}-300 focus:text-${color}-900 focus:bg-${color}-300`
|
||||
: `text-white bg-accent hover:bg-accentDark focus:bg-accentDark`,
|
||||
label ? 'px-4' : 'px-2',
|
||||
rounded ? 'rounded-full' : 'rounded-lg',
|
||||
{ 'opacity-50 cursor-not-allowed': disabled },
|
||||
{ 'pointer-events-none': loading },
|
||||
{ 'px-4 py-4 text-lg': large },
|
||||
{ 'shadow-lg hover:shadow-xl': shadow },
|
||||
{
|
||||
'text-white bg-gradient-to-tr from-gradientFrom via-gradientVia to-gradientTo':
|
||||
gradient,
|
||||
},
|
||||
]"
|
||||
:disabled="disabled"
|
||||
:tabindex="loading ? '-1' : '0'"
|
||||
>
|
||||
<span
|
||||
v-if="!loading"
|
||||
class="inline-flex items-center justify-center whitespace-nowrap"
|
||||
:class="{ 'flex-row-reverse': reverse }"
|
||||
>
|
||||
<i
|
||||
v-if="icon"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="material-icons"
|
||||
>
|
||||
{{ icon }}
|
||||
</i>
|
||||
<SmartIcon
|
||||
v-if="svg"
|
||||
:name="svg"
|
||||
:class="label ? (reverse ? 'ml-4' : 'mr-4') : ''"
|
||||
class="svg-icons"
|
||||
/>
|
||||
{{ label }}
|
||||
<span v-if="shortkey" class="px-1 ml-2 rounded bg-accentLight">{{
|
||||
shortkey
|
||||
}}</span>
|
||||
</span>
|
||||
<SmartSpinner v-else />
|
||||
</SmartLink>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
exact: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
blank: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
svg: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
large: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
shadow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
reverse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rounded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
gradient: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
shortkey: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
96
components/button/Secondary.vue
Normal file
96
components/button/Secondary.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<SmartLink
|
||||
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
||||
:exact="exact"
|
||||
:blank="blank"
|
||||
class="
|
||||
inline-flex
|
||||
items-center
|
||||
justify-center
|
||||
py-2
|
||||
font-semibold
|
||||
transition
|
||||
focus:outline-none
|
||||
"
|
||||
:class="[
|
||||
color
|
||||
? `text-${color}-500 hover:text-${color}-600 focus:text-${color}-600`
|
||||
: 'hover:text-secondaryDark focus:text-secondaryDark',
|
||||
label ? 'px-3 rounded-lg' : 'px-2 rounded-full',
|
||||
rounded ? 'rounded-full' : 'rounded-lg',
|
||||
{ 'opacity-50 cursor-not-allowed': disabled },
|
||||
{ 'flex-row-reverse': reverse },
|
||||
{
|
||||
'border border-divider hover:border-dividerDark focus:border-dividerDark':
|
||||
outline,
|
||||
},
|
||||
]"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<i
|
||||
v-if="icon"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="material-icons"
|
||||
>
|
||||
{{ icon }}
|
||||
</i>
|
||||
<SmartIcon
|
||||
v-if="svg"
|
||||
:name="svg"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="svg-icons"
|
||||
/>
|
||||
{{ label }}
|
||||
</SmartLink>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
exact: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
blank: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
svg: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
reverse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rounded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
outline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
118
components/landing/Features.vue
Normal file
118
components/landing/Features.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p class="my-4 font-semibold tracking-widest text-center text-accent">
|
||||
FEATURES
|
||||
</p>
|
||||
<h3
|
||||
class="
|
||||
max-w-xl
|
||||
mt-4
|
||||
mb-8
|
||||
text-4xl
|
||||
font-bold
|
||||
leading-tight
|
||||
tracking-tight
|
||||
text-center
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
A technology-first approach to payments and finance
|
||||
</h3>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="(feature, index) in features"
|
||||
:key="`feature-${index}`"
|
||||
class="
|
||||
inline-flex
|
||||
flex-col
|
||||
p-8
|
||||
transition
|
||||
border
|
||||
rounded-lg
|
||||
border-divider
|
||||
"
|
||||
>
|
||||
<i class="text-3xl material-icons text-accent">{{ feature.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2
|
||||
class="
|
||||
mt-4
|
||||
mb-2
|
||||
text-lg
|
||||
font-semibold
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
{{ feature.title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ feature.description }}
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
<NuxtLink :to="feature.link.target" class="link">
|
||||
{{ feature.link.title }}
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
features: [
|
||||
{
|
||||
icon: "offline_bolt",
|
||||
title: "SaaS Executives",
|
||||
description:
|
||||
"Unblock your barriers to new markets to sell software globally, to companies of all sizes, at all different price points, and increase your revenue growth with Kooli’s SaaS Commerce Platform.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "stars",
|
||||
title: "Product Managers",
|
||||
description:
|
||||
"Don’t let your billing stack hold you back. Everything you need to grow your SaaS business. Subscription billing, payments, taxes and more, in one unified platform.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "supervised_user_circle",
|
||||
title: "Creators",
|
||||
description:
|
||||
"Kooli is for artists and creators: writers, designers, software developers, musicians, educators, filmmakers, and anyone in-between. If you make stuff, you can sell that stuff.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "build_circle",
|
||||
title: "Developers",
|
||||
description:
|
||||
"Focus on building, not billing. Save the blood, sweat and tears for your software development, not your billing stack, with Paddle’s subscription and commerce platform.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "monetization_on",
|
||||
title: "Finance",
|
||||
description:
|
||||
"All your payments, subscriptions, taxes, invoices, SaaS metrics, and more in one place. Integrate your product, CRM, and accounting with only one tool, not dozens.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "group_work",
|
||||
title: "Enterprise",
|
||||
description:
|
||||
"Accept payments in 135+ currencies to better serve your international customers with a single integration. No international business entity required. Fees exclude GST.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
168
components/landing/Footer.vue
Normal file
168
components/landing/Footer.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<footer class="flex flex-col px-8 py-16">
|
||||
<nav class="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<SmartLink
|
||||
:to="
|
||||
!$store.state.authUser
|
||||
? localePath('/')
|
||||
: localePath(`/${$store.state.login}`)
|
||||
"
|
||||
class="my-2"
|
||||
>
|
||||
<AppLogo class="h-8" />
|
||||
</SmartLink>
|
||||
<span> Kooli </span>
|
||||
<SmartChangeLanguage />
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<SmartAnchor label="Terms" to="/about/terms" class="footer-nav" />
|
||||
</li>
|
||||
<li>
|
||||
<SmartAnchor
|
||||
label="Privacy"
|
||||
to="/about/privacy"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<SmartColorModePicker />
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Solutions</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.solutions"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Platform</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.platform"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Company</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.company"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
navigation: {
|
||||
solutions: [
|
||||
{
|
||||
name: "SaaS",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Products",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Creators",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Developers",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Finance",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Enterprise",
|
||||
link: "/settings",
|
||||
},
|
||||
],
|
||||
platform: [
|
||||
{
|
||||
name: "Payments",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Subscriptions",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "API",
|
||||
link: "https://docs.kooli.tech/api",
|
||||
},
|
||||
{
|
||||
name: "Guides",
|
||||
link: "https://docs.kooli.tech/guides",
|
||||
},
|
||||
],
|
||||
company: [
|
||||
{
|
||||
name: "About",
|
||||
link: "/about",
|
||||
},
|
||||
{
|
||||
name: "Jobs",
|
||||
link: "/about/jobs",
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
link: "/about/integrations",
|
||||
},
|
||||
{
|
||||
name: "Support",
|
||||
link: "",
|
||||
},
|
||||
{
|
||||
name: "Contact",
|
||||
link: "/about/contact",
|
||||
},
|
||||
{
|
||||
name: "Blog",
|
||||
link: "https://blog.kooli.tech",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer-nav {
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply text-secondaryDark;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div id="globe" class="w-full"></div>
|
||||
<div ref="globe"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -32,12 +32,23 @@ export default {
|
||||
mounted() {
|
||||
this.init()
|
||||
this.animate()
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
() => {
|
||||
this.camera.aspect =
|
||||
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.renderer.setSize(
|
||||
this.$refs.globe.clientWidth,
|
||||
this.$refs.globe.clientHeight
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
const container = document.getElementById("globe")
|
||||
|
||||
this.globe = new ThreeGlobe()
|
||||
.globeImageUrl(texture)
|
||||
.atmosphereColor("#aaaaaa")
|
||||
@@ -56,9 +67,12 @@ export default {
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
alpha: true,
|
||||
})
|
||||
this.renderer.setSize(container.clientWidth, container.clientHeight)
|
||||
this.renderer.setSize(
|
||||
this.$refs.globe.clientWidth,
|
||||
this.$refs.globe.clientHeight
|
||||
)
|
||||
|
||||
container.appendChild(this.renderer.domElement)
|
||||
this.$refs.globe.appendChild(this.renderer.domElement)
|
||||
|
||||
this.scene = new THREE.Scene()
|
||||
this.scene.background = null
|
||||
@@ -67,7 +81,8 @@ export default {
|
||||
this.scene.add(new THREE.DirectionalLight(0xffffff, 0.8))
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera()
|
||||
this.camera.aspect = container.clientWidth / container.clientHeight
|
||||
this.camera.aspect =
|
||||
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.camera.position.z = 300
|
||||
},
|
||||
42
components/landing/Hero.vue
Normal file
42
components/landing/Hero.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="flex p-4 relative overflow-hidden">
|
||||
<div class="relative my-16 z-10 max-w-3xl">
|
||||
<h2
|
||||
class="
|
||||
leading-none
|
||||
tracking-tighter
|
||||
font-semibold
|
||||
text-accent text-5xl
|
||||
md:text-6xl
|
||||
lg:text-8xl
|
||||
"
|
||||
>
|
||||
Open Source
|
||||
</h2>
|
||||
<h3
|
||||
class="
|
||||
text-3xl
|
||||
my-8
|
||||
font-mono
|
||||
text-secondaryDark
|
||||
md:text-4xl
|
||||
lg:text-4xl
|
||||
font-semibold
|
||||
"
|
||||
>
|
||||
API Development Ecosystem
|
||||
</h3>
|
||||
<p class="text-lg my-4 text-secondaryLight max-w-4/5">
|
||||
Millions of developers and companies build, ship, and maintain their
|
||||
APIs on Hoppscotch — the largest and most advanced development platform
|
||||
in the world.
|
||||
</p>
|
||||
<div class="my-8 flex items-center">
|
||||
<button class="button rounded text-xl">Get Started</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
|
||||
<LandingGlobe class="h-64 w-full sm:h-72 md:h-96 lg:w-full lg:h-full" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
142
components/landing/Package.vue
Normal file
142
components/landing/Package.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<div
|
||||
class="inline-flex flex-col transition border rounded-lg border-divider"
|
||||
>
|
||||
<div class="flex flex-col p-8">
|
||||
<p class="font-semibold tracking-widest text-accent">API</p>
|
||||
<h3
|
||||
class="
|
||||
max-w-xl
|
||||
my-4
|
||||
text-4xl
|
||||
font-semibold
|
||||
leading-tight
|
||||
tracking-tight
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
Powerful and easy-to-use APIs
|
||||
</h3>
|
||||
<p class="my-4 text-lg md:w-4/5">
|
||||
We agonize over the right abstractions so your teams don’t need to
|
||||
stitch together disparate systems or spend months integrating
|
||||
payments functionality.
|
||||
</p>
|
||||
<div class="flex mt-4">
|
||||
<ButtonPrimary
|
||||
to="https://docs.kooli.tech/guides"
|
||||
label="Read the docs"
|
||||
icon="chevron_right"
|
||||
reverse
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<div
|
||||
v-for="(api, index) in apis"
|
||||
:key="`api-${index}`"
|
||||
class="inline-flex flex-col p-8"
|
||||
>
|
||||
<i class="text-3xl material-icons text-accent">{{ api.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2
|
||||
class="
|
||||
mt-4
|
||||
mb-2
|
||||
text-lg
|
||||
font-semibold
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
{{ api.title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ api.description }}
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
<SmartLink :to="api.link.target" class="link">
|
||||
{{ api.link.title }}
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</SmartLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
inline-flex
|
||||
flex-col
|
||||
p-4
|
||||
transition
|
||||
border
|
||||
rounded-lg
|
||||
border-divider
|
||||
bg-primaryLight
|
||||
"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="w-3 h-3 mr-2 bg-red-500 rounded-full"></div>
|
||||
<div class="w-3 h-3 mr-2 bg-yellow-500 rounded-full"></div>
|
||||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
</div>
|
||||
<div class="flex flex-1 p-4 overflow-auto whitespace-pre">
|
||||
<pre>
|
||||
/*
|
||||
** TailwindCSS Configuration File
|
||||
**
|
||||
** Docs: https://tailwindcss.com/docs/configuration
|
||||
** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js
|
||||
*/
|
||||
module.exports = {
|
||||
future: {
|
||||
removeDeprecatedGapUtilities: true,
|
||||
purgeLayersByDefault: true,
|
||||
},
|
||||
experimental: "all",
|
||||
dark: "class",
|
||||
corePlugins: {
|
||||
float: false,
|
||||
clear: false,
|
||||
transitionDelay: false,
|
||||
skew: false,
|
||||
},
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
apis: [
|
||||
{
|
||||
icon: "layers",
|
||||
title: "Tools for every stack",
|
||||
description:
|
||||
"We offer client and server libraries in everything from React, PHP to .NET and iOS.",
|
||||
link: {
|
||||
title: "See libraries",
|
||||
target: "https://docs.kooli.tech/api",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "extension",
|
||||
title: "Prebuilt integrations",
|
||||
description:
|
||||
"Use integrations for systems like Shopify, PayPal, and more.",
|
||||
link: { title: "Explore partners", target: "/about/integrations" },
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
27
components/landing/Stats.vue
Normal file
27
components/landing/Stats.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="flex p-4 font-mono space-x-16">
|
||||
<div v-for="(stat, index) in stats" :key="`stat-${index}`">
|
||||
<span class="text-xl font-bold">
|
||||
{{ stat.count }}<span class="text-secondaryLight">+</span>
|
||||
</span>
|
||||
<br />
|
||||
<span class="text-sm">
|
||||
{{ stat.audience }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
stats: [
|
||||
{ count: "350k", audience: "Developers" },
|
||||
{ count: "10k", audience: "Organizations" },
|
||||
{ count: "1m", audience: "Requests" },
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
27
components/landing/Users.vue
Normal file
27
components/landing/Users.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p
|
||||
class="mt-4 mb-8 font-semibold tracking-widest text-center text-accent"
|
||||
>
|
||||
EMPOWERING CREATORS
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4 md:grid-cols-4 lg:grid-cols-6">
|
||||
<div
|
||||
v-for="(user, index) in 12"
|
||||
:key="`user-${index}`"
|
||||
class="inline-flex flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img
|
||||
:src="`https://i.pravatar.cc/${Math.floor(
|
||||
Math.random() * (600 - 400 + 1) + 400
|
||||
)}`"
|
||||
alt="Profile picture"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center h-24 w-24"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
74
components/smart/Anchor.vue
Normal file
74
components/smart/Anchor.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<SmartLink
|
||||
:to="`${/^\/(?!\/).*$/.test(to) ? localePath(to) : to}`"
|
||||
:exact="exact"
|
||||
:blank="blank"
|
||||
class="inline-flex items-center justify-center focus:outline-none"
|
||||
:class="[
|
||||
color
|
||||
? `text-${color}-500 transition hover:text-${color}-600 focus:text-${color}-600`
|
||||
: 'transition hover:text-secondaryDark focus:text-secondaryDark',
|
||||
{ 'opacity-50 cursor-not-allowed': disabled },
|
||||
{ 'flex-row-reverse': reverse },
|
||||
]"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<i
|
||||
v-if="icon"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="material-icons"
|
||||
>
|
||||
{{ icon }}
|
||||
</i>
|
||||
<SmartIcon
|
||||
v-if="svg"
|
||||
:name="svg"
|
||||
:class="label ? (reverse ? 'ml-2' : 'mr-2') : ''"
|
||||
class="svg-icons"
|
||||
/>
|
||||
{{ label }}
|
||||
</SmartLink>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
exact: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
blank: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
svg: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
reverse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
51
components/smart/ChangeLanguage.vue
Normal file
51
components/smart/ChangeLanguage.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<span>
|
||||
<tippy
|
||||
ref="language"
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
interactive
|
||||
:animate-fill="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<button
|
||||
v-tippy="{ animateFill: false, theme: 'tooltip' }"
|
||||
:title="$t('choose_language')"
|
||||
>
|
||||
<span class="mr-2 text-lg">
|
||||
{{
|
||||
$i18n.locales.find(({ code }) => code == $i18n.locale).country
|
||||
| formatCountry
|
||||
}}
|
||||
</span>
|
||||
{{ $i18n.locales.find(({ code }) => code == $i18n.locale).name }}
|
||||
</button>
|
||||
</template>
|
||||
<NuxtLink
|
||||
v-for="locale in $i18n.locales.filter(
|
||||
({ code }) => code !== $i18n.locale
|
||||
)"
|
||||
:key="locale.code"
|
||||
class="
|
||||
inline-flex
|
||||
items-center
|
||||
px-4
|
||||
py-2
|
||||
transition
|
||||
rounded-lg
|
||||
hover:bg-accentLight hover:text-secondaryDark
|
||||
focus:bg-accentLight focus:text-secondaryDark focus:outline-none
|
||||
"
|
||||
:to="switchLocalePath(locale.code)"
|
||||
>
|
||||
<span class="mr-2 text-lg">
|
||||
{{ locale.country | formatCountry }}
|
||||
</span>
|
||||
<span class="font-semibold">
|
||||
{{ locale.name }}
|
||||
</span>
|
||||
</NuxtLink>
|
||||
</tippy>
|
||||
</span>
|
||||
</template>
|
||||
67
components/smart/Link.js
Normal file
67
components/smart/Link.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/* Vue 2 Functional Component: https://vuejs.org/v2/guide/render-function.html#Functional-Components */
|
||||
import { mergeData } from "vue-functional-data-merge"
|
||||
import getLinkTag, { ANCHOR_TAG, FRAMEWORK_LINK } from "~/assets/js/getLinkTag"
|
||||
|
||||
const SmartLink = {
|
||||
functional: true,
|
||||
props: {
|
||||
to: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
exact: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
blank: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
// It's a convention to rename `createElement` to `h`
|
||||
render(h, context) {
|
||||
const tag = getLinkTag(context.props)
|
||||
|
||||
// Map our attributes correctly
|
||||
const attrs = {}
|
||||
let on = {}
|
||||
switch (tag) {
|
||||
case ANCHOR_TAG:
|
||||
// Map `to` prop to the correct attribute
|
||||
attrs.href = context.props.to
|
||||
|
||||
// Handle `blank` prop
|
||||
if (context.props.blank) {
|
||||
attrs.target = "_blank"
|
||||
attrs.rel = "noopener"
|
||||
}
|
||||
|
||||
// Transform native events to regular events for HTML anchor tag
|
||||
on = { ...context.data.nativeOn }
|
||||
delete context.data.nativeOn
|
||||
break
|
||||
|
||||
case FRAMEWORK_LINK:
|
||||
// Map `to` prop to the correct attribute
|
||||
attrs.to = context.props.to
|
||||
|
||||
// Handle `exact` prop
|
||||
if (context.props.exact) {
|
||||
attrs.exact = true
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
// Merge our new data with existing ones
|
||||
const data = mergeData(context.data, { attrs, on })
|
||||
|
||||
// Return a new virtual node
|
||||
return h(tag, data, context.children)
|
||||
},
|
||||
}
|
||||
|
||||
export default SmartLink
|
||||
@@ -6,7 +6,7 @@
|
||||
<Pane
|
||||
v-if="!hideNavigationPane"
|
||||
style="width: auto"
|
||||
class="overflow-y-auto bg-primaryDark flex items-stretch"
|
||||
class="overflow-y-auto bg-primaryLight flex items-stretch"
|
||||
>
|
||||
<AppSidenav />
|
||||
</Pane>
|
||||
@@ -30,7 +30,7 @@
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</Pane>
|
||||
<Pane style="height: auto" class="bg-primaryDark">
|
||||
<Pane style="height: auto" class="bg-primaryLight">
|
||||
<button
|
||||
class="button icon"
|
||||
@click="hideNavigationPane = !hideNavigationPane"
|
||||
@@ -120,12 +120,8 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.splitpanes--vertical > .splitpanes__splitter {
|
||||
display: none;
|
||||
width: 0;
|
||||
background: linear-gradient(90deg, #ccc, #111);
|
||||
}
|
||||
.splitpanes--horizontal > .splitpanes__splitter {
|
||||
display: none;
|
||||
height: 0;
|
||||
background: linear-gradient(0deg, #ccc, #111);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -93,11 +93,13 @@ export default {
|
||||
|
||||
// Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
|
||||
plugins: [
|
||||
"~/plugins/v-tippy",
|
||||
"~/plugins/vuex-persist",
|
||||
"~/plugins/v-tooltip",
|
||||
"~/plugins/vue-rx",
|
||||
"~/plugins/vue-apollo",
|
||||
{ src: "~/plugins/web-worker", ssr: false },
|
||||
"~/plugins/formatCountry.js",
|
||||
],
|
||||
|
||||
// Auto import components (https://go.nuxtjs.dev/config-components)
|
||||
@@ -225,114 +227,133 @@ export default {
|
||||
name: "English",
|
||||
iso: "en-US",
|
||||
file: "en-US.json",
|
||||
country: "US",
|
||||
},
|
||||
{
|
||||
code: "es",
|
||||
name: "Español",
|
||||
iso: "es-ES",
|
||||
file: "es-ES.json",
|
||||
country: "ES",
|
||||
},
|
||||
{
|
||||
code: "fr",
|
||||
name: "Français",
|
||||
iso: "fr-FR",
|
||||
file: "fr-FR.json",
|
||||
country: "FR",
|
||||
},
|
||||
{
|
||||
code: "fa",
|
||||
name: "Farsi",
|
||||
iso: "fa-IR",
|
||||
file: "fa-IR.json",
|
||||
country: "IR",
|
||||
},
|
||||
{
|
||||
code: "pt",
|
||||
name: "Português",
|
||||
iso: "pt-PT",
|
||||
file: "pt-PT.json",
|
||||
country: "PT",
|
||||
},
|
||||
{
|
||||
code: "pt-br",
|
||||
name: "Português Brasileiro",
|
||||
iso: "pt-BR",
|
||||
file: "pt-BR.json",
|
||||
country: "BR",
|
||||
},
|
||||
{
|
||||
code: "cn",
|
||||
name: "简体中文",
|
||||
iso: "zh-CN",
|
||||
file: "zh-CN.json",
|
||||
country: "CN",
|
||||
},
|
||||
{
|
||||
code: "tw",
|
||||
name: "繁體中文",
|
||||
iso: "zh-TW",
|
||||
file: "zh-TW.json",
|
||||
country: "TW",
|
||||
},
|
||||
{
|
||||
code: "id",
|
||||
name: "Bahasa Indonesia",
|
||||
iso: "id-ID",
|
||||
file: "id-ID.json",
|
||||
country: "ID",
|
||||
},
|
||||
{
|
||||
code: "tr",
|
||||
name: "Türkçe",
|
||||
iso: "tr-TR",
|
||||
file: "tr-TR.json",
|
||||
country: "TR",
|
||||
},
|
||||
{
|
||||
code: "de",
|
||||
name: "Deutsch",
|
||||
iso: "de-DE",
|
||||
file: "de-DE.json",
|
||||
country: "DE",
|
||||
},
|
||||
{
|
||||
code: "ja",
|
||||
name: "日本語",
|
||||
iso: "ja-JP",
|
||||
file: "ja-JP.json",
|
||||
country: "JP",
|
||||
},
|
||||
{
|
||||
code: "ko",
|
||||
name: "한국어",
|
||||
iso: "ko-KR",
|
||||
file: "ko-KR.json",
|
||||
country: "KR",
|
||||
},
|
||||
{
|
||||
code: "in",
|
||||
name: "हिंदी",
|
||||
iso: "in-IN",
|
||||
file: "in-IN.json",
|
||||
country: "IN",
|
||||
},
|
||||
{
|
||||
code: "bn",
|
||||
name: "Bengali",
|
||||
iso: "bn-BD",
|
||||
file: "bn-BD.json",
|
||||
country: "BD",
|
||||
},
|
||||
{
|
||||
code: "ml",
|
||||
name: "മലയാളം",
|
||||
iso: "ml-ML",
|
||||
file: "ml-ML.json",
|
||||
country: "ML",
|
||||
},
|
||||
{
|
||||
code: "vi",
|
||||
name: "Vietnamese",
|
||||
iso: "vi-VN",
|
||||
file: "vi-VN.json",
|
||||
country: "VN",
|
||||
},
|
||||
{
|
||||
code: "nl",
|
||||
name: "Dutch",
|
||||
iso: "nl-BE",
|
||||
file: "nl-BE.json",
|
||||
country: "BE",
|
||||
},
|
||||
{
|
||||
code: "nb",
|
||||
name: "Norwegian (Bokmål)",
|
||||
iso: "nb-NO",
|
||||
file: "nb-NO.json",
|
||||
country: "NO",
|
||||
},
|
||||
],
|
||||
defaultLocale: "en",
|
||||
|
||||
59
package-lock.json
generated
59
package-lock.json
generated
@@ -38,8 +38,10 @@
|
||||
"v-tooltip": "^2.1.3",
|
||||
"vue-apollo": "^3.0.7",
|
||||
"vue-cli-plugin-apollo": "^0.22.2",
|
||||
"vue-functional-data-merge": "^3.1.0",
|
||||
"vue-github-button": "^1.3.0",
|
||||
"vue-rx": "^6.2.0",
|
||||
"vue-tippy": "^4.10.2",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"vuex-persist": "^3.1.3",
|
||||
"yargs-parser": "^20.2.9"
|
||||
@@ -16826,6 +16828,11 @@
|
||||
"node": ">=10.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/humps": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
|
||||
"integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao="
|
||||
},
|
||||
"node_modules/husky": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz",
|
||||
@@ -27863,6 +27870,14 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/tippy.js": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-4.3.5.tgz",
|
||||
"integrity": "sha512-NDq3efte8nGK6BOJ1dDN1/WelAwfmh3UtIYXXck6+SxLzbIQNZE/cmRSnwScZ/FyiKdIcvFHvYUgqmoGx8CcyA==",
|
||||
"dependencies": {
|
||||
"popper.js": "^1.14.7"
|
||||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
@@ -29230,6 +29245,11 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-functional-data-merge": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
|
||||
"integrity": "sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA=="
|
||||
},
|
||||
"node_modules/vue-github-button": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-github-button/-/vue-github-button-1.3.0.tgz",
|
||||
@@ -29491,6 +29511,18 @@
|
||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
|
||||
},
|
||||
"node_modules/vue-tippy": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-tippy/-/vue-tippy-4.10.2.tgz",
|
||||
"integrity": "sha512-o1E4Q7gJHrL5VKpPF10Mdp9Grrqi9JGUBrkOQQUmTrklxctYvjpsSxePZVM0mgfGfRFAboOClw33X1ueB4Byjw==",
|
||||
"dependencies": {
|
||||
"humps": "^2.0.1",
|
||||
"tippy.js": "^4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^2.5.9"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-toasted": {
|
||||
"version": "1.1.28",
|
||||
"resolved": "https://registry.npmjs.org/vue-toasted/-/vue-toasted-1.1.28.tgz",
|
||||
@@ -45572,6 +45604,11 @@
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
|
||||
},
|
||||
"humps": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz",
|
||||
"integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao="
|
||||
},
|
||||
"husky": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz",
|
||||
@@ -54714,6 +54751,14 @@
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz",
|
||||
"integrity": "sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA=="
|
||||
},
|
||||
"tippy.js": {
|
||||
"version": "4.3.5",
|
||||
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-4.3.5.tgz",
|
||||
"integrity": "sha512-NDq3efte8nGK6BOJ1dDN1/WelAwfmh3UtIYXXck6+SxLzbIQNZE/cmRSnwScZ/FyiKdIcvFHvYUgqmoGx8CcyA==",
|
||||
"requires": {
|
||||
"popper.js": "^1.14.7"
|
||||
}
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
@@ -55855,6 +55900,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"vue-functional-data-merge": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
|
||||
"integrity": "sha512-leT4kdJVQyeZNY1kmnS1xiUlQ9z1B/kdBFCILIjYYQDqZgLqCLa0UhjSSeRX6c3mUe6U5qYeM8LrEqkHJ1B4LA=="
|
||||
},
|
||||
"vue-github-button": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-github-button/-/vue-github-button-1.3.0.tgz",
|
||||
@@ -56094,6 +56144,15 @@
|
||||
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
|
||||
"integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
|
||||
},
|
||||
"vue-tippy": {
|
||||
"version": "4.10.2",
|
||||
"resolved": "https://registry.npmjs.org/vue-tippy/-/vue-tippy-4.10.2.tgz",
|
||||
"integrity": "sha512-o1E4Q7gJHrL5VKpPF10Mdp9Grrqi9JGUBrkOQQUmTrklxctYvjpsSxePZVM0mgfGfRFAboOClw33X1ueB4Byjw==",
|
||||
"requires": {
|
||||
"humps": "^2.0.1",
|
||||
"tippy.js": "^4.3.5"
|
||||
}
|
||||
},
|
||||
"vue-toasted": {
|
||||
"version": "1.1.28",
|
||||
"resolved": "https://registry.npmjs.org/vue-toasted/-/vue-toasted-1.1.28.tgz",
|
||||
|
||||
@@ -54,8 +54,10 @@
|
||||
"v-tooltip": "^2.1.3",
|
||||
"vue-apollo": "^3.0.7",
|
||||
"vue-cli-plugin-apollo": "^0.22.2",
|
||||
"vue-functional-data-merge": "^3.1.0",
|
||||
"vue-github-button": "^1.3.0",
|
||||
"vue-rx": "^6.2.0",
|
||||
"vue-tippy": "^4.10.2",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"vuex-persist": "^3.1.3",
|
||||
"yargs-parser": "^20.2.9"
|
||||
|
||||
@@ -1,42 +1,10 @@
|
||||
<template>
|
||||
<div class="flex p-4 justify-between">
|
||||
<div class="max-w-md">
|
||||
<h2
|
||||
class="
|
||||
leading-none
|
||||
tracking-tighter
|
||||
font-semibold
|
||||
text-accent text-5xl
|
||||
md:text-6xl
|
||||
lg:text-8xl
|
||||
"
|
||||
>
|
||||
Open Source
|
||||
</h2>
|
||||
<h3
|
||||
class="
|
||||
text-3xl
|
||||
my-8
|
||||
font-mono
|
||||
text-secondaryDark
|
||||
md:text-4xl
|
||||
lg:text-4xl
|
||||
font-semibold
|
||||
"
|
||||
>
|
||||
API Development Ecosystem
|
||||
</h3>
|
||||
<p class="text-lg my-4 text-secondaryLight max-w-4/5">
|
||||
Millions of developers and companies build, ship, and maintain their
|
||||
APIs on Hoppscotch — the largest and most advanced development platform
|
||||
in the world.
|
||||
</p>
|
||||
<div class="my-8 flex items-center">
|
||||
<button class="button rounded-2xl text-xl">Get Started</button>
|
||||
<button class="button icon rounded-2xl text-xl">Login</button>
|
||||
<AppGitHubStarButton class="ml-8" />
|
||||
</div>
|
||||
</div>
|
||||
<AppGlobe />
|
||||
<div>
|
||||
<LandingHero />
|
||||
<LandingStats />
|
||||
<LandingUsers />
|
||||
<LandingFeatures />
|
||||
<LandingPackage />
|
||||
<LandingFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
7
plugins/formatCountry.js
Normal file
7
plugins/formatCountry.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Vue from "vue"
|
||||
|
||||
Vue.filter("formatCountry", (countryCode) =>
|
||||
String.fromCodePoint(
|
||||
...[...countryCode.toUpperCase()].map((x) => 0x1f1a5 + x.charCodeAt())
|
||||
)
|
||||
)
|
||||
5
plugins/v-tippy.js
Normal file
5
plugins/v-tippy.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import Vue from "vue"
|
||||
import VueTippy, { TippyComponent } from "vue-tippy"
|
||||
|
||||
Vue.use(VueTippy)
|
||||
Vue.component("Tippy", TippyComponent)
|
||||
Reference in New Issue
Block a user