mirror of
https://github.com/penpot/penpot.git
synced 2025-04-05 03:21:26 -05:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
3fd429c72a
30 changed files with 420 additions and 174 deletions
|
@ -384,8 +384,10 @@
|
|||
f.revn,
|
||||
f.vern,
|
||||
f.is_shared,
|
||||
ft.media_id AS thumbnail_id
|
||||
ft.media_id AS thumbnail_id,
|
||||
p.team_id
|
||||
from file as f
|
||||
inner join project as p on (p.id = f.project_id)
|
||||
left join file_thumbnail as ft on (ft.file_id = f.id
|
||||
and ft.revn = f.revn
|
||||
and ft.deleted_at is null)
|
||||
|
@ -539,7 +541,8 @@
|
|||
f.modified_at,
|
||||
f.name,
|
||||
f.is_shared,
|
||||
ft.media_id
|
||||
ft.media_id,
|
||||
p.team_id
|
||||
from file as f
|
||||
inner join project as p on (p.id = f.project_id)
|
||||
left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn and ft.deleted_at is null)
|
||||
|
@ -686,7 +689,8 @@
|
|||
f.name,
|
||||
f.is_shared,
|
||||
ft.media_id AS thumbnail_id,
|
||||
row_number() over w as row_num
|
||||
row_number() over w as row_num,
|
||||
p.team_id
|
||||
from file as f
|
||||
inner join project as p on (p.id = f.project_id)
|
||||
left join file_thumbnail as ft on (ft.file_id = f.id
|
||||
|
|
BIN
docs/img/styling/color-picker-gradient.webp
Normal file
BIN
docs/img/styling/color-picker-gradient.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.7 KiB |
|
@ -51,13 +51,24 @@ title: 06· Styling
|
|||
<ol>
|
||||
<li><strong>Eyedropper</strong> - Allows you to pick any color of the objects at the viewport.</li>
|
||||
<li><strong>Color profiles</strong> - Select between RGB, the Harmony Wheel or HSV.</li>
|
||||
<li><strong>Color type</strong> - Solid, linear gradient, radial gradient or image.</li>
|
||||
<li><strong>Color type</strong> - Solid, gradient, or image.</li>
|
||||
<li><strong>Sliders</strong> - Easily manage settings like brightness, saturation or opacity.</li>
|
||||
<li><strong>Values</strong> - Set precise color values of red(R), green(G), blue(B) and transparency(A).</li>
|
||||
<li><strong>Libraries</strong> - Switch between recent colors and libraries.</li>
|
||||
<li><strong>Color palette</strong> - A quick launcher of the palette with the selected library.</li>
|
||||
</ol>
|
||||
|
||||
<h3 id="color-picker-gradients">Gradients</h3>
|
||||
<p>You can apply gradient fills to layers. To do that select the Gradient type at the color picker.</p>
|
||||
<figure>
|
||||
<img alt="Gradient" src="/img/styling/color-picker-gradient.webp"/>
|
||||
</figure>
|
||||
<p>You can choose between two types of gradients:</p>
|
||||
<ul>
|
||||
<li><strong>Linear Gradient:</strong> A smooth transition between two or more colors along a straight line, with the option to adjust the angle.</li>
|
||||
<li><strong>Radial Gradient:</strong> A circular color transition that starts with one color at the center and gradually shifts to another at the edges, which could be a different color or a fade to transparency.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="color-palette">Color palette</h2>
|
||||
<p>The color palette allows you to have a selected color library in plain sight.</p>
|
||||
<figure>
|
||||
|
|
115
frontend/playwright/data/workspace/get-file-10113.json
Normal file
115
frontend/playwright/data/workspace/get-file-10113.json
Normal file
|
@ -0,0 +1,115 @@
|
|||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"layout/grid",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "10113 - Emtpy lib",
|
||||
"~:revn": 1,
|
||||
"~:modified-at": "~m1739365936352",
|
||||
"~:vern": 0,
|
||||
"~:id": "~u5b7ebd2b-2907-80db-8005-b9d67c20cf2e",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~u1ad2931c-eb80-8098-8005-b86c1d9d26c2",
|
||||
"~:created-at": "~m1739365911709",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~u5b7ebd2b-2907-80db-8005-b9d67c20cf2f"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~u5b7ebd2b-2907-80db-8005-b9d67c20cf2f": {
|
||||
"~#penpot/pointer": [
|
||||
"~u5b7ebd2b-2907-80db-8005-b9d67c21cbd3",
|
||||
{
|
||||
"~:created-at": "~m1739365911687"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"~:id": "~u5b7ebd2b-2907-80db-8005-b9d67c20cf2e",
|
||||
"~:options": {
|
||||
"~:components-v2": true
|
||||
},
|
||||
"~:colors": {
|
||||
"~u84a1567d-3f0f-804e-8005-b9d6907e3c8a": {
|
||||
"~:path": "",
|
||||
"~:color": "#0087ff",
|
||||
"~:name": "#0087ff",
|
||||
"~:modified-at": "~m1739365936355",
|
||||
"~:opacity": 1,
|
||||
"~:id": "~u84a1567d-3f0f-804e-8005-b9d6907e3c8a"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
101
frontend/playwright/data/workspace/get-file-fragment-10113.json
Normal file
101
frontend/playwright/data/workspace/get-file-fragment-10113.json
Normal file
|
@ -0,0 +1,101 @@
|
|||
{
|
||||
"~:id": "~u5b7ebd2b-2907-80db-8005-b9d67c21cbd3",
|
||||
"~:file-id": "~u5b7ebd2b-2907-80db-8005-b9d67c20cf2e",
|
||||
"~:created-at": "~m1739365911680",
|
||||
"~:data": {
|
||||
"~:options": {},
|
||||
"~:objects": {
|
||||
"~u00000000-0000-0000-0000-000000000000": {
|
||||
"~#shape": {
|
||||
"~:y": 0,
|
||||
"~:hide-fill-on-export": false,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:name": "Root Frame",
|
||||
"~:width": 0.01,
|
||||
"~:type": "~:frame",
|
||||
"~:points": [
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0.0,
|
||||
"~:y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0.01,
|
||||
"~:y": 0.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0.01,
|
||||
"~:y": 0.01
|
||||
}
|
||||
},
|
||||
{
|
||||
"~#point": {
|
||||
"~:x": 0.0,
|
||||
"~:y": 0.01
|
||||
}
|
||||
}
|
||||
],
|
||||
"~:r2": 0,
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:r3": 0,
|
||||
"~:r1": 0,
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [],
|
||||
"~:x": 0,
|
||||
"~:proportion": 1.0,
|
||||
"~:r4": 0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 0,
|
||||
"~:y": 0,
|
||||
"~:width": 0.01,
|
||||
"~:height": 0.01,
|
||||
"~:x1": 0,
|
||||
"~:y1": 0,
|
||||
"~:x2": 0.01,
|
||||
"~:y2": 0.01
|
||||
}
|
||||
},
|
||||
"~:fills": [
|
||||
{
|
||||
"~:fill-color": "#FFFFFF",
|
||||
"~:fill-opacity": 1
|
||||
}
|
||||
],
|
||||
"~:flip-x": null,
|
||||
"~:height": 0.01,
|
||||
"~:flip-y": null,
|
||||
"~:shapes": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:id": "~u5b7ebd2b-2907-80db-8005-b9d67c20cf2f",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"~:id": "~u5b7ebd2b-2907-80db-8005-b9d67c20cf2e",
|
||||
"~:name": "10113 - Emtpy lib",
|
||||
"~:is-shared": true
|
||||
}
|
|
@ -227,7 +227,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||
}
|
||||
|
||||
async openLibrariesModal(clickOptions = {}) {
|
||||
await this.sidebar.getByText("Libraries").click(clickOptions);
|
||||
await this.sidebar.getByTestId("libraries").click(clickOptions);
|
||||
await expect(this.librariesModal).toBeVisible();
|
||||
}
|
||||
|
||||
|
|
|
@ -72,3 +72,41 @@ test("Bug 9056 - 'More info' doesn't open the update tab", async ({ page }) => {
|
|||
/library updates/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("Bug 10113 - Empty library modal for non-empty library", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
|
||||
await workspace.setupEmptyFile(page);
|
||||
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-10113.json");
|
||||
await workspace.mockRPC(
|
||||
"get-file-fragment?file-id=*&fragment-id=*",
|
||||
"workspace/get-file-fragment-10113.json",
|
||||
);
|
||||
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-10113.json");
|
||||
await workspace.mockRPC(
|
||||
"get-team-shared-files?team-id=*",
|
||||
"workspace/get-team-shared-files-empty.json",
|
||||
);
|
||||
await workspace.mockRPC(
|
||||
"set-file-shared",
|
||||
"workspace/set-file-shared-10113.json",
|
||||
);
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "5b7ebd2b-2907-80db-8005-b9d67c20cf2e",
|
||||
pageId: "5b7ebd2b-2907-80db-8005-b9d67c20cf2f",
|
||||
});
|
||||
|
||||
await workspace.clickAssets();
|
||||
await workspace.openLibrariesModal();
|
||||
|
||||
await workspace.librariesModal
|
||||
.getByRole("button", { name: "Publish" })
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
workspace.page.getByText("Publish empty library"),
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
:controls :inline-actions
|
||||
:type :inline
|
||||
:level level
|
||||
:accept {:label (tr "Refresh")
|
||||
:accept {:label (tr "labels.refresh")
|
||||
:callback force-reload!}
|
||||
:tag :notification))
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.data.common :as dcm]
|
||||
[app.main.data.event :as ev]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.websocket :as dws]
|
||||
[app.main.features :as features]
|
||||
|
@ -248,15 +249,18 @@
|
|||
(ptk/reify ::create-project
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [unames (cfh/get-used-names (get state :projects))
|
||||
(let [team-id (:current-team-id state)
|
||||
projects (dsh/lookup-team-projects state team-id)
|
||||
unames (cfh/get-used-names projects)
|
||||
base-name (tr "dashboard.new-project-prefix")
|
||||
name (cfh/generate-unique-name base-name unames :immediate-suffix? true)
|
||||
team-id (:current-team-id state)
|
||||
params {:name name
|
||||
:team-id team-id}
|
||||
name (cfh/generate-unique-name base-name unames :immediate-suffix? true)
|
||||
team-id (:current-team-id state)
|
||||
params {:name name
|
||||
:team-id team-id}
|
||||
{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)]
|
||||
on-error rx/throw}}
|
||||
(meta params)]
|
||||
(->> (rp/cmd! :create-project params)
|
||||
(rx/tap on-success)
|
||||
(rx/map project-created)
|
||||
|
@ -465,10 +469,11 @@
|
|||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:files id] file)
|
||||
(assoc-in [:recent-files id] file)
|
||||
(update-in [:projects project-id :count] inc)))))
|
||||
(let [file (dissoc file :data)]
|
||||
(-> state
|
||||
(assoc-in [:files id] file)
|
||||
(assoc-in [:recent-files id] file)
|
||||
(update-in [:projects project-id :count] inc))))))
|
||||
|
||||
(defn create-file
|
||||
[{:keys [project-id name] :as params}]
|
||||
|
@ -482,8 +487,11 @@
|
|||
(watch [it state _]
|
||||
(let [{:keys [on-success on-error]
|
||||
:or {on-success identity
|
||||
on-error rx/throw}} (meta params)
|
||||
unames (cfh/get-used-names (get state :files))
|
||||
on-error rx/throw}}
|
||||
(meta params)
|
||||
|
||||
files (dsh/lookup-team-files state)
|
||||
unames (cfh/get-used-names files)
|
||||
base-name (tr "dashboard.new-file-prefix")
|
||||
name (or name
|
||||
(cfh/generate-unique-name base-name unames :immediate-suffix? true))
|
||||
|
@ -597,10 +605,10 @@
|
|||
pparams (:path-params route)
|
||||
in-project? (contains? pparams :project-id)
|
||||
name (if in-project?
|
||||
(let [files (get state :files)
|
||||
(let [files (dsh/lookup-team-files state team-id)
|
||||
unames (cfh/get-used-names files)]
|
||||
(cfh/generate-unique-name (tr "dashboard.new-file-prefix") unames :immediate-suffix? true))
|
||||
(let [projects (get state :projects)
|
||||
(let [projects (dsh/lookup-team-projects state team-id)
|
||||
unames (cfh/get-used-names projects)]
|
||||
(cfh/generate-unique-name (tr "dashboard.new-project-prefix") unames :immediate-suffix? true)))
|
||||
params (if in-project?
|
||||
|
|
|
@ -168,3 +168,21 @@
|
|||
[state]
|
||||
(when-let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
|
||||
(gpt/point (+ x (/ width 2)) (+ y (/ height 2)))))
|
||||
|
||||
(defn lookup-team-files
|
||||
([state]
|
||||
(lookup-team-files state (:current-team-id state)))
|
||||
([state team-id]
|
||||
(->> state
|
||||
:files
|
||||
(filter #(= team-id (:team-id (val %))))
|
||||
(into {}))))
|
||||
|
||||
(defn lookup-team-projects
|
||||
([state]
|
||||
(lookup-team-projects (:current-team-id state)))
|
||||
([state team-id]
|
||||
(->> state
|
||||
:projects
|
||||
(filter #(= team-id (:team-id (val %))))
|
||||
(into {}))))
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.logging :as log]
|
||||
[app.common.schema :as sm]
|
||||
[app.common.types.team :as ctt]
|
||||
|
@ -118,8 +119,10 @@
|
|||
(let [team-id (:current-team-id state)
|
||||
teams (get state :teams)
|
||||
team (get teams team-id)]
|
||||
(rx/of (set-current-team team)
|
||||
(fetch-members))))))
|
||||
(if (not team)
|
||||
(rx/throw (ex/error :type :authentication))
|
||||
(rx/of (set-current-team team)
|
||||
(fetch-members)))))))
|
||||
|
||||
(defn initialize-team
|
||||
[team-id]
|
||||
|
|
|
@ -319,8 +319,6 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(dissoc :files)
|
||||
(dissoc :workspace-ready)
|
||||
(assoc :recent-colors (:recent-colors storage/user))
|
||||
(assoc :recent-fonts (:recent-fonts storage/user))
|
||||
(assoc :current-file-id file-id)
|
||||
|
@ -395,12 +393,9 @@
|
|||
(dissoc
|
||||
:current-file-id
|
||||
:workspace-editor-state
|
||||
:files
|
||||
:workspace-media-objects
|
||||
:workspace-persistence
|
||||
:workspace-presence
|
||||
:workspace-tokens
|
||||
:workspace-ready
|
||||
:workspace-undo)
|
||||
(update :workspace-global dissoc :read-only?)
|
||||
(assoc-in [:workspace-global :options-mode] :design)))
|
||||
|
@ -428,18 +423,18 @@
|
|||
(defmethod ptk/resolve ::reload-current-file [_ _] (reload-current-file))
|
||||
|
||||
(defn initialize-page
|
||||
[page-id]
|
||||
(assert (uuid? page-id) "expected valid uuid for `page-id`")
|
||||
[file-id page-id]
|
||||
(assert (uuid? file-id) "expected valid uuid for `file-id`")
|
||||
|
||||
(ptk/reify ::initialize-page
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if-let [{:keys [id] :as page} (dsh/lookup-page state page-id)]
|
||||
(if-let [page (dsh/lookup-page state file-id page-id)]
|
||||
;; we maintain a cache of page state for user convenience with the exception of the
|
||||
;; selection; when user abandon the current page, the selection is lost
|
||||
(let [local (dm/get-in state [:workspace-cache id] default-workspace-local)]
|
||||
(let [local (dm/get-in state [:workspace-cache [file-id page-id]] default-workspace-local)]
|
||||
(-> state
|
||||
(assoc :current-page-id id)
|
||||
(assoc :current-page-id page-id)
|
||||
(assoc :workspace-local (assoc local :selected (d/ordered-set)))
|
||||
(assoc :workspace-trimmed-page (dm/select-keys page [:id :name]))
|
||||
|
||||
|
@ -451,24 +446,25 @@
|
|||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(if (dsh/lookup-page state page-id)
|
||||
(let [file-id (:current-file-id state)]
|
||||
(rx/of (preload-data-uris page-id)
|
||||
(dwth/watch-state-changes file-id page-id)
|
||||
(dwl/watch-component-changes)))
|
||||
(rx/of (dcm/go-to-workspace))))))
|
||||
(if (dsh/lookup-page state file-id page-id)
|
||||
(rx/of (preload-data-uris page-id)
|
||||
(dwth/watch-state-changes file-id page-id)
|
||||
(dwl/watch-component-changes))
|
||||
(rx/of (dcm/go-to-workspace :file-id file-id ::rt/replace true))))))
|
||||
|
||||
(defn finalize-page
|
||||
[page-id]
|
||||
[file-id page-id]
|
||||
(assert (uuid? file-id) "expected valid uuid for `file-id`")
|
||||
(assert (uuid? page-id) "expected valid uuid for `page-id`")
|
||||
|
||||
(ptk/reify ::finalize-page
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [local (-> (:workspace-local state)
|
||||
(dissoc :edition :edit-path :selected))
|
||||
exit? (not= :workspace (dm/get-in state [:route :data :name]))
|
||||
exit? (not= :workspace (rt/lookup-name state))
|
||||
state (-> state
|
||||
(update :workspace-cache assoc page-id local)
|
||||
(update :workspace-cache assoc [file-id page-id] local)
|
||||
(dissoc :current-page-id
|
||||
:workspace-local
|
||||
:workspace-trimmed-page
|
||||
|
|
|
@ -741,12 +741,12 @@
|
|||
redirect-to-page
|
||||
(fn [page-id shape-id]
|
||||
(rx/merge
|
||||
(rx/of (dcm/go-to-workspace :page-id page-id))
|
||||
(->> stream
|
||||
(rx/filter (ptk/type? ::initialize-page))
|
||||
(rx/filter (ptk/type? ::dw/initialize-page))
|
||||
(rx/take 1)
|
||||
(rx/observe-on :async)
|
||||
(rx/mapcat (fn [_] (select-and-zoom shape-id))))))]
|
||||
(rx/mapcat (fn [_] (select-and-zoom shape-id))))
|
||||
(rx/of (dcm/go-to-workspace :page-id page-id))))]
|
||||
|
||||
(when-let [component (dm/get-in data [:components id])]
|
||||
(let [page-id (:main-instance-page component)
|
||||
|
|
|
@ -120,6 +120,11 @@
|
|||
([id params & {:as options}]
|
||||
(navigate id params options)))
|
||||
|
||||
(defn lookup-name
|
||||
[state]
|
||||
(dm/get-in state [:route :data :name]))
|
||||
|
||||
;; FIXME: rename to lookup-params
|
||||
(defn get-params
|
||||
[state]
|
||||
(dm/get-in state [:route :params :query]))
|
||||
|
|
|
@ -127,7 +127,6 @@
|
|||
{::mf/props :obj
|
||||
::mf/private true}
|
||||
[{:keys [team-id children]}]
|
||||
|
||||
(mf/with-effect [team-id]
|
||||
(st/emit! (dtm/initialize-team team-id))
|
||||
(fn []
|
||||
|
|
|
@ -237,9 +237,14 @@
|
|||
(str/last-index-of (subs node-text 0 offset) "@")
|
||||
|
||||
mention-text
|
||||
(subs node-text current-at-symbol)]
|
||||
(subs node-text current-at-symbol)
|
||||
|
||||
(if (re-matches #"@\w*" mention-text)
|
||||
at-symbol-inside-word?
|
||||
(and (> current-at-symbol 0)
|
||||
(str/word? (str/slice node-text (- current-at-symbol 1) current-at-symbol)))]
|
||||
|
||||
(if (and (not at-symbol-inside-word?)
|
||||
(re-matches #"@\w*" mention-text))
|
||||
(do
|
||||
(reset! cur-mention mention-text)
|
||||
(rx/push! mentions-s {:type :display-mentions})
|
||||
|
@ -305,6 +310,17 @@
|
|||
(when (fn? on-change)
|
||||
(on-change (parse-nodes node))))))))
|
||||
|
||||
handle-insert-at-symbol
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(let [node (mf/ref-val local-ref) [span-node] (current-text-node node)]
|
||||
(when span-node
|
||||
(let [node-text (dom/get-text span-node)
|
||||
at-symbol (if (blank-content? node-text) "@" " @")]
|
||||
|
||||
(dom/set-html! span-node (str/concat node-text at-symbol))
|
||||
(wapi/set-cursor-after! span-node))))))
|
||||
|
||||
handle-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps on-esc on-ctrl-enter handle-select handle-input)
|
||||
|
@ -386,6 +402,8 @@
|
|||
(case type
|
||||
:insert-mention
|
||||
(handle-insert-mention data)
|
||||
:insert-at-symbol
|
||||
(handle-insert-at-symbol)
|
||||
|
||||
nil))))))
|
||||
|
||||
|
@ -521,15 +539,25 @@
|
|||
{::mf/props :obj
|
||||
::mf/private true}
|
||||
[]
|
||||
(let [mentions-s (mf/use-ctx mentions-context)
|
||||
(let [mentions-s (mf/use-ctx mentions-context)
|
||||
display-mentions* (mf/use-state false)
|
||||
|
||||
handle-mouse-down
|
||||
handle-pointer-down
|
||||
(mf/use-fn
|
||||
(mf/deps @display-mentions*)
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(rx/push! mentions-s {:type :display-mentions})))]
|
||||
(if @display-mentions*
|
||||
(rx/push! mentions-s {:type :hide-mentions})
|
||||
(rx/push! mentions-s {:type :insert-at-symbol}))))
|
||||
|
||||
handle-key-down
|
||||
(mf/use-fn
|
||||
(mf/deps @display-mentions*)
|
||||
(fn [event]
|
||||
(when (or (kbd/enter? event) (kbd/space? event))
|
||||
(handle-pointer-down event))))]
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
|
@ -545,8 +573,9 @@
|
|||
|
||||
[:> icon-button*
|
||||
{:variant "ghost"
|
||||
:aria-label (tr "labels.options")
|
||||
:on-pointer-down handle-mouse-down
|
||||
:aria-label (tr "labels.mention")
|
||||
:on-pointer-down handle-pointer-down
|
||||
:on-key-down handle-key-down
|
||||
:icon-class (stl/css-case :open-mentions-button true
|
||||
:is-toggled @display-mentions*)
|
||||
:icon "at"}]))
|
||||
|
|
|
@ -255,7 +255,6 @@
|
|||
}
|
||||
|
||||
.open-mentions-button {
|
||||
cursor: pointer;
|
||||
stroke: none;
|
||||
fill: var(--color-foreground-secondary);
|
||||
|
||||
|
|
|
@ -149,7 +149,6 @@
|
|||
[:input {:type "text"
|
||||
:value (:token created "")
|
||||
:class (stl/css :custom-input-token)
|
||||
:placeholder (tr "modals.create-access-token.token")
|
||||
:read-only true}]
|
||||
[:button {:title (tr "modals.create-access-token.copy-token")
|
||||
:class (stl/css :copy-btn)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.common :as dcm]
|
||||
[app.main.data.helpers :as dsh]
|
||||
[app.main.data.persistence :as dps]
|
||||
[app.main.data.plugins :as dpl]
|
||||
[app.main.data.workspace :as dw]
|
||||
|
@ -43,13 +42,6 @@
|
|||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- make-workspace-ready-ref
|
||||
[file-id]
|
||||
(l/derived (fn [state]
|
||||
(and (= file-id (:workspace-ready state))
|
||||
(some? (dsh/lookup-file-data state file-id))))
|
||||
st/state))
|
||||
|
||||
(mf/defc workspace-content*
|
||||
{::mf/private true}
|
||||
[{:keys [file layout page wglobal]}]
|
||||
|
@ -138,22 +130,18 @@
|
|||
key (events/listen globals/window "blur" focus-out)]
|
||||
(partial events/unlistenByKey key)))
|
||||
|
||||
(mf/with-effect [page-id]
|
||||
(if (some? page-id)
|
||||
(st/emit! (dw/initialize-page page-id))
|
||||
(st/emit! (dcm/go-to-workspace ::rt/replace true)))
|
||||
|
||||
(mf/with-effect [file page-id]
|
||||
(st/emit! (dw/initialize-page (:id file) page-id))
|
||||
(fn []
|
||||
(when (some? page-id)
|
||||
(st/emit! (dw/finalize-page page-id)))))
|
||||
(when page-id
|
||||
(st/emit! (dw/finalize-page (:id file) page-id)))))
|
||||
|
||||
(if (some? page)
|
||||
[:> workspace-content* {:file file
|
||||
:page page
|
||||
:wglobal wglobal
|
||||
:layout layout}]
|
||||
[:& workspace-loader*])))
|
||||
|
||||
[:> workspace-loader*])))
|
||||
|
||||
(def ^:private ref:file-without-data
|
||||
(l/derived (fn [file]
|
||||
|
@ -181,10 +169,6 @@
|
|||
read-only? (mf/deref refs/workspace-read-only?)
|
||||
read-only? (or read-only? (not (:can-edit permissions)))
|
||||
|
||||
ready* (mf/with-memo [file-id]
|
||||
(make-workspace-ready-ref file-id))
|
||||
ready? (mf/deref ready*)
|
||||
|
||||
design-tokens? (features/use-feature "design-tokens/v1")
|
||||
|
||||
background-color (:background-color wglobal)]
|
||||
|
@ -207,6 +191,10 @@
|
|||
(st/emit! ::dps/force-persist
|
||||
(dw/finalize-workspace file-id))))
|
||||
|
||||
(mf/with-effect [file page-id]
|
||||
(when-not page-id
|
||||
(st/emit! (dcm/go-to-workspace :file-id file-id ::rt/replace true))))
|
||||
|
||||
[:> (mf/provider ctx/current-project-id) {:value project-id}
|
||||
[:> (mf/provider ctx/current-file-id) {:value file-id}
|
||||
[:> (mf/provider ctx/current-page-id) {:value page-id}
|
||||
|
@ -219,9 +207,10 @@
|
|||
:touch-action "none"}}
|
||||
[:> context-menu*]
|
||||
|
||||
(if ^boolean ready?
|
||||
[:> workspace-page* {:page-id page-id
|
||||
:file file
|
||||
:wglobal wglobal
|
||||
:layout layout}]
|
||||
(if (some? file)
|
||||
[:> workspace-page*
|
||||
{:page-id page-id
|
||||
:file file
|
||||
:wglobal wglobal
|
||||
:layout layout}]
|
||||
[:> workspace-loader*])]]]]]]]))
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
[app.common.types.file :as ctf]
|
||||
[app.common.types.typographies-list :as ctyl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.dashboard :as dd]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.notifications :as ntf]
|
||||
|
@ -176,15 +175,7 @@
|
|||
(defn- empty-library?
|
||||
"Check if currentt library summary has elements or not"
|
||||
[summary]
|
||||
(let [colors (or (-> summary :colors :count) 0)
|
||||
components (or (-> summary :components :count) 0)
|
||||
media (or (-> summary :media :count) 0)
|
||||
typographies (or (-> summary :typographies :count) 0)]
|
||||
|
||||
(and (zero? colors)
|
||||
(zero? components)
|
||||
(zero? media)
|
||||
(zero? typographies))))
|
||||
(boolean (:is-empty summary)))
|
||||
|
||||
(mf/defc libraries-tab*
|
||||
{::mf/props :obj
|
||||
|
@ -362,7 +353,7 @@
|
|||
(nil? shared-libraries)
|
||||
(tr "workspace.libraries.loading")
|
||||
|
||||
(and (str/empty? search-term) (cf/external-feature-flag "templates-03" "test"))
|
||||
(str/empty? search-term)
|
||||
[:*
|
||||
[:div {:class (stl/css :sample-libraries-info)}
|
||||
(tr "workspace.libraries.empty.no-libraries")
|
||||
|
@ -377,19 +368,6 @@
|
|||
{:library library
|
||||
:importing importing*}])]]
|
||||
|
||||
(str/empty? search-term)
|
||||
[:*
|
||||
[:span {:class (stl/css :empty-state-icon)}
|
||||
library-icon]
|
||||
(tr "workspace.libraries.no-shared-libraries-available")
|
||||
(when (cf/external-feature-flag "templates-01" "test")
|
||||
[:div {:class (stl/css :templates-info)}
|
||||
(tr "workspace.libraries.more-templates")
|
||||
[:a {:target "_blank"
|
||||
:class (stl/css :templates-info-link)
|
||||
:href "https://penpot.app/libraries-templates"}
|
||||
(tr "workspace.libraries.more-templates-link")]])]
|
||||
|
||||
:else
|
||||
(tr "workspace.libraries.no-matches-for" search-term))]))]]))
|
||||
|
||||
|
|
|
@ -326,16 +326,6 @@
|
|||
padding: $s-0 $s-16;
|
||||
}
|
||||
|
||||
.templates-info {
|
||||
color: var(--color-accent-primary);
|
||||
}
|
||||
|
||||
.templates-info-link {
|
||||
color: var(--color-accent-primary);
|
||||
text-decoration: underline;
|
||||
font-weight: $fw400;
|
||||
}
|
||||
|
||||
.sample-libraries-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.config :as cf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.assets :as dwa]
|
||||
|
@ -92,12 +91,6 @@
|
|||
reverse-sort? (= :desc ordering)
|
||||
num-libs (count (mf/deref refs/libraries))
|
||||
|
||||
show-templates-04-test1?
|
||||
(and (cf/external-feature-flag "templates-04" "test1") (zero? num-libs))
|
||||
|
||||
show-templates-04-test2?
|
||||
(and (cf/external-feature-flag "templates-04" "test2") (zero? num-libs))
|
||||
|
||||
toggle-ordering
|
||||
(mf/use-fn
|
||||
(mf/deps ordering)
|
||||
|
@ -166,18 +159,12 @@
|
|||
[:article {:class (stl/css :assets-bar)}
|
||||
[:div {:class (stl/css :assets-header)}
|
||||
(when-not ^boolean read-only?
|
||||
(cond
|
||||
show-templates-04-test1?
|
||||
[:button {:class (stl/css :libraries-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
(tr "workspace.assets.add-library")]
|
||||
show-templates-04-test2?
|
||||
(if (= num-libs 1)
|
||||
[:button {:class (stl/css :add-library-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
(tr "workspace.assets.add-library")]
|
||||
:else
|
||||
|
||||
[:button {:class (stl/css :libraries-button)
|
||||
:on-click show-libraries-dialog
|
||||
:data-testid "libraries"}
|
||||
|
|
|
@ -534,7 +534,7 @@
|
|||
:on-ungroup on-ungroup
|
||||
:on-context-menu on-context-menu
|
||||
:selected-full selected-full
|
||||
:local ^boolean is-local}])
|
||||
:is-local ^boolean is-local}])
|
||||
|
||||
[:& cmm/assets-context-menu
|
||||
{:on-close on-close-menu
|
||||
|
|
|
@ -77,12 +77,12 @@
|
|||
[:& radio-button {:icon i/boolean-intersection
|
||||
:value "intersection"
|
||||
:disabled disabled-bool-btns
|
||||
:title (str (tr "intersection") " (" (sc/get-tooltip :bool-intersection) ")")
|
||||
:title (str (tr "workspace.shape.menu.intersection") " (" (sc/get-tooltip :bool-intersection) ")")
|
||||
:id "bool-opt-intersection"}]
|
||||
[:& radio-button {:icon i/boolean-exclude
|
||||
:value "exclude"
|
||||
:disabled disabled-bool-btns
|
||||
:title (str (tr "exclude") " (" (sc/get-tooltip :bool-exclude) ")")
|
||||
:title (str (tr "workspace.shape.menu.exclude") " (" (sc/get-tooltip :bool-exclude) ")")
|
||||
:id "bool-opt-exclude"}]]]
|
||||
|
||||
[:button
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
(dom/prevent-default e)
|
||||
(dom/stop-propagation e)
|
||||
(st/emit! (wdt/toggle-token-theme-active? group name)))}
|
||||
[:& switch {:name (tr "workspace.token.theme" name)
|
||||
[:& switch {:name (tr "workspace.token.theme-name" name)
|
||||
:on-change (constantly nil)
|
||||
:selected? selected?}]]
|
||||
[:> text* {:as "span" :typography "body-medium" :class (stl/css :theme-name)} name]]
|
||||
|
|
|
@ -63,10 +63,10 @@
|
|||
target-container-id (or target-container-id (:parent-id shape))]
|
||||
|
||||
(filter some?
|
||||
[(when target-page-id (dw/initialize-page target-page-id))
|
||||
[(when target-page-id (dw/initialize-page (:id file) target-page-id))
|
||||
(dws/select-shape target-container-id)
|
||||
(dw/paste-shapes pdata)
|
||||
(when target-page-id (dw/initialize-page (:id page)))])))
|
||||
(when target-page-id (dw/initialize-page (:id file) (:id page)))])))
|
||||
|
||||
(defn- sync-file [file]
|
||||
(map (fn [component-tag]
|
||||
|
|
|
@ -12,9 +12,8 @@ msgstr ""
|
|||
"X-Generator: Weblate 5.10-dev\n"
|
||||
|
||||
#: src/app/main/data/common.cljs:87
|
||||
#, fuzzy
|
||||
msgid "Refresh"
|
||||
msgstr ""
|
||||
msgid "labels.refresh"
|
||||
msgstr "Refresh"
|
||||
|
||||
#: src/app/main/ui/auth/register.cljs:133, src/app/main/ui/static.cljs:155, src/app/main/ui/viewer/login.cljs:98
|
||||
msgid "auth.already-have-account"
|
||||
|
@ -1386,11 +1385,6 @@ msgstr "Email or password is incorrect."
|
|||
msgid "errors.wrong-old-password"
|
||||
msgstr "Old password is incorrect"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:85
|
||||
#, fuzzy
|
||||
msgid "exclude"
|
||||
msgstr ""
|
||||
|
||||
#: src/app/main/ui/settings/feedback.cljs:74
|
||||
msgid "feedback.description"
|
||||
msgstr "Description"
|
||||
|
@ -1691,11 +1685,6 @@ msgstr "Text"
|
|||
msgid "inspect.tabs.info"
|
||||
msgstr "Info"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/bool.cljs:80
|
||||
#, fuzzy
|
||||
msgid "intersection"
|
||||
msgstr ""
|
||||
|
||||
#: src/app/main/ui/workspace/main_menu.cljs:162
|
||||
msgid "label.shortcuts"
|
||||
msgstr "Shortcuts"
|
||||
|
@ -2020,6 +2009,10 @@ msgstr "Member"
|
|||
msgid "labels.members"
|
||||
msgstr "Members"
|
||||
|
||||
#: src/app/main/ui/comments.cljs:558
|
||||
msgid "labels.mention"
|
||||
msgstr "Mention"
|
||||
|
||||
#: src/app/main/ui/settings/password.cljs:84
|
||||
msgid "labels.new-password"
|
||||
msgstr "New password"
|
||||
|
@ -2458,11 +2451,6 @@ msgstr "Create token"
|
|||
msgid "modals.create-access-token.title"
|
||||
msgstr "Generate access token"
|
||||
|
||||
#: src/app/main/ui/settings/access_tokens.cljs:152
|
||||
#, fuzzy
|
||||
msgid "modals.create-access-token.token"
|
||||
msgstr ""
|
||||
|
||||
#: src/app/main/ui/dashboard/team.cljs:921
|
||||
msgid "modals.create-webhook.submit-label"
|
||||
msgstr "Create webhook"
|
||||
|
@ -4806,10 +4794,6 @@ msgstr "Loading…"
|
|||
msgid "workspace.libraries.more-templates"
|
||||
msgstr "You can look for "
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:391
|
||||
msgid "workspace.libraries.more-templates-link"
|
||||
msgstr "more templates in here"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:481
|
||||
msgid "workspace.libraries.no-libraries-need-sync"
|
||||
msgstr "There are no Shared Libraries that need update"
|
||||
|
@ -4818,10 +4802,6 @@ msgstr "There are no Shared Libraries that need update"
|
|||
msgid "workspace.libraries.no-matches-for"
|
||||
msgstr "No matches found for “%s“"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:384
|
||||
msgid "workspace.libraries.no-shared-libraries-available"
|
||||
msgstr "There are no Shared Libraries available"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:337
|
||||
msgid "workspace.libraries.search-shared-libraries"
|
||||
msgstr "Search shared libraries"
|
||||
|
@ -6625,11 +6605,6 @@ msgstr "Select set."
|
|||
msgid "workspace.token.set-selection-theme"
|
||||
msgstr "Define what token sets should be used as part of this theme option:"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs:123
|
||||
#, fuzzy
|
||||
msgid "workspace.token.theme"
|
||||
msgstr ""
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/modals/themes.cljs
|
||||
#, unused
|
||||
msgid "workspace.token.theme-name"
|
||||
|
|
|
@ -2015,6 +2015,10 @@ msgstr "Integrante"
|
|||
msgid "labels.members"
|
||||
msgstr "Integrantes"
|
||||
|
||||
#: src/app/main/ui/comments.cljs:558
|
||||
msgid "labels.mention"
|
||||
msgstr "Mencionar"
|
||||
|
||||
#: src/app/main/ui/settings/password.cljs:84
|
||||
msgid "labels.new-password"
|
||||
msgstr "Nueva contraseña"
|
||||
|
@ -4800,10 +4804,6 @@ msgstr "Cargando…"
|
|||
msgid "workspace.libraries.more-templates"
|
||||
msgstr "Puedes buscar "
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:391
|
||||
msgid "workspace.libraries.more-templates-link"
|
||||
msgstr "más plantillas aquí"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:481
|
||||
msgid "workspace.libraries.no-libraries-need-sync"
|
||||
msgstr "No hay bibliotecas que necesiten ser actualizadas"
|
||||
|
@ -4812,10 +4812,6 @@ msgstr "No hay bibliotecas que necesiten ser actualizadas"
|
|||
msgid "workspace.libraries.no-matches-for"
|
||||
msgstr "No se encuentra “%s“"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:384
|
||||
msgid "workspace.libraries.no-shared-libraries-available"
|
||||
msgstr "No hay bibliotecas compartidas disponibles"
|
||||
|
||||
#: src/app/main/ui/workspace/libraries.cljs:337
|
||||
msgid "workspace.libraries.search-shared-libraries"
|
||||
msgstr "Buscar bibliotecas compartidas"
|
||||
|
|
Loading…
Add table
Reference in a new issue