diff --git a/.agents/skills/flotilla-model/SKILL.md b/.agents/skills/flotilla-model/SKILL.md index 9cc58f72..e77319cc 100644 --- a/.agents/skills/flotilla-model/SKILL.md +++ b/.agents/skills/flotilla-model/SKILL.md @@ -168,8 +168,8 @@ relay's URL, each call signed with a fresh NIP-98 event. Every method resolves t | Methods | Called from | |---|---| | `supportedMethods`, `listMethodAssignees`, `listBannedPubkeys` (cached) | `SpaceManagement` (`src/app/management.ts`) | -| `banPubkey`, `unbanPubkey`, `allowPubkey`, `unallowPubkey`, `listBannedPubkeys` | `ProfileDetail`, `SpaceMemberMenu`, `SpaceMemberBannedMenu`, `ReportMenuList`, `allowPubkeys` (`SpaceMembersAdd`, `RoleAddMembers`, `addRoomMembers`) | -| `banEvent` | `RoomItemMenu`, `EventMenu`, `ReportMenuList`, `RoomJoinItem` (dismissing a join request) | +| `banPubkey`, `unbanPubkey`, `allowPubkey`, `unallowPubkey`, `listBannedPubkeys` | `ProfileDetail`, `SpaceMemberMenu`, `SpaceMemberBannedMenu`, `ReportMenu`, `allowPubkeys` (`SpaceMembersAdd`, `RoleAddMembers`, `addRoomMembers`) | +| `banEvent` | `RoomItemMenu`, `EventMenu`, `ReportMenu`, `RoomJoinItem` (dismissing a join request) | | `createRole`, `editRole`, `deleteRole`, `assignRole`, `unassignRole` | `RoleCreate`, `RoleEdit`, `SpaceRoleMenu`, `SpaceMemberRoles` (via `syncAssignments`), `RoleAddMembers` | | `listClaims`, `createClaim` | `Access.prepareInvite` | | `changeRelayName`, `changeRelayDescription`, `changeRelayIcon` | `SpaceEdit` | diff --git a/.agents/skills/flotilla-model/kinds.md b/.agents/skills/flotilla-model/kinds.md index 2a57e120..21a39a02 100644 --- a/.agents/skills/flotilla-model/kinds.md +++ b/.agents/skills/flotilla-model/kinds.md @@ -16,7 +16,7 @@ and NIP-43 tables in [SKILL.md](SKILL.md). Routes are under `src/routes/`. | Classifieds | `CLASSIFIED` (30402) | `Classified` | `src/app/classifieds.ts` | `spaces/[relay]/classifieds`, `classifieds/[address]` (`ClassifiedForm`) | | Goals | `ZAP_GOAL` (9041) | `ZapGoal` | none | `spaces/[relay]/goals`, `goals/[id]` (`GoalCreate`) | | Polls | `POLL` (1068), `POLL_RESPONSE` (1018) | `Poll`, `PollResponse` | none | `spaces/[relay]/polls`, `polls/[id]` (`PollCreate`, `PollVotes`) | -| Library | `PINBOARD` (30067), `PIN` (39067) | `Pinboard`, `Pin` | `src/app/pinboards.ts` | `spaces/[relay]/library` (`PinboardEdit`, `PinAdd`); published by any member | +| Library | `PINBOARD` (30067), `PIN` (39067) | `Pinboard`, `Pin` | `src/app/pinboards.ts` | `spaces/[relay]/library` (`PinboardEdit`, `PinSave`); published by any member | | Room pins | `ROOM_PINS` (39005), `ROOM_UPDATE_PINS` (9010) | `RoomPins`, `RoomUpdatePins` | `src/app/roomPins.ts` | `RoomItemMenu`, `RoomItemMenuMobile`, `RoomPinnedMessagesAll` | | Featured content | `APP_DATA` (30078), `d` = `flotilla/featured-content` | `AppData` | `src/app/featured.ts` | `SpaceFeaturedContent`; published as the relay | | Bot commands (NIP-CD) | `COMMAND` (31992) | `Command` | `src/app/commands.ts` | `RoomCompose`, `ContentCommand` | @@ -28,8 +28,8 @@ and NIP-43 tables in [SKILL.md](SKILL.md). Routes are under `src/routes/`. |---|---|---|---|---| | Reactions | `REACTION` (7) | `Reaction`, through the `Reactions` plugin | `src/app/reactions.ts` | `RoomItem`, `EventReactButtons`, `ContentActions`, `CommentActions`, `ArticleActionBar` | | Zaps | `ZAP_REQUEST` (9734), `ZAP_RECEIPT` (9735) | `ZapRequest`; receipts checked by `Zappers.validZapReceipts` | `src/app/lightning.ts` (wallets) | `Zap`, `ZapButton`, `GoalSummary`; off on iOS | -| Reports | `REPORT` (1984) | `Report` | `src/app/actionItems.ts` | `Report`, `ReportMenuList` | -| Deletes | `DELETE` (5) | `Delete`, through the `Deletes` plugin | none | `EventDeleteConfirm`, `ReportMenuList` | +| Reports | `REPORT` (1984) | `Report` | `src/app/actionItems.ts` | `Report`, `ReportMenu` | +| Deletes | `DELETE` (5) | `Delete`, through the `Deletes` plugin | none | `EventDeleteConfirm`, `ReportMenu` | ## Direct messages diff --git a/.agents/skills/flotilla-views/naming.md b/.agents/skills/flotilla-views/naming.md index 743661bf..4210ba7d 100644 --- a/.agents/skills/flotilla-views/naming.md +++ b/.agents/skills/flotilla-views/naming.md @@ -10,9 +10,9 @@ PascalCase. This is what the qualifiers mean, drawn from the ~320 components the | `Item` | one row or card in a list | `ClassifiedItem`, `NoteItem`, `PeopleItem`, `ReportItem`, `RoomItem` (a message in a room), `SpaceMenuRoomItem` | | `Actions` | the footer row under an event: reactions, status, overflow menu | `ContentActions`, `CommentActions`, `EventActions` | | `Menu` | the contents of a popover, taking an `onClick` that closes it | `EventMenu`, `ChatMenu`, `RoomItemMenu`, `SpaceMemberMenu` | -| `MenuList` | the popover contents when `*Menu` is the trigger instead | `ProfileMenu` → `ProfileMenuList`, `ReportMenu` → `ReportMenuList` | | `Mobile` | the same actions as a modal, pushed on tap instead of hovered | `RoomItemMenuMobile`, `ChatMessageMenuMobile`, `SpaceMenuMobile` | -| `Create` / `Edit` | a modal that publishes a new or edited event | `ClassifiedCreate`, `RoomEdit`, `PinEdit` | +| `Create` / `Edit` | a modal that publishes a new or edited event | `ClassifiedCreate`, `RoomEdit`, `GoalCreate` | +| `Save` | one modal that creates or edits, depending on whether it's given the event | `PinSave` | | `Form` | the fields shared by a Create/Edit pair | `ClassifiedForm`, `RoomForm`, `RoleForm` | | `Detail` | a modal with everything known about an entity | `ProfileDetail`, `RoomDetail`, `ContentLinkDetail` | | `Info` | a modal with an event's underlying data | `EventInfo`, `ProfileInfo` | @@ -22,7 +22,7 @@ PascalCase. This is what the qualifiers mean, drawn from the ~320 components the | `Bar` | a horizontal strip across the top or bottom of something | `SpaceBar`, `ComposeBar`, `EventActionBar` | | `Compose` | a message composer | `ChatCompose`, `RoomCompose`, `CommentCompose` | | `Confirm` | the step that confirms an action or a code | `EventDeleteConfirm`, `LogInOTPConfirm`, `SignUpEmailConfirm` | -| `Add` | a modal that adds something to a collection | `SpaceAdd`, `PinAdd`, `RoomMembersAdd` | +| `Add` | a modal that adds something to a collection | `SpaceAdd`, `RoomMembersAdd` | | `Select` | a picker, as a modal or a bindable input | `PinboardSelect`, `TopicMultiSelect` | | `Button` | a self-contained trigger | `ZapButton`, `DictationButton`, `RoomItemEmojiButton` | | `Name`, `Image`, `Icon`, `Link`, `Circle` | one identifier rendered | `RoomName`, `RelayIcon`, `ProfileLink`, `ProfileCircle` | diff --git a/src/app/components/BoardMenu.svelte b/src/app/components/BoardMenu.svelte index be90f00c..74784289 100644 --- a/src/app/components/BoardMenu.svelte +++ b/src/app/components/BoardMenu.svelte @@ -11,7 +11,7 @@ import Button from "@lib/components/Button.svelte" import Confirm from "@lib/components/Confirm.svelte" import EventInfo from "@app/components/EventInfo.svelte" - import PinAdd from "@app/components/PinAdd.svelte" + import PinSave from "@app/components/PinSave.svelte" import PinboardEdit from "@app/components/PinboardEdit.svelte" import {deletes, relays, user} from "@app/core" import {shareEvent} from "@app/share" @@ -30,7 +30,7 @@ const share = () => shareEvent(url, "Shelf", board.event) - const addLink = () => pushModal(PinAdd, {url, address: board.address()}) + const addLink = () => pushModal(PinSave, {url, address: board.address()}) const edit = () => pushModal(PinboardEdit, {url, board}) diff --git a/src/app/components/CalendarEventForm.svelte b/src/app/components/CalendarEventForm.svelte index 8011a0db..3b8d1cb1 100644 --- a/src/app/components/CalendarEventForm.svelte +++ b/src/app/components/CalendarEventForm.svelte @@ -14,7 +14,7 @@ import ModalFooter from "@lib/components/ModalFooter.svelte" import Modal from "@lib/components/Modal.svelte" import ModalBody from "@lib/components/ModalBody.svelte" - import DateTimeRangeInput from "@lib/components/DateTimeRangeInput.svelte" + import DateTimeInput from "@lib/components/DateTimeInput.svelte" import EditorContent from "@app/editor/EditorContent.svelte" import {publishToSpace} from "@app/rooms" import {makeEditor} from "@app/editor" @@ -182,7 +182,7 @@

Date and time*

{/snippet} {#snippet input()} - + {/snippet} diff --git a/src/app/components/Chat.svelte b/src/app/components/Chat.svelte index ee543d2a..0552db36 100644 --- a/src/app/components/Chat.svelte +++ b/src/app/components/Chat.svelte @@ -27,12 +27,12 @@ import PageContent from "@lib/components/PageContent.svelte" import Divider from "@lib/components/Divider.svelte" import Button from "@lib/components/Button.svelte" - import ProfileName from "@app/components/ProfileName.svelte" import ProfileLink from "@app/components/ProfileLink.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" import ProfileCircles from "@app/components/ProfileCircles.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte" import ChatMembers from "@app/components/ChatMembers.svelte" + import ChatTitle from "@app/components/ChatTitle.svelte" import ChatMessage from "@app/components/ChatMessage.svelte" import ComposeArea, {type ComposeValues} from "@app/components/ComposeArea.svelte" import ThunkToast from "@app/components/ThunkToast.svelte" @@ -200,31 +200,16 @@
diff --git a/src/app/components/ChatItem.svelte b/src/app/components/ChatItem.svelte index 92911c7e..1cad0b4b 100644 --- a/src/app/components/ChatItem.svelte +++ b/src/app/components/ChatItem.svelte @@ -6,7 +6,7 @@ import type {TrustedEvent} from "@welshman/util" import {fade} from "@lib/transition" import Button from "@lib/components/Button.svelte" - import ProfileName from "@app/components/ProfileName.svelte" + import ChatTitle from "@app/components/ChatTitle.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" import ProfileCircles from "@app/components/ProfileCircles.svelte" import {makeChatPath, goToChat} from "@app/routes" @@ -45,20 +45,14 @@
- {#if others.length === 0} - - Note to self - {:else if others.length === 1} - - - {:else} + {#if others.length > 1} -

- - and {others.length - 1} - {others.length > 2 ? "others" : "other"} -

+ {:else} + {/if} +

+ +

{#if !active && $notifications.has(path)}
diff --git a/src/app/components/ChatTitle.svelte b/src/app/components/ChatTitle.svelte new file mode 100644 index 00000000..3843e5fd --- /dev/null +++ b/src/app/components/ChatTitle.svelte @@ -0,0 +1,24 @@ + + +{#if others.length > 0} + + {#if others.length === 2} + and + {:else if others.length > 2} + and {others.length - 1} others + {/if} +{:else} + Note to self +{/if} diff --git a/src/app/components/HomeInboxItem.svelte b/src/app/components/HomeInboxItem.svelte index 8eee9bd3..a1b8a3ec 100644 --- a/src/app/components/HomeInboxItem.svelte +++ b/src/app/components/HomeInboxItem.svelte @@ -1,7 +1,9 @@ -{#if conversation.url && conversation.h} - -{:else if conversation.url} - +{#if url} + + {#snippet icon()} + + {/snippet} + {#snippet title()} + {#if h} + + {:else} + Chat + {/if} + {/snippet} + {#snippet subtitle()} + + {/snippet} + {:else} - + {/if} diff --git a/src/app/components/HomeInboxItemChat.svelte b/src/app/components/HomeInboxItemChat.svelte index 865be043..82f61cc7 100644 --- a/src/app/components/HomeInboxItemChat.svelte +++ b/src/app/components/HomeInboxItemChat.svelte @@ -2,8 +2,8 @@ import {remove, uniq} from "@welshman/lib" import type {TrustedEvent} from "@welshman/util" import HomeInboxItemCard from "@app/components/HomeInboxItemCard.svelte" + import ChatTitle from "@app/components/ChatTitle.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" - import ProfileName from "@app/components/ProfileName.svelte" import {user} from "@app/core" type Props = { @@ -22,14 +22,7 @@ {/snippet} {#snippet title()} - {#if others.length > 0} - - {#if others.length > 1} - and {others.length - 1} {others.length > 2 ? "others" : "other"} - {/if} - {:else} - Note to self - {/if} + {/snippet} {#snippet subtitle()} direct message diff --git a/src/app/components/HomeInboxItemRoom.svelte b/src/app/components/HomeInboxItemRoom.svelte deleted file mode 100644 index d2b03762..00000000 --- a/src/app/components/HomeInboxItemRoom.svelte +++ /dev/null @@ -1,28 +0,0 @@ - - - - {#snippet icon()} - - {/snippet} - {#snippet title()} - - {/snippet} - {#snippet subtitle()} - - {/snippet} - diff --git a/src/app/components/HomeInboxItemSpaceChat.svelte b/src/app/components/HomeInboxItemSpaceChat.svelte deleted file mode 100644 index d693b679..00000000 --- a/src/app/components/HomeInboxItemSpaceChat.svelte +++ /dev/null @@ -1,26 +0,0 @@ - - - - {#snippet icon()} - - {/snippet} - {#snippet title()} - Chat - {/snippet} - {#snippet subtitle()} - - {/snippet} - diff --git a/src/app/components/PinAdd.svelte b/src/app/components/PinAdd.svelte deleted file mode 100644 index 9473660f..00000000 --- a/src/app/components/PinAdd.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - - diff --git a/src/app/components/PinEdit.svelte b/src/app/components/PinEdit.svelte deleted file mode 100644 index 241cdaf1..00000000 --- a/src/app/components/PinEdit.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - - diff --git a/src/app/components/PinMenu.svelte b/src/app/components/PinMenu.svelte index 2f298897..e504c68e 100644 --- a/src/app/components/PinMenu.svelte +++ b/src/app/components/PinMenu.svelte @@ -10,7 +10,7 @@ import Button from "@lib/components/Button.svelte" import Confirm from "@lib/components/Confirm.svelte" import EventInfo from "@app/components/EventInfo.svelte" - import PinEdit from "@app/components/PinEdit.svelte" + import PinSave from "@app/components/PinSave.svelte" import {deletes, relays, user} from "@app/core" import {shareEvent} from "@app/share" import {pushModal} from "@app/modal" @@ -28,7 +28,7 @@ const share = () => shareEvent(url, "Link", pin.event) - const edit = () => pushModal(PinEdit, {url, pin}) + const edit = () => pushModal(PinSave, {url, pin}) const deletePin = async () => { try { diff --git a/src/app/components/PinForm.svelte b/src/app/components/PinSave.svelte similarity index 71% rename from src/app/components/PinForm.svelte rename to src/app/components/PinSave.svelte index 4896b363..bfed52f6 100644 --- a/src/app/components/PinForm.svelte +++ b/src/app/components/PinSave.svelte @@ -1,14 +1,8 @@ - - diff --git a/src/app/components/ProfileDetail.svelte b/src/app/components/ProfileDetail.svelte index 2df78d9c..701b7318 100644 --- a/src/app/components/ProfileDetail.svelte +++ b/src/app/components/ProfileDetail.svelte @@ -11,6 +11,7 @@ import Icon from "@lib/components/Icon.svelte" import Confirm from "@lib/components/Confirm.svelte" import Button from "@lib/components/Button.svelte" + import MenuButton from "@lib/components/MenuButton.svelte" import Modal from "@lib/components/Modal.svelte" import ModalBody from "@lib/components/ModalBody.svelte" import ModalFooter from "@lib/components/ModalFooter.svelte" @@ -100,45 +101,44 @@
- - {#snippet customActions()} - {#if !isSelf} + {#snippet customActions()} + {#if !isSelf} +
  • + +
  • + {/if} + {#if isBanned} + {#if $permissions.addMembers}
  • -
  • {/if} - {#if isBanned} - {#if $permissions.addMembers} -
  • - -
  • - {/if} - {:else} - {#if $permissions.removeMembers} -
  • - -
  • - {/if} - {#if $permissions.ban} -
  • - -
  • - {/if} + {:else} + {#if $permissions.removeMembers} +
  • + +
  • {/if} - {/snippet} -
    + {#if $permissions.ban} +
  • + +
  • + {/if} + {/if} + {/snippet} +
    diff --git a/src/app/components/ProfileMenu.svelte b/src/app/components/ProfileMenu.svelte index 258be1c6..7518bb59 100644 --- a/src/app/components/ProfileMenu.svelte +++ b/src/app/components/ProfileMenu.svelte @@ -1,34 +1,52 @@ - + diff --git a/src/app/components/ProfileMenuList.svelte b/src/app/components/ProfileMenuList.svelte deleted file mode 100644 index 7518bb59..00000000 --- a/src/app/components/ProfileMenuList.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - - diff --git a/src/app/components/ProfilePage.svelte b/src/app/components/ProfilePage.svelte index de92f697..786440f5 100644 --- a/src/app/components/ProfilePage.svelte +++ b/src/app/components/ProfilePage.svelte @@ -11,6 +11,7 @@ import Icon from "@lib/components/Icon.svelte" import Link from "@lib/components/Link.svelte" import Button from "@lib/components/Button.svelte" + import MenuButton from "@lib/components/MenuButton.svelte" import Spinner from "@lib/components/Spinner.svelte" import ProfileAbout from "@app/components/ProfileAbout.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" @@ -170,7 +171,7 @@ {/if}
    - +
    {/if} diff --git a/src/app/components/ReportItem.svelte b/src/app/components/ReportItem.svelte index 63de59e1..36f3f92a 100644 --- a/src/app/components/ReportItem.svelte +++ b/src/app/components/ReportItem.svelte @@ -6,6 +6,7 @@ import Card from "@lib/components/Card.svelte" import Cv from "@lib/components/Cv.svelte" import Button from "@lib/components/Button.svelte" + import MenuButton from "@lib/components/MenuButton.svelte" import Profile from "@app/components/Profile.svelte" import ProfileName from "@app/components/ProfileName.svelte" import ProfileDetail from "@app/components/ProfileDetail.svelte" @@ -48,7 +49,7 @@ {/if}
    - +
    {#if event.content}
    diff --git a/src/app/components/ReportMenu.svelte b/src/app/components/ReportMenu.svelte index e27555e9..487f7386 100644 --- a/src/app/components/ReportMenu.svelte +++ b/src/app/components/ReportMenu.svelte @@ -1,15 +1,131 @@ - + diff --git a/src/app/components/ReportMenuList.svelte b/src/app/components/ReportMenuList.svelte deleted file mode 100644 index 487f7386..00000000 --- a/src/app/components/ReportMenuList.svelte +++ /dev/null @@ -1,131 +0,0 @@ - - - diff --git a/src/app/components/Share.svelte b/src/app/components/Share.svelte index 07186611..c98609c7 100644 --- a/src/app/components/Share.svelte +++ b/src/app/components/Share.svelte @@ -13,9 +13,9 @@ import ModalHeader from "@lib/components/ModalHeader.svelte" import ModalSubtitle from "@lib/components/ModalSubtitle.svelte" import ModalTitle from "@lib/components/ModalTitle.svelte" + import ChatTitle from "@app/components/ChatTitle.svelte" import ProfileCircle from "@app/components/ProfileCircle.svelte" import ProfileCircles from "@app/components/ProfileCircles.svelte" - import ProfileName from "@app/components/ProfileName.svelte" import RelayName from "@app/components/RelayName.svelte" import RoomNameWithImage from "@app/components/RoomNameWithImage.svelte" import {chatSearch} from "@app/chats" @@ -95,19 +95,12 @@ onclick={() => toggle(path)}> {#if others.length > 1} - - - +{others.length - 1} - - {:else if others.length === 1} - - - - {:else} - - Note to self + {/if} + + + {/each}
    diff --git a/src/lib/components/DateTimeInput.svelte b/src/lib/components/DateTimeInput.svelte index 1b7a03ee..ac58f5ba 100644 --- a/src/lib/components/DateTimeInput.svelte +++ b/src/lib/components/DateTimeInput.svelte @@ -3,28 +3,16 @@ import {DatePicker} from "@svelte-plugins/datepicker" import CloseCircle from "@assets/icons/close-circle.svg?dataurl" import CalendarMinimalistic from "@assets/icons/calendar-minimalistic.svg?dataurl" - import {anchorDatepicker} from "@lib/html" import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" type Props = { value?: number | undefined + end?: number | undefined + isRange?: boolean } - let {value = $bindable()}: Props = $props() - - // The picker works in millisecond timestamps, and we expose unix seconds. - let element: HTMLElement - let isOpen = $state(false) - let startDate: number | undefined = $state(value ? value * 1000 : undefined) - let startDateTime = $state("") - - // With no date the library initializes its time input from the epoch. - onMount(() => { - if (!startDate) { - startDateTime = "12:00" - } - }) + let {value = $bindable(), end = $bindable(), isRange = false}: Props = $props() const toggle = () => { isOpen = !isOpen @@ -38,24 +26,93 @@ const fmt = (ts: number) => new Date(ts).toLocaleString([], {dateStyle: "medium", timeStyle: "short"}) - const display = $derived(startDate ? fmt(startDate) : "") + // Fixed positioning so the popup escapes scroll-container clipping. + const anchor = () => { + const reposition = () => { + const label = element.querySelector("label") + const container = element.querySelector(".calendars-container") + + if (label && container) { + const margin = 8 + const rect = label.getBoundingClientRect() + const height = container.offsetHeight + const width = container.offsetWidth + + let top = rect.bottom + 4 + + // flip above the input when there's not enough room below, else pin to the bottom edge + if (height && top + height > innerHeight - margin) { + top = + rect.top - height - 4 >= margin + ? rect.top - height - 4 + : Math.max(margin, innerHeight - height - margin) + } + + const left = width + ? Math.max(margin, Math.min(rect.left, innerWidth - width - margin)) + : rect.left + + element.style.setProperty("--datepicker-container-position", "fixed") + element.style.setProperty("--datepicker-container-top", `${top}px`) + element.style.setProperty("--datepicker-container-left", `${left}px`) + } + } + + reposition() + addEventListener("scroll", reposition, true) + addEventListener("resize", reposition) + + return () => { + removeEventListener("scroll", reposition, true) + removeEventListener("resize", reposition) + } + } + + // The picker works in millisecond timestamps, and we expose unix seconds. + let element: HTMLElement + let isOpen = $state(false) + let startDate: number | undefined = $state(value ? value * 1000 : undefined) + let endDate: number | undefined = $state(end ? end * 1000 : undefined) + let startDateTime = $state("") + let endDateTime = $state("") + + const display = $derived( + startDate ? (endDate ? `${fmt(startDate)} – ${fmt(endDate)}` : fmt(startDate)) : "", + ) $effect(() => { if (isOpen && element) { - return anchorDatepicker(element) + return anchor() } }) $effect(() => { value = startDate ? Math.round(startDate / 1000) : undefined }) + + $effect(() => { + end = endDate ? Math.round(endDate / 1000) : undefined + }) + + // With no date the library initializes its time inputs from the epoch. + onMount(() => { + if (!startDate) { + startDateTime = "12:00" + } + if (!endDate) { + endDateTime = "13:00" + } + })
    @@ -65,10 +122,10 @@ type="text" readonly class="grow cursor-pointer" - placeholder="Select date" + placeholder={isRange ? "Select dates" : "Select date"} value={display} onclick={toggle} /> - {#if startDate} + {#if startDate && !isRange} diff --git a/src/lib/components/DateTimeRangeInput.svelte b/src/lib/components/DateTimeRangeInput.svelte deleted file mode 100644 index c316803e..00000000 --- a/src/lib/components/DateTimeRangeInput.svelte +++ /dev/null @@ -1,81 +0,0 @@ - - -
    - - - -
    diff --git a/src/lib/html.ts b/src/lib/html.ts index dcb59281..60b4739e 100644 --- a/src/lib/html.ts +++ b/src/lib/html.ts @@ -26,50 +26,6 @@ export const documentActive = readable( }, ) -// Fixed positioning so an @svelte-plugins/datepicker popup escapes scroll-container clipping. -export const anchorDatepicker = (wrapper: HTMLElement) => { - const reposition = () => { - const anchor = wrapper.querySelector("label") - const container = wrapper.querySelector(".calendars-container") as HTMLElement | null - - if (!anchor || !container) { - return - } - - const margin = 8 - const rect = anchor.getBoundingClientRect() - const height = container.offsetHeight - const width = container.offsetWidth - - let top = rect.bottom + 4 - - // flip above the input when there's not enough room below, else pin to the bottom edge - if (height && top + height > innerHeight - margin) { - top = - rect.top - height - 4 >= margin - ? rect.top - height - 4 - : Math.max(margin, innerHeight - height - margin) - } - - const left = width - ? Math.max(margin, Math.min(rect.left, innerWidth - width - margin)) - : rect.left - - wrapper.style.setProperty("--datepicker-container-position", "fixed") - wrapper.style.setProperty("--datepicker-container-top", `${top}px`) - wrapper.style.setProperty("--datepicker-container-left", `${left}px`) - } - - reposition() - addEventListener("scroll", reposition, true) - addEventListener("resize", reposition) - - return () => { - removeEventListener("scroll", reposition, true) - removeEventListener("resize", reposition) - } -} - export const copyToClipboard = (text: string) => { const {activeElement} = document const input = document.createElement("textarea") diff --git a/src/routes/spaces/[relay]/library/+page.svelte b/src/routes/spaces/[relay]/library/+page.svelte index abb7025a..85904fdf 100644 --- a/src/routes/spaces/[relay]/library/+page.svelte +++ b/src/routes/spaces/[relay]/library/+page.svelte @@ -18,7 +18,7 @@ import Spinner from "@lib/components/Spinner.svelte" import SpaceBar from "@app/components/SpaceBar.svelte" import PinItem from "@app/components/PinItem.svelte" - import PinAdd from "@app/components/PinAdd.svelte" + import PinSave from "@app/components/PinSave.svelte" import PinboardItem from "@app/components/PinboardItem.svelte" import PinboardEdit from "@app/components/PinboardEdit.svelte" import {app, events, network, reader} from "@app/core" @@ -61,7 +61,7 @@ const createBoard = () => pushModal(PinboardEdit, {url}) - const addLink = () => pushModal(PinAdd, {url, address}) + const addLink = () => pushModal(PinSave, {url, address}) const selectBoard = (board: PinboardReader) => goto(makeLibraryPath(url, board.address()), {replaceState: true, noScroll: true})