refs https://github.com/TryGhost/Team/issues/1245
- Used onmouseover because a CSS-only approach wouldn't work
- CSS can only highligh next siblings on hover, while we want to highlight previous sibling in our case
- This adds a hover class on mouseover to the right stars and removes it onmouseout
refs https://github.com/TryGhost/Team/issues/1239
Repeatedly clicking the quote icon in the editor toolbar or using the <kbd>Ctrl+Q</kbd> keyboard shortcut will cycle through standard-blockquote -> alternative-blockquot -> standard paragraph.
refs https://github.com/TryGhost/Team/issues/1245
- Several inputs where dismissing the content when pasting HTML without the expected input tag
- The inputs where: the caption of the feature image; the email footer; the caption in the bookmark, code, embed, gallery, image, video cards; the callout card title; the product card header and description; the toggle header
- This makes sure the wrapper html tag is the expected one.
Co-authored-by:
- Kevin Ansfield (@kevinansfield)
closes https://github.com/TryGhost/Team/issues/1254
- when switching over to a more generic file paste handling the prevention of the default paste behaviour was missed meaning an additional image element was added to the contenteditable area which was not rendered on the front-end and disappeared when reloading the post
no issue
- pasted URLs that are converted into an embed card were triggering the `convertUrl` task twice, once on insertion and then again on the immediate deselection that happens whilst the caret is adjusted
- a change in browsers or in Twitter's script meant that the double-fetch and iframe population started to become unreliable with the script not triggering, or the iframe contents being replaced interfering with script timing, resulting in un-styled Twitter embeds immediately after pasting a URL
- added `.drop()` to the `convertUrl` task so the second deselection call is ignored whilst the first call is still in progress
refs https://github.com/TryGhost/Team/issues/1230
- when updating a thumbnail for audio card, the thumbnail url doesn't change as its tied to the audio src. this can cause the old thumbnail to show in frontend due to browser caching
- this change adds a cache busting param at the end of url for each thumbnail upload, thus bypassing cache and showing the right thumbnail
refs https://github.com/TryGhost/Team/issues/1245
- Moved away from using only checkboxes and css because we need to use SVGs to display the stars
- Made it simple to keep CSS to the minimum and enable flexibility
refs https://github.com/TryGhost/Team/issues/1209
- updates tab/enter behavior for toggle heading -> content to use editor keyboard commands instead of overriding html input component
refs https://github.com/TryGhost/Team/issues/1225
When inserted opens a searchable gif selector, creating an image card when a gif is chosen.
Includes keyboard navigation once open:
- `Tab`/`Shift+Tab` cycles highlighted gif through search result order
- `Up` / `Down` / `Left` / `Right` moves highlight in the respective direction
- `Enter` inserts highlighted gif
For the gifs card to be activated it's necessary to add a Tenor API key (obtained freely from https://tenor.com/gifapi) via your config.production.json file:
```
...
"tenor": {
"publicReadOnlyApiKey": "YOUR_KEY"
}
}
```
refs e419e4e99f
- added an optional options param to `addParagraphAfterCard` but was missing a default value meaning the destructuring errored when the param wasn't provided
refs https://github.com/TryGhost/Team/issues/1245
- This is a proof-of-concept as there are so many ways to implement a star-rating component
- The component is only available when editing the product card
- Uses the unicode star character: ★
- The inspiration is an old article from Lea Verou: https://lea.verou.me/2011/08/accessible-star-rating-widget-with-pure-css/ (minus the clever use of :not(:checked) on something that's not an input to filter-out IE)
- There is currently no way to remove the star rating as it'll get designed this week
closes https://github.com/TryGhost/Team/issues/1244
- moved the `<KoenigSettingsPanel>` usage outside of the `<KoenigCard>` contents so it gets positioned relative to the outer card container rather than the inner card container so it's not affected by the inner card changing width/breaking out of the outer container
refs https://github.com/TryGhost/Team/issues/1225
- provides a more obvious continuation point after selecting an image
- added `scrollIntoView` option to `addParagraphAfterCard()` because for large images the cursor in the new paragraph could be pushed out of the visible area
refs https://github.com/TryGhost/Team/issues/1225
After selecting an image you were left in a state where it looked like the image card is inserted and selected but pressing Cmd+Z, Delete, Enter, etc had no effect.
- added a manual deselect/reselect of the card to make sure it's properly selected and editor has focus
- added a scrollToCard call so the newly inserted card position feels more natural
refs https://github.com/TryGhost/Team/issues/1225
When the gif selector is activated we want to scroll as much of the picker into view as possible.
- updated `scroll-into-view` modifier so it can work on bounding rects as well as offsets for those situations where a scroll parent is not the offset parent
- only implemented "off bottom" behavior for now as that's all we need for this use-case
- if the scroll adjustment would mean the top is cut off we readjust so that the top of the element is always visible
refs https://github.com/TryGhost/Team/issues/1237#issuecomment-981770688
- API key names for external services now follow a standard pattern
- top-level key of the service name
- public/private and read/write perms inside the name, eg. `publicReadOnlyApiKey`
- updated test to match expected API key name
refs https://github.com/TryGhost/Team/issues/1225
- `TAB` / `SHIFT+TAB` now cycle through gifs in the search return order. It means the highlight gif will not always be in the next column over but it drastically reduces the vertical scroll jumping
- `LEFT` / `RIGHT` now select the gif to the left/right that visually lines up with the top third of the currently highlighted gif and will stop at the grid edges. The result is `UP` / `DOWN` / `LEFT` / `RIGHT` act more like spatial navigation with no unexpected scroll jumps
- switching to only storing the highlighted gif and relying on indexes added to each gif by the `tenor` service when assigning to columns means that column number changes when resizing the viewport are automatically handled
refs https://github.com/TryGhost/Team/issues/1225
- added `scroll-into-view` modifier that will scroll an element into view putting it at the top or bottom of the viewport depending on which direction scroll is required
- used the `scroll-into-view` modifier to scroll the highlighted gif into view
refs https://github.com/TryGhost/Team/issues/1225
Behaviour:
- `TAB`
- highlights first gif if pressed whist search has focus
- moves highlight to right, wrapping to next row, stops at final gif
- `SHIFT+TAB`
- moves highlight to the left, wrapping to previous row
- focuses the search bar if pressed whilst first gif is highlighted
- `ENTER`
- highlights first gif if pressed whilst search has focus
- inserts the highlighted gif
- `UP`
- moves highlight up through current column
- focuses the search bar if pressed when gif in top row is highlighted
- `DOWN`
- highlights first gif if pressed whilst search has focus
- moves highlight down through current column, stops at final gif in column
- `LEFT`
- moves highlight to the left, wrapping to previous row
- focuses the search bar if pressed whilst first gif is highlighted
- `RIGHT`
- moves highlight to the right, wrapping to next row, stops at final gif
refs https://github.com/TryGhost/Team/issues/1225
- iterations of earlier approaches (modal-image-selector, and kg-media-selector) for the gif selector were kept around whilst we narrowed down the interaction patterns, we've settled on the inline-image-selector pattern introduced in 3f3b66b668 meaning the others are no longer needed
refs https://github.com/TryGhost/Team/issues/1233
- Moved to in-line editing for the title/description
- Only the button is clickable. The button dissapears if the text/href isn't defined
- Added an image (the image can't be deleted for now)
refs https://github.com/TryGhost/Team/issues/1229
- generalized `insertImageCards()` to `insertCardsFromFiles()` and added support for video cards
- added `canInsertCardsFromFiles()` function so the editor can check before starting an editor run loop and generating an undo point
refs https://github.com/TryGhost/Team/issues/1229
- mostly mirrors image card functionality but rebuilt with more modern syntax
- when uploading a video the size and duration is extracted along with a screen capture of the video from 0.5s in, the screen capture is uploaded once the video finishes because we need to use the uploaded video url as a reference to attach it as a thumbnail via the API
- the captured screenshot is currently what's shown in the card
To be implemented:
- "incomplete" state when video has been uploaded but not a thumbnail
- uploader in settings panel to change the video thumbnail
- play button overlay _or_ switch to `<video>` so it can be previewed
refs https://github.com/TryGhost/Team/issues/1225
- if set, typing `/card-name<space>` will instantly insert the card. Useful for cards that will trigger a media browser or similar where continuing to type will search with card-specific autocomplete
refs https://github.com/TryGhost/Team/issues/1239
- added new action for toggling quote sections that will loop through blockquote/aside/p when clicked
- copied `kg-quote.svg` to `kg-quote-2.svg` ready for alternate icon designs
no issue
- card definitions allow multiple params split by spaces in /menu commands but that meant for search terms that included spaces only the first word was being captured
- updated the param handling so the last param specified includes all remaining words in the slash command
refs https://github.com/TryGhost/Team/issues/1225
- added `scrollToCard` action to `koenig-editor` and passed it through to all card components curried to already include the card as an argument
- action selects the card which places the cursor on it, then triggers our existing cursor scroll-into-view behaviour with an option to allow scrolling a card into view
- updated our cursor scroll-into-view behaviour so if the cursor is large (eg, when it covers a card's height) we make sure the top of the cursor is kept in view rather than the bottom
- ensures that on small-height screens, opening the gif selector doesn't result in it's search bar being scrolled off screen
refs https://github.com/TryGhost/Team/issues/1225
- deleting an empty image card in the initial cleanup phase triggers the `onDeselect` action in the card but at that point the card's section no longer exists in the editor post model which was causing errors when attempting to replace it's section with an empty paragraph
refs https://github.com/TryGhost/Team/issues/1225
- static outline was removed but we were still showing an outline when hovered
- added `media-card-placeholder` kg-style case that is the same as `media-card` but without the `kg-card-hover` class
refs https://github.com/TryGhost/Team/issues/1225
- when we're showing a placeholder style image selector we shouldn't show the extra green border around the browser, they should have their own borders and not look exactly like a card
refs https://github.com/TryGhost/Team/issues/1225
- updated `selector-tenor` component to use the correct passed-in action
- fixed focus moving to top of the editor after selecting a gif or unsplash image
refs https://github.com/TryGhost/Team/issues/1225
- the `imageSelector` options in the image card have been expanded to have both a `component` and a `type` property. If the `type` is set to "placeholder" the related image selector component will be rendered in place of the default upload placeholder
- updated `isEmpty()` so the card is always cleaned up if no src has been selected - prevents image selectors popping up when opening a post if it was saved whilst the image selector was open
- updated close-selector behaviour to exit back to a blank paragraph so a different image embed type can be selected easily instead of leaving an image card that you then have to delete, create a new paragraph, and choose the embed type
- added `koenig-image-card/selector-tenor` child component
- the same as `koenig-media-selector-tenor` except with the "Escape" key handling added
- added as a separate component for now to allow for easy switching until we're settled on the selector type we want
refs https://github.com/TryGhost/Team/issues/1225
- when opening the media selector and the bottom is cut off, scroll the whole selector into view so it's bottom is 20px away from the viewport bottom
- if the adjusted scroll would hide the top of the selector, make sure the top is 20px from the viewport top leaving the bottom cut off
refs https://github.com/TryGhost/Team/issues/1225
- if there's not enough space at the end of the editor canvas to fit the selector the browser adds extra space and pushes the scroll up making things feel janky
- added a scroll position reset as soon as the selector renders so nothing jumps around, the canvas just extends so it's possible to scroll the full selector into view
refs https://github.com/TryGhost/Team/issues/1225
Re-using the existing pattern of creating an image card and having it launch an image selector was proving to have a lot of edge cases when we wanted a more streamlined in-line image selector for gifs.
- added a new `'selector'` type to card definitions
- requires a `selectorComponent` argument that is the name of a component that renders the media and handles search
- updated card components to open the selector component when respective menu item is activated
- updated slash menu to instantly trigger the selector component when the slash command matches a card and is followed by a space so that searches continue inside the selector
- added `<KoenigMediaSelector>` component that wraps the card-definition provided component and handles escape key, clicks outside of the editor, and provides a stripped down API to the child component for selecting/closing
- added `<KoenigMediaSelectorTenor>` which mostly replicates the `<GhTenor>` component but has different styling and uses the provided media selector API
no issue
emoji-button uses focus-trap (https://www.npmjs.com/package/focus-trap) to gain/keep focus and allow clicks on the background to close the picker. Unfortunately when deactivating the focus-trap it attempts to return focus but it ends up putting the cursor back at the beginning of the post, deselecting the card and causing scroll jump.
- changed the `<div>` we were using as a button to trigger the emoji picker to `<button>` so that it's focusable meaning `focus-trap` returns focus there when the picker closes keeping focus inside the card
refs https://github.com/TryGhost/Team/issues/1206
Clicking inside the emoji picker was causing focus to be lost which then deselected the card causing an annoying jump between rendered/edit mode whilst working on the card's content. A secondary issue was the picker sticking around after you intentionally clicked elsewhere in the document to leave edit mode.
- before initiating the emoji-button instance, create a container that's appended at the bottom of the document body and that prevents any click events on elements inside the container from bubbling up and causing focus changes. Updated the emoji-button instance to render the picker inside that container
- added a call to hide the picker any time the card leaves edit mode
refs https://github.com/TryGhost/Team/issues/1206
- added `@allowBr` argument to `<KoenigBasicHtmlInput>` and used it in the callout card
- when enabled, adds `soft-break` atom and parser, enables `<br>` support in `cleanBasicHtml()`
no issue
- we had some code in `<KoenigEditor>` that was automatically saving a card when exiting edit mode based on some flawed logic, this meant we were making multiple editor updates simultaneously resulting in usage+change of the upstream `post` model within the same render loop (add paragraph code deselected the card, starts adding new paragraph, the deselection triggered the exit-edit mode which then caused a simultaneous post update)
- passed card env through to `<KoenigEditor>` so that it can compare current payload state against the "saved" payload state to determine if it should save when leaving edit mode
- renamed `_textReplacementEditor` to `_calloutTextEditor` to better match it's usage
- removed unnecessary `onNewline` handling, it wasn't being called because we overrode `ENTER` key
no issue
- switching to `<KoenigBasicHtmlInput>` means that the payload is no longer wrapped in `<p>` tags because the callout should not be a multi-paragraph input
refs https://github.com/TryGhost/Team/issues/1206
- The KoenigBasicHtmlTextarea wasn't an option as the `Enter` key was binded to a line return. And we want to use the `Enter` key to switch to the next paragraph outside of the callout card.
no issue
- text-replacement-input text expansions were customised to not use backticks for code and to have special format handling for `{}`
- switched to it's own set of text expansions that cover the standard expansions used in the main editor except for headings and card replacements
no issue
- the component was still using the text-replacement component's key commands which didn't include code formatting because code in text-replacement component is re-purposed for replacement string highlights
refs https://github.com/TryGhost/Team/issues/1206
- Store the latest emoji locally in the component
- Create a defaultEmoji method to ease retrieving the right emoji
- Use the default emoji when toggling the emoji back on in the card
- Still use 💡 as the default callout emoji
refs https://github.com/TryGhost/Team/issues/1206
- Because we allow <br> in the editor, the editor placeholder was remaining when editing (<p><br></p>). This clean the editor placeholder.
refs https://github.com/TryGhost/Team/issues/1211
- the outline and snippet availability were toggled on `payload.html` presence which isn't set for nft embeds. Switched to toggling on `payload.type` instead as per the main rendered/blank slate conditional
refs https://github.com/TryGhost/Team/issues/1211
Using `type` rather than `card_type` since that's been replaced in the
API.
Moves the rendering of the card in the template so that we get
access to the caption editor, and selectable card (rather than the
anchor tag redirecting us to OpenSea)
Updates the `isEmpty` property to take into account NFT cards.
refs https://github.com/TryGhost/Team/issues/1217
- tenor API config is now an object that lives at `config.tenor`
- updated gifs card gating to use the new config object
- added passthrough of `contentfilter` param from config when making Tenor API requests
refs https://github.com/TryGhost/Team/issues/1206
- Re-use the saved emoji as the default for future callout cards
- Introduced a localstorage util that ignores errors. Ignoring errors avoids issues with browsers that don't support localstorage
refs https://github.com/TryGhost/Team/issues/1206
- This only solves partially the line-returns being eaten by the editor. When editing a card with a line return, it will be removed.
refs https://github.com/TryGhost/Team/issues/1220
- added array support to `{{card-is-available}}` helper where every key needs to be present for the card to be available
- changed gifs card `isAvailable` property to require both the feature flag and the tenor api key to be set in config
no issue
- `{{action}}` is deprecated and is only available for backwards compatibility with action hashes in non-Glimmer components
- for event handlers we should be using the `{{on}}` modifier to use `addEventListener` on an element rather than directly attaching JS functions using `onclick=` attributes to avoid subtle DOM event ordering bugs
refs https://github.com/TryGhost/Team/issues/1206
- prevents text in curly brakets from being wrapped into a <code> tag
- updated the callout card to this generic textarea editor instead of the email-specific editor
no issue
- the number of properties that cards have in their definition was growing so it made sense to drop the single-use `feature` property in favor of the `isAvailable` property that can perform the same functionality
closes https://github.com/TryGhost/Team/issues/1223
It's possible to disable the Unsplash integration from the integration settings but when disabled the image selector was still available for post feature images and as an editor embed option.
- adds `isAvailable` property to card definitions, if it's set it should be a string that matches a config/setting that determines the card's availability
- unsplash card updated to use `'settings.unsplash'` as it's `isAvailable` property
- adds conditional to `<GhEditorFeatureImage>` so the Unsplash selector is only shown when enabled to bring it inline with the `<GhImageUploader>` component that was used previously for post feature images
refs https://github.com/TryGhost/Team/issues/1211
Since we have switched to rendering the NFT card outside of the OEmbed
response we no longer include a html property. This check would throw
for missing HTML so we've updated it to not include NFT cards
refs https://github.com/TryGhost/Team/issues/1211
We've moved the rendering of HTML outside of the OEmbed endpoint - so
the Admin needs to handle constructing the HTML from the OEmbed response
metadata.
refs https://github.com/TryGhost/Team/issues/1219
- added optional `adjustOnResize` argument to `{{movable}}` modifier
- when the movable element's size changes (based on a `ResizeObserver` event) the passed in action is called with the element and the current x/y position
- having this handled via the `{{movable}}` modifier is needed because that's what knows about any CSS translations that are present and allows the drag position to remain in sync with any resize-related adjustments
- return value of the action is expected to be a new `{x, y}` tuple
- used `adjustOnResize` in the `<KoenigSettingsPanel>` component so that any time the settings panel grows and causes part of it to be off-screen we re-position so that the whole panel is kept on-screen
refs https://github.com/TryGhost/Team/issues/1217
- adds `tenor` service that acts as a coordinator for the Tenor API similar to the `unsplash` service
- adds `<GhTenor>` component that renders an image search and select modal using the `tenor` service
- swapped the gifs card over to use the `tenor` image selector so it opens the tenor modal instead of the unsplash modal
no refs
- we added a default tag override for basic html input component, but the edit mode was still using `p` as the tag irrespective of default set, which caused switching back to p tag when editing the component
refs https://github.com/TryGhost/Team/issues/1209
- adds new `KoenigHtmlTextarea` component for entering multi line html in cards
- updates accordion card to use html text area component
refs https://github.com/TryGhost/Team/issues/1217
- behind the `gifsCard` feature flag
- currently maps to the unsplash image selector whilst we're building out an alternative selector
refs https://github.com/TryGhost/Team/issues/1210
- when rendered, show button as greyed out if either text or url is missing
- show "Add button URL" in tooltip if the url is missing
- show "Add button text" in button if the text is missing
- adds wrapper element so the tooltip is not greyed out
refs https://github.com/TryGhost/Team/issues/1205
- Stored in the payload as an hexadecimal (or variable) value
- chosen with a dropdown through a pre-selected list of colors (or the publication accent color)
refs https://github.com/TryGhost/Team/issues/1205
- Uses the KoenigSettingsPanel to display a toggle
- The Emoji is set to an empty string when disabled
- And arbitrarily set to 💡 by default when toggled back on
no issue
The logic for "delete if empty" was duplicated in two places:
1. when `createComponentCard` is used to register a card, this option method was used when cleaning up a post when first rendering (cards in empty state can be saved before the editor auto-removes them but we don't want to show them again)
2. inside of card's own delete-if-empty handling on certain actions such as deselection or leaving edit mode
- added an `ifEmpty` property to each card component
- used by the editor's first-render cleanup routing if the property is present
- can be re-used internally for the card's own deselect/exit-edit-mode behaviour
- updated the cleanup routine in `<KoenigEditor>`
- added a `allComponentCardsRegistered` property that will return `true` when the `.component` property is set on every card (the property is set during card component initialisation so we're at the mercy of Ember's render process so not all card components will be immediately registered)
- swapped `_cleanup` for `_cleanupTask` that will wait for `allComponentCardsRegistered` to be `true` before performing cleanup, ensuring that we always have access to the card component's `isEmpty` property even when Ember renders cards across multiple render batches
- checks for `isEmpty` being a boolean and will delete the card if it's value is `true`
- updated all cards that had delete-if-empty behaviour
- added `isEmpty` properties
- removed duplicated logic in the `createComponentCard` calls
no issue
- adds `{{movable}}` render modifier that sets up basic mouse/touch event handlers that adjust the element's `translate` style when click/touched+dragged so it can be moved around the screen
no issue
- added the edit toolbar icon back to the email-cta card
- switched back to displaying the non-editable version of the card when not in edit mode
- removed unused `showSettingsPanel` property from `<KoenigCard>` component, cards can now control display of settings panel using `@isEditing`
- removed unused `@visible` argument for `<KoenigSettingsPanel>` component
no issue
UI experiment (behind the `cardSettingsPanel` labs flag) for pulling card settings out into a separate side-panel. Prototyping against the email-cta card.
- shows panel immediately when selected unless selected by keyboard in which case it will wait for mouse input
- panel position is fixed so it's always on screen
- initial position tries to be as close to centered right of the card as possible
- ensures all of the panel is on-screen, will overlap the card if there's no enough horizontal width
- re-positions after window resize
- card no longer has a separate edit mode, the text replacement field is directly editable
no issue
Pages can not be sent by email so by hiding email-specific cards when editing pages we can reduce confusion and clutter.
- added a `@postType` argument to editor components that can be passed through to sub components
- set `@postType` to `post.displayName` which is either `post` or `page`
- updated `{{card-is-available}}` helper to compare a `postType` property on card menu items to the passed in `postType`
no issue
- Sentry was showing `card` being undefined at times when attempting to select/edit a card after it's inserted in `replaceWithCardSection()` (called by card menus and text expansions) but it's not been reproducible in local environments
- it's unclear if the problem occurs due to the card render not happening in the immediate render queue or if it's some other problem
- added a retry if the card is not found after the first render plus logs to Sentry and console when the unexpected state occurs for better insight
no issue
- wrapped dropdown options in an options group so the heading can provide more context
- fixed styling so there isn't double padding around grouped options
closes https://github.com/TryGhost/Team/issues/1020
- image card payloads should have a valid `src` attribute, use that instead of relying on the associated `img` element which won't be found/have an unexpected `src` when it's newly created because the image card will still be using the blob url generated for a preview whilst uploading
closes https://github.com/TryGhost/Team/issues/1013
Image cards could be dropped into a gallery when at least one image was already present but there was no handling for image cards being dropped onto an empty gallery.
- added `data-gallery` attribute to the placeholder element so it gets picked up by the drag-and-drop container registration
- added drag enter/leave event handlers so we can show the same dropzone indicator as when a standard file is being dropped onto an empty gallery
- updated the drop handling to handle insertion when no images already exist by forcing insertion at index `0`
- updated the register or refresh handler to switch between different drag-and-drop container elements as needed and switched to `scheduleOnce` to prevent duplicate containers being created if the function is called twice before render occurs
no issue
- the editor range was getting out of sync between pasting and the subsequent `didUpdatePost` callback which cleans up content to conform to our "basic html" expectations resulting in errors in the console and potentially lost content if the paste occurred with the cursor inside existing text
- updated `handlePaste` to perform the same parse as the default paste handler but then cleanup the post before inserting so there's no mismatch of content across the editor callbacks
closes https://github.com/TryGhost/Team/issues/1023
closes https://github.com/TryGhost/Ghost/issues/11424
- we were comparing the caption editor's contenteditable inner html vs the passed in html in order to reset the editor if the bound value changed upstream, this was causing problems because the editor and external html cleaning were treating trailing whitespace slightly differently which resulted in the editor being reset every time it gained focus when a trailing space was present
- removed reliance on inspecting the editor's inner html and instead switched to comparing a changed `html` value to that which was passed in during initialisation or was last passed upstream from the editor so there's no chance of unexpected deviance