Merge branch 'main' into button-component

This commit is contained in:
Jesse Winton
2025-04-04 10:16:28 -04:00
8 changed files with 211 additions and 140 deletions

View File

@@ -27,7 +27,7 @@
"dependencies": { "dependencies": {
"@number-flow/svelte": "^0.3.3", "@number-flow/svelte": "^0.3.3",
"h3": "^1.14.0", "h3": "^1.14.0",
"melt": "^0.28.0", "melt": "^0.28.2",
"posthog-js": "^1.210.2", "posthog-js": "^1.210.2",
"sharp": "^0.33.5" "sharp": "^0.33.5"
}, },

10
pnpm-lock.yaml generated
View File

@@ -15,8 +15,8 @@ importers:
specifier: ^1.14.0 specifier: ^1.14.0
version: 1.15.1 version: 1.15.1
melt: melt:
specifier: ^0.28.0 specifier: ^0.28.2
version: 0.28.0(@floating-ui/dom@1.6.13)(svelte@5.25.6) version: 0.28.2(@floating-ui/dom@1.6.13)(svelte@5.25.6)
posthog-js: posthog-js:
specifier: ^1.210.2 specifier: ^1.210.2
version: 1.230.4 version: 1.230.4
@@ -2726,8 +2726,8 @@ packages:
meilisearch@0.37.0: meilisearch@0.37.0:
resolution: {integrity: sha512-LdbK6JmRghCawrmWKJSEQF0OiE82md+YqJGE/U2JcCD8ROwlhTx0KM6NX4rQt0u0VpV0QZVG9umYiu3CSSIJAQ==} resolution: {integrity: sha512-LdbK6JmRghCawrmWKJSEQF0OiE82md+YqJGE/U2JcCD8ROwlhTx0KM6NX4rQt0u0VpV0QZVG9umYiu3CSSIJAQ==}
melt@0.28.0: melt@0.28.2:
resolution: {integrity: sha512-kiqaTgNB/IkADmUfJZKROqQ3z+isal8LjLhckQANqjfjggIosHM8M7RO3Og7IQ12zK06nLnwanL80SuTPhblrw==} resolution: {integrity: sha512-55DGQ4B3bHKnDnK1ECJ46D+xythNKvuil60k0RXWJ3eEY5XsGjT6WZPVpRooYLfMlAki2J7JCWglCMYzvXwxVw==}
peerDependencies: peerDependencies:
'@floating-ui/dom': ^1.6.0 '@floating-ui/dom': ^1.6.0
svelte: ^5.0.0 svelte: ^5.0.0
@@ -6407,7 +6407,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
melt@0.28.0(@floating-ui/dom@1.6.13)(svelte@5.25.6): melt@0.28.2(@floating-ui/dom@1.6.13)(svelte@5.25.6):
dependencies: dependencies:
'@floating-ui/dom': 1.6.13 '@floating-ui/dom': 1.6.13
jest-axe: 9.0.0 jest-axe: 9.0.0

View File

@@ -164,39 +164,39 @@
/* Font sizes */ /* Font sizes */
--text-x-micro: 0.625rem; --text-x-micro: 0.625rem;
--text-x-micro--line-height: 0.875rem; --text-x-micro--line-height: 0.875rem;
--text-x-micro--tracking: var(--tracking-tighter); --text-x-micro--letter-spacing: var(--tracking-tighter);
--text-micro: 0.75rem; --text-micro: 0.75rem;
--text-micro--line-height: 1rem; --text-micro--line-height: 1rem;
--text-micro--tracking: var(--tracking-tighter); --text-micro--letter-spacing: var(--tracking-tighter);
--text-caption: 0.875rem; --text-caption: 0.875rem;
--text-caption--line-height: 1.375rem; --text-caption--line-height: 1.375rem;
--text-caption--tracking: var(--tracking-tight); --text-caption--letter-spacing: var(--tracking-tight);
--text-sub-body: clamp(0.875rem, 2vw, 1rem); --text-sub-body: clamp(0.875rem, 2vw, 1rem);
--text-sub-body--line-height: 1.375rem; --text-sub-body--line-height: 1.375rem;
--text-sub-body--tracking: var(--tracking-tight); --text-sub-body--letter-spacing: var(--tracking-tight);
--text-body: clamp(1rem, 2.5vw, 1.125rem); --text-body: clamp(1rem, 2.5vw, 1.125rem);
--text-body--line-height: clamp(1.375rem, 3vw, 1.625rem); --text-body--line-height: clamp(1.375rem, 3vw, 1.625rem);
--text-body--tracking: var(--tracking-tight); --text-body--letter-spacing: var(--tracking-tight);
--text-paragraph-md: 1rem; --text-paragraph-md: 1rem;
--text-paragraph-md--line-height: 1.625rem; --text-paragraph-md--line-height: 1.625rem;
--text-paragraph-md--tracking: var(--tracking-tight); --text-paragraph-md--letter-spacing: var(--tracking-tight);
--text-paragraph-lg: 1.125rem; --text-paragraph-lg: 1.125rem;
--text-paragraph-lg--line-height: 1.75rem; --text-paragraph-lg--line-height: 1.75rem;
--text-paragraph-lg--tracking: var(--tracking-tight); --text-paragraph-lg--letter-spacing: var(--tracking-tight);
--text-description: clamp(1.125rem, 3vw, 1.25rem); --text-description: clamp(1.125rem, 3vw, 1.25rem);
--text-description--line-height: clamp(1.625rem, 3.5vw, 1.75rem); --text-description--line-height: clamp(1.625rem, 3.5vw, 1.75rem);
--text-description--tracking: var(--tracking-tighter); --text-description--letter-spacing: var(--tracking-tighter);
--text-label: 1.5rem; --text-label: 1.5rem;
--text-label--line-height: 1.75rem; --text-label--line-height: 1.75rem;
--text-title: clamp(2rem, 5vw, 2.5rem); --text-title: clamp(2rem, 5vw, 2.5rem);
--text-title--line-height: clamp(2.125rem, 5.5vw, 2.75rem); --text-title--line-height: clamp(2.125rem, 5.5vw, 2.75rem);
--text-title--tracking: var(--tracking-squeezed); --text-title--letter-spacing: var(--tracking-squeezed);
--text-display: clamp(3rem, 7vw, 4rem); --text-display: clamp(3rem, 7vw, 4rem);
--text-display--line-height: clamp(3.125rem, 7.5vw, 4.25rem); --text-display--line-height: clamp(3.125rem, 7.5vw, 4.25rem);
--text-display--tracking: var(--tracking-compressed); --text-display--letter-spacing: var(--tracking-compressed);
--text-headline: clamp(3.5rem, 8vw, 5.5rem); --text-headline: clamp(3.5rem, 8vw, 5.5rem);
--text-headline--line-height: clamp(3.5rem, 8.5vw, 5.75rem); --text-headline--line-height: clamp(3.5rem, 8.5vw, 5.75rem);
--text-headline--tracking: var(--tracking-compressed); --text-headline--letter-spacing: var(--tracking-compressed);
/* Letter spacing */ /* Letter spacing */
--tracking-*: initial; --tracking-*: initial;
@@ -208,11 +208,14 @@
--tracking-loose: 0.08em; --tracking-loose: 0.08em;
} }
@layer components { @utility container {
.container { margin-inline: auto;
@apply mx-auto box-content max-w-[75rem] px-5; padding-inline: calc(var(--spacing) * 5);
} box-sizing: box-content;
max-width: 75rem;
}
@layer components {
.mask { .mask {
mask-image: linear-gradient( mask-image: linear-gradient(
to var(--mask-direction, top), to var(--mask-direction, top),

View File

@@ -6,11 +6,11 @@
interface Props { interface Props {
size?: 'default' | 'medium' | 'big'; size?: 'default' | 'medium' | 'big';
gap?: number; gap?: number;
header: Snippet; header?: Snippet;
children: Snippet; children: Snippet;
} }
let { size = 'default', gap = 32, header, children }: Props = $props(); const { size = 'default', gap = 32, header, children }: Props = $props();
let scroll = 0; let scroll = 0;
function calculateScrollAmount(prev = false) { function calculateScrollAmount(prev = false) {
@@ -51,7 +51,9 @@
<div> <div>
<div class="mt-2 flex flex-wrap items-center"> <div class="mt-2 flex flex-wrap items-center">
{@render header()} {#if header}
{@render header()}
{/if}
<div class="nav ml-auto flex items-end gap-3"> <div class="nav ml-auto flex items-end gap-3">
<button <button
class="web-icon-button" class="web-icon-button"

View File

@@ -1,121 +1,115 @@
<script lang="ts" module> <script lang="ts" module>
import type { Writable } from 'svelte/store'; import { writable, type Writable } from 'svelte/store';
import { Tabs } from 'melt/builders';
export type TabsItemProps = {
id: string;
title: string;
};
export type TabsContext = Writable<{ export type TabsContext = Writable<{
content: ReturnType<typeof createTabs>['elements']['content']; triggers: Array<TabsItemProps>;
triggers: Map<string, string>; tabs: Tabs<string>;
}>; }>;
</script> </script>
<script lang="ts"> <script lang="ts">
import Select from '$lib/components/Select.svelte';
import { classNames } from '$lib/utils/classnames'; import { classNames } from '$lib/utils/classnames';
import { createTabs } from '@melt-ui/svelte';
import { setContext, type Snippet } from 'svelte'; import { setContext, type Snippet } from 'svelte';
import { writable } from 'svelte/store'; import { Select } from '$lib/components';
interface Props { const tabs = new Tabs<string>({
children: Snippet; value: ''
} });
const { children }: Props = $props();
const {
elements: { root, list, content, trigger },
states: { value }
} = createTabs();
const ctx = setContext<TabsContext>( const ctx = setContext<TabsContext>(
'tabs', 'tabs',
writable({ writable({
content, triggers: [],
triggers: new Map() tabs
}) })
); );
setContext('tabs-selection', value); $effect(() => {
if ($ctx.triggers.length > 0 && !$ctx.tabs.value) {
$ctx.tabs.value = $ctx.triggers[0].id;
}
});
type TabsProps = {
children: Snippet;
};
const { children }: TabsProps = $props();
</script> </script>
<div class="web-card is-normal mt-4" {...$root} use:root> <div
class="dark:bg-greyscale-850/90 mt-4 mb-8 flex flex-col gap-1 rounded-2xl border border-black/8 bg-white/90 px-6 pt-4 pb-6 outline-0 dark:border-white/10"
>
<div <div
class="tabs flex items-center gap-4 overflow-scroll" class="flex items-center gap-4 overflow-scroll [-ms-overflow-style:none] [scrollbard-width:none]"
style="scrollbar-width: none; -ms-overflow-style: none;"
> >
<ul class="tabs-list hidden items-center gap-4 sm:flex" {...$list} use:list> <div class="hidden items-center gap-4 sm:flex" {...tabs.triggerList}>
{#each Array.from($ctx.triggers.entries()).slice(0, 7) as [id, title]} {#each $ctx.triggers.slice(0, 7) as { title, id }}
<li <button
class="tabs-item shrink-0 rounded-t-[0.625rem] text-center hover:bg-white/4" class={classNames(
class:text-[var(--color-primary)]={$value === id} 'shrink-0 rounded-t-[0.625rem] text-center hover:bg-white/4',
'relative cursor-pointer bg-clip-padding px-1 py-[0.625rem] font-light outline-none',
'after:relative after:top-1 after:bottom-0 after:block after:h-px after:transition-all',
{
'after:bg-[var(--color-primary)]': tabs.value === id
}
)}
{...tabs.getTrigger(id)}
> >
<button {title}
class={classNames( </button>
'tabs-button relative cursor-pointer bg-clip-padding px-1 py-[0.625rem] font-light outline-none',
'after:relative after:top-1 after:bottom-0 after:block after:h-px after:transition-all',
{
'after:bg-[var(--color-primary)]': $value === id
}
)}
{...$trigger(id)}
use:trigger>{title}</button
>
</li>
{/each} {/each}
{#if Array.from($ctx.triggers.entries()).slice(7).length} {#if $ctx.triggers.slice(7).length}
{@const entries = Array.from($ctx.triggers.entries())} {@const desktopOptions = $ctx.triggers.slice(7)}
{@const desktopOptions = entries.slice(7)} <Select
initialLabel="More"
<li> options={desktopOptions.map(({ id, title }) => {
<Select return {
initialLabel="More" value: id,
options={desktopOptions.map(([value, label]) => { label: title
return { };
value, })}
label bind:value={$ctx.tabs.value}
}; />
})}
bind:value={$value}
/>
</li>
{/if} {/if}
</ul> </div>
<ul class="tabs-list flex items-center gap-4 sm:hidden" {...$list} use:list> <div class="flex items-center gap-4 sm:hidden" {...tabs.triggerList}>
{#each Array.from($ctx.triggers.entries()).slice(0, 2) as [id, title]} {#each $ctx.triggers.slice(0, 2) as { title, id }}
<li <button
class="tabs-item shrink-0 rounded-t-[0.625rem] text-center hover:bg-white/4" class={classNames(
class:text-[var(--color-primary)]={$value === id} 'shrink-0 rounded-t-[0.625rem] text-center hover:bg-white/4',
'relative cursor-pointer bg-clip-padding px-1 py-[0.625rem] font-light outline-none',
'after:relative after:top-1 after:bottom-0 after:block after:h-px after:transition-all',
{
'after:bg-[var(--color-primary)]': tabs.value === id
}
)}
{...tabs.getTrigger(id)}
> >
<button {title}
class={classNames( </button>
'tabs-button relative cursor-pointer bg-clip-padding px-1 py-[0.625rem] font-light outline-none',
'after:relative after:top-1 after:bottom-0 after:block after:h-px after:transition-all',
{
'after:bg-[var(--color-primary)]': $value === id
}
)}
{...$trigger(id)}
use:trigger
>
{title}
</button>
</li>
{/each} {/each}
{#if Array.from($ctx.triggers.entries()).slice(2).length} {#if $ctx.triggers.slice(2).length}
{@const entries = Array.from($ctx.triggers.entries())} {@const desktopOptions = $ctx.triggers.slice(7)}
{@const desktopOptions = entries.slice(2)} <Select
initialLabel="More"
<li> options={desktopOptions.map(({ id, title }) => {
<Select return {
initialLabel="More" value: id,
options={desktopOptions.map(([value, label]) => { label: title
return { };
value, })}
label bind:value={$ctx.tabs.value}
}; />
})}
bind:value={$value}
/>
</li>
{/if} {/if}
</ul> </div>
</div> </div>
{@render children?.()}
{@render children()}
</div> </div>

View File

@@ -1,25 +1,19 @@
<script lang="ts"> <script lang="ts">
import { getContext, type Snippet } from 'svelte'; import { getContext, type Snippet } from 'svelte';
import type { TabsContext } from './Tabs.svelte'; import { type TabsContext, type TabsItemProps } from './Tabs.svelte';
interface Props {
id: string;
title: string;
children: Snippet;
}
const { id, title, children }: Props = $props();
const ctx = getContext<TabsContext>('tabs'); const ctx = getContext<TabsContext>('tabs');
const { content } = $ctx; const { id, title, children }: TabsItemProps & { children: Snippet } = $props();
ctx.update((n) => { $effect(() => {
n.triggers.set(id, title); ctx.update((context) => {
return n; context.triggers.push({ id, title });
return context;
});
}); });
</script> </script>
<div class="web-u-sep-block-start pt-4" {...$content(id)} use:content> <div class="border-smooth border-t pt-4" {...$ctx.tabs.getContent(id)}>
{@render children()} {@render children()}
</div> </div>

View File

@@ -6,7 +6,7 @@ description: Learn how to manage team invites in Appwrite. Implement both client
Appwrite provides two approaches for adding members to teams: client-side email invites and server-side custom flows. Each approach serves different use cases and offers unique benefits. Appwrite provides two approaches for adding members to teams: client-side email invites and server-side custom flows. Each approach serves different use cases and offers unique benefits.
# Invite client-side {% #client-side %} # Invite client-side
Client-side email invites are perfect for implementing user-to-user invitations, allowing your users to invite others to join their teams, organizations, or shared resources. When creating a membership, Appwrite: Client-side email invites are perfect for implementing user-to-user invitations, allowing your users to invite others to join their teams, organizations, or shared resources. When creating a membership, Appwrite:
1. Creates a new user account if one doesn't exist for the email address 1. Creates a new user account if one doesn't exist for the email address
@@ -91,7 +91,7 @@ val response = teams.createMembership(
``` ```
{% /multicode %} {% /multicode %}
## Accept invitations {% #accept-invitations %} ## Accept invitations
For client-side email invites, users must accept the invitation to join the team. The acceptance flow: For client-side email invites, users must accept the invitation to join the team. The acceptance flow:
1. User receives an email with an invitation link containing a secret token 1. User receives an email with an invitation link containing a secret token
@@ -171,7 +171,7 @@ val response = teams.updateMembershipStatus(
``` ```
{% /multicode %} {% /multicode %}
# Server-side custom flows {% #server-side %} # Server-side custom flows
Server-side membership creation bypasses the email invitation process, allowing direct member addition. This approach: Server-side membership creation bypasses the email invitation process, allowing direct member addition. This approach:
1. Creates an active membership immediately 1. Creates an active membership immediately
@@ -258,11 +258,11 @@ val response = teams.createMembership(
``` ```
{% /multicode %} {% /multicode %}
# Manage memberships {% #manage-memberships %} # Manage memberships
Once team memberships are created, you'll need to manage their lifecycle. This includes checking status, updating roles, and removing members when necessary. Once team memberships are created, you'll need to manage their lifecycle. This includes checking status, updating roles, and removing members when necessary.
## Check membership status {% #status %} ## Check membership status
Before performing actions on team memberships, you often need to verify a user's current status within a team. The process differs between client-side and server-side implementations. Before performing actions on team memberships, you often need to verify a user's current status within a team. The process differs between client-side and server-side implementations.
@@ -497,7 +497,7 @@ teamsList.teams.forEach { team ->
``` ```
{% /multicode %} {% /multicode %}
## Remove members {% #remove-members %} ## Remove members
Team owners can remove members or users can leave teams: Team owners can remove members or users can leave teams:
@@ -561,11 +561,11 @@ teams.deleteMembership(
``` ```
{% /multicode %} {% /multicode %}
# Manage team permissions {% #permissions %} # Manage team permissions
Teams in Appwrite use a role-based access control (RBAC) system. Each team member can be assigned one or more roles that define their permissions within the team. Teams in Appwrite use a role-based access control (RBAC) system. Each team member can be assigned one or more roles that define their permissions within the team.
## Update roles {% #update-roles %} ## Update roles
You can assign roles when creating a membership or update them later. Note that only team members with the owner role can update other members' roles: You can assign roles when creating a membership or update them later. Note that only team members with the owner role can update other members' roles:
@@ -637,7 +637,7 @@ teams.updateMembership(
``` ```
{% /multicode %} {% /multicode %}
## Check role access {% #check-role-access %} ## Check role access
You can verify if a user has specific roles: You can verify if a user has specific roles:

View File

@@ -135,6 +135,11 @@ query {
} }
} }
``` ```
```http
GET /v1/databases/<DATABASE_ID>/collections/<COLLECTION_ID>/documents?queries[]=%7B%22method%22%3A%22equal%22%2C%22attribute%22%3A%22title%22%2C%22values%22%3A%5B%22Avatar%22%2C%22Lord%20of%20the%20Rings%22%5D%7D&queries[]=%7B%22method%22%3A%22greaterThan%22%2C%22attribute%22%3A%22year%22%2C%22values%22%3A%5B1999%5D%7D HTTP/1.1
Content-Type: application/json
X-Appwrite-Project: <PROJECT_ID>
```
{% /multicode %} {% /multicode %}
# Query operators {% #query-operators %} # Query operators {% #query-operators %}
@@ -165,6 +170,9 @@ Query::select(["name", "title"])
```swift ```swift
Query.select(["name", "title"]) Query.select(["name", "title"])
``` ```
```http
{"method":"select","values":["name","title"]}
```
{% /multicode %} {% /multicode %}
## Comparison operators {% #comparison %} ## Comparison operators {% #comparison %}
@@ -195,6 +203,9 @@ Query::equal("title", ["Iron Man"])
```swift ```swift
Query.equal("title", value: ["Iron Man"]) Query.equal("title", value: ["Iron Man"])
``` ```
```http
{"method":"equal","attribute":"title","values":["Iron Man"]}
```
{% /multicode %} {% /multicode %}
### Not equal {% #not-equal %} ### Not equal {% #not-equal %}
@@ -223,6 +234,9 @@ Query::notEqual("title", ["Iron Man"])
```swift ```swift
Query.notEqual("title", value: ["Iron Man"]) Query.notEqual("title", value: ["Iron Man"])
``` ```
```http
{"method":"notEqual","attribute":"title","values":["Iron Man"]}
```
{% /multicode %} {% /multicode %}
### Less than {% #less-than %} ### Less than {% #less-than %}
@@ -251,6 +265,9 @@ Query::lessThan("score", 10)
```swift ```swift
Query.lessThan("score", value: 10) Query.lessThan("score", value: 10)
``` ```
```http
{"method":"lessThan","attribute":"score","values":[10]}
```
{% /multicode %} {% /multicode %}
### Less than or equal {% #less-than-equal %} ### Less than or equal {% #less-than-equal %}
@@ -279,6 +296,9 @@ Query::lessThanEqual("score", 10)
```swift ```swift
Query.lessThanEqual("score", value: 10) Query.lessThanEqual("score", value: 10)
``` ```
```http
{"method":"lessThanEqual","attribute":"score","values":[10]}
```
{% /multicode %} {% /multicode %}
### Greater than {% #greater-than %} ### Greater than {% #greater-than %}
@@ -307,6 +327,9 @@ Query::greaterThan("score", 10)
```swift ```swift
Query.greaterThan("score", value: 10) Query.greaterThan("score", value: 10)
``` ```
```http
{"method":"greaterThan","attribute":"score","values":[10]}
```
{% /multicode %} {% /multicode %}
### Greater than or equal {% #greater-than-equal %} ### Greater than or equal {% #greater-than-equal %}
@@ -335,6 +358,9 @@ Query::greaterThanEqual("score", 10)
```swift ```swift
Query.greaterThanEqual("score", value: 10) Query.greaterThanEqual("score", value: 10)
``` ```
```http
{"method":"greaterThanEqual","attribute":"score","values":[10]}
```
{% /multicode %} {% /multicode %}
### Between {% #between %} ### Between {% #between %}
@@ -363,6 +389,9 @@ Query::between("price", 5, 10)
```swift ```swift
Query.between("price", start: 5, end: 10) Query.between("price", start: 5, end: 10)
``` ```
```http
{"method":"between","attribute":"price","values":[5,10]}
```
{% /multicode %} {% /multicode %}
## Null checks {% #null-checks %} ## Null checks {% #null-checks %}
@@ -393,6 +422,9 @@ Query::isNull("name")
```swift ```swift
Query.isNull("name") Query.isNull("name")
``` ```
```http
{"method":"isNull","attribute":"name"}
```
{% /multicode %} {% /multicode %}
### Is not null {% #is-not-null %} ### Is not null {% #is-not-null %}
@@ -421,6 +453,9 @@ Query::isNotNull("name")
```swift ```swift
Query.isNotNull("name") Query.isNotNull("name")
``` ```
```http
{"method":"isNotNull","attribute":"name"}
```
{% /multicode %} {% /multicode %}
## String operations {% #string-operations %} ## String operations {% #string-operations %}
@@ -451,6 +486,9 @@ Query::startsWith("name", "Once upon a time")
```swift ```swift
Query.startsWith("name", value: "Once upon a time") Query.startsWith("name", value: "Once upon a time")
``` ```
```http
{"method":"startsWith","attribute":"name","values":["Once upon a time"]}
```
{% /multicode %} {% /multicode %}
### Ends with {% #ends-with %} ### Ends with {% #ends-with %}
@@ -479,6 +517,9 @@ Query::endsWith("name", "happily ever after.")
```swift ```swift
Query.endsWith("name", value: "happily ever after.") Query.endsWith("name", value: "happily ever after.")
``` ```
```http
{"method":"endsWith","attribute":"name","values":["happily ever after."]}
```
{% /multicode %} {% /multicode %}
### Contains {% #contains %} ### Contains {% #contains %}
@@ -535,6 +576,13 @@ Query.contains("ingredients", value: ['apple', 'banana'])
// For strings // For strings
Query.contains("name", value: "Tom") Query.contains("name", value: "Tom")
``` ```
```http
# For arrays
{"method":"contains","attribute":"ingredients","values":["apple","banana"]}
# For strings
{"method":"contains","attribute":"name","values":["Tom"]}
```
{% /multicode %} {% /multicode %}
### Search {% #search %} ### Search {% #search %}
@@ -563,6 +611,9 @@ Query::search("text", "key words")
```swift ```swift
Query.search("text", value: "key words") Query.search("text", value: "key words")
``` ```
```http
{"method":"search","attribute":"text","values":["key words"]}
```
{% /multicode %} {% /multicode %}
## Logical operators {% #logical-operators %} ## Logical operators {% #logical-operators %}
@@ -614,6 +665,9 @@ Query.and([
Query.greaterThan("size", value: 5) Query.greaterThan("size", value: 5)
]) ])
``` ```
```http
{"method":"and","values":[{"method":"lessThan","attribute":"size","values":[10]},{"method":"greaterThan","attribute":"size","values":[5]}]}
```
{% /multicode %} {% /multicode %}
### OR {% #or %} ### OR {% #or %}
@@ -663,6 +717,9 @@ Query.or([
Query.greaterThan("size", value: 10) Query.greaterThan("size", value: 10)
]) ])
``` ```
```http
{"method":"or","values":[{"method":"lessThan","attribute":"size","values":[5]},{"method":"greaterThan","attribute":"size","values":[10]}]}
```
{% /multicode %} {% /multicode %}
## Ordering {% #ordering %} ## Ordering {% #ordering %}
@@ -693,6 +750,9 @@ Query::orderDesc("attribute")
```swift ```swift
Query.orderDesc("attribute") Query.orderDesc("attribute")
``` ```
```http
{"method":"orderDesc","attribute":"attribute"}
```
{% /multicode %} {% /multicode %}
### Order ascending {% #order-asc %} ### Order ascending {% #order-asc %}
@@ -721,6 +781,9 @@ Query::orderAsc("attribute")
```swift ```swift
Query.orderAsc("attribute") Query.orderAsc("attribute")
``` ```
```http
{"method":"orderAsc","attribute":"attribute"}
```
{% /multicode %} {% /multicode %}
## Pagination {% #pagination %} ## Pagination {% #pagination %}
@@ -751,6 +814,9 @@ Query::limit(25)
```swift ```swift
Query.limit(25) Query.limit(25)
``` ```
```http
{"method":"limit","values":[25]}
```
{% /multicode %} {% /multicode %}
### Offset {% #offset %} ### Offset {% #offset %}
@@ -779,6 +845,9 @@ Query::offset(0)
```swift ```swift
Query.offset(0) Query.offset(0)
``` ```
```http
{"method":"offset","values":[0]}
```
{% /multicode %} {% /multicode %}
### Cursor after {% #cursor-after %} ### Cursor after {% #cursor-after %}
@@ -807,6 +876,9 @@ Query::cursorAfter("62a7...f620")
```swift ```swift
Query.cursorAfter("62a7...f620") Query.cursorAfter("62a7...f620")
``` ```
```http
{"method":"cursorAfter","values":["62a7...f620"]}
```
{% /multicode %} {% /multicode %}
### Cursor before {% #cursor-before %} ### Cursor before {% #cursor-before %}
@@ -835,6 +907,9 @@ Query::cursorBefore("62a7...a600")
```swift ```swift
Query.cursorBefore("62a7...a600") Query.cursorBefore("62a7...a600")
``` ```
```http
{"method":"cursorBefore","values":["62a7...a600"]}
```
{% /multicode %} {% /multicode %}
# Complex queries {% #complex-queries %} # Complex queries {% #complex-queries %}
@@ -896,6 +971,9 @@ results = databases.list_documents(
] ]
) )
``` ```
```http
{"method":"or","values":[{"method":"and","values":[{"method":"equal","attribute":"category","values":["books"]},{"method":"lessThan","attribute":"price","values":[20]}]},{"method":"and","values":[{"method":"equal","attribute":"category","values":["magazines"]},{"method":"lessThan","attribute":"price","values":[10]}]}]}
```
{% /multicode %} {% /multicode %}
This example demonstrates how to combine `OR` and `AND` operations. The query uses `Query.or()` to match either condition: books under $20 OR magazines under $10. This example demonstrates how to combine `OR` and `AND` operations. The query uses `Query.or()` to match either condition: books under $20 OR magazines under $10.