Skip to content

Commit 132bdb1

Browse files
alecmevjonastreub
andcommitted
Address feedback
Co-authored-by: Jonas Treub <[email protected]>
1 parent 03cfb14 commit 132bdb1

File tree

6 files changed

+56
-54
lines changed

6 files changed

+56
-54
lines changed

package-lock.json

+4-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

starters/cms/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"typescript": "^5.7.3",
3535
"typescript-eslint": "^8.25.0",
3636
"vite": "^6.2.4",
37-
"vite-plugin-framer": "^1.0.6",
37+
"vite-plugin-framer": "^1.0.7",
3838
"vite-plugin-mkcert": "^1.17.7"
3939
}
4040
}

starters/cms/src/App.css

+6-5
Original file line numberDiff line numberDiff line change
@@ -147,11 +147,11 @@ form {
147147
box-shadow: none;
148148
}
149149

150-
[data-framer-theme=light] .mapping .source-field input[type="checkbox"]:not(:checked) {
150+
[data-framer-theme="light"] .mapping .source-field input[type="checkbox"]:not(:checked) {
151151
background: #ccc;
152152
}
153153

154-
[data-framer-theme=dark] .mapping .source-field input[type="checkbox"]:not(:checked) {
154+
[data-framer-theme="dark"] .mapping .source-field input[type="checkbox"]:not(:checked) {
155155
background: #666;
156156
}
157157

@@ -163,9 +163,6 @@ form {
163163
background-color: var(--framer-color-bg);
164164
margin-top: auto;
165165
padding-bottom: 15px;
166-
display: flex;
167-
flex-direction: column;
168-
gap: 15px;
169166
}
170167

171168
.mapping footer::before {
@@ -178,3 +175,7 @@ form {
178175
background: linear-gradient(to bottom, transparent, var(--framer-color-bg));
179176
pointer-events: none;
180177
}
178+
179+
.mapping footer > hr {
180+
margin-bottom: 15px;
181+
}

starters/cms/src/FieldMapping.tsx

+29-21
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1-
import { type EditableManagedCollectionField, framer, type ManagedCollection, useIsAllowedTo } from "framer-plugin"
1+
import { type ManagedCollectionFieldInput, framer, type ManagedCollection, useIsAllowedTo } from "framer-plugin"
22
import { useEffect, useState } from "react"
33
import { type DataSource, dataSourceOptions, mergeFieldsWithExistingFields, syncCollection, syncMethods } from "./data"
44

55
interface FieldMappingRowProps {
6-
field: EditableManagedCollectionField
6+
field: ManagedCollectionFieldInput
77
originalFieldName: string | undefined
8-
disabled: boolean
8+
isIgnored: boolean
99
onToggleDisabled: (fieldId: string) => void
1010
onNameChange: (fieldId: string, name: string) => void
11+
disabled: boolean
1112
}
1213

13-
function FieldMappingRow({ field, originalFieldName, disabled, onToggleDisabled, onNameChange }: FieldMappingRowProps) {
14+
function FieldMappingRow({
15+
field,
16+
originalFieldName,
17+
isIgnored,
18+
onToggleDisabled,
19+
onNameChange,
20+
disabled,
21+
}: FieldMappingRowProps) {
1422
return (
1523
<>
1624
<button
1725
type="button"
1826
className="source-field"
19-
aria-disabled={disabled}
27+
aria-disabled={isIgnored}
2028
onClick={() => onToggleDisabled(field.id)}
2129
tabIndex={0}
30+
disabled={disabled}
2231
>
23-
<input type="checkbox" checked={!disabled} tabIndex={-1} readOnly />
32+
<input type="checkbox" checked={!isIgnored} tabIndex={-1} readOnly />
2433
<span>{originalFieldName ?? field.id}</span>
2534
</button>
2635
<svg xmlns="http://www.w3.org/2000/svg" width="8" height="8" fill="none">
@@ -35,8 +44,8 @@ function FieldMappingRow({ field, originalFieldName, disabled, onToggleDisabled,
3544
</svg>
3645
<input
3746
type="text"
38-
style={{ width: "100%", opacity: disabled ? 0.5 : 1 }}
39-
disabled={disabled}
47+
style={{ width: "100%", opacity: isIgnored || disabled ? 0.5 : 1 }}
48+
disabled={isIgnored || disabled}
4049
placeholder={field.id}
4150
value={field.name}
4251
onChange={event => onNameChange(field.id, event.target.value)}
@@ -50,7 +59,7 @@ function FieldMappingRow({ field, originalFieldName, disabled, onToggleDisabled,
5059
)
5160
}
5261

53-
const initialManagedCollectionFields: EditableManagedCollectionField[] = []
62+
const initialManagedCollectionFields: ManagedCollectionFieldInput[] = []
5463
const initialFieldIds: ReadonlySet<string> = new Set()
5564

5665
interface FieldMappingProps {
@@ -68,7 +77,7 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie
6877

6978
const [possibleSlugFields] = useState(() => dataSource.fields.filter(field => field.type === "string"))
7079

71-
const [selectedSlugField, setSelectedSlugField] = useState<EditableManagedCollectionField | null>(
80+
const [selectedSlugField, setSelectedSlugField] = useState<ManagedCollectionFieldInput | null>(
7281
possibleSlugFields.find(field => field.id === initialSlugFieldId) ?? possibleSlugFields[0] ?? null
7382
)
7483

@@ -137,12 +146,6 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie
137146
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
138147
event.preventDefault()
139148

140-
if (!isAllowedToManage) {
141-
console.error("Insufficient permissions for management")
142-
framer.notify("Insufficient permissions for management", { variant: "error" })
143-
return
144-
}
145-
146149
if (!selectedSlugField) {
147150
// This can't happen because the form will not submit if no slug field is selected
148151
// but TypeScript can't infer that.
@@ -196,6 +199,8 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie
196199
if (!selectedField) return
197200
setSelectedSlugField(selectedField)
198201
}}
202+
disabled={!isAllowedToManage}
203+
style={{ opacity: isAllowedToManage ? 1 : 0.5 }}
199204
>
200205
{possibleSlugFields.map(possibleSlugField => {
201206
return (
@@ -215,24 +220,27 @@ export function FieldMapping({ collection, dataSource, initialSlugFieldId }: Fie
215220
key={`field-${field.id}`}
216221
field={field}
217222
originalFieldName={dataSource.fields.find(sourceField => sourceField.id === field.id)?.name}
218-
disabled={ignoredFieldIds.has(field.id)}
223+
isIgnored={ignoredFieldIds.has(field.id)}
219224
onToggleDisabled={toggleFieldDisabledState}
220225
onNameChange={changeFieldName}
226+
disabled={!isAllowedToManage}
221227
/>
222228
))}
223229
</div>
224230

225231
<footer>
226232
<hr />
227-
<button disabled={isSyncing || !isAllowedToManage} tabIndex={0}>
233+
<button
234+
disabled={isSyncing || !isAllowedToManage}
235+
tabIndex={0}
236+
title={isAllowedToManage ? undefined : "Insufficient permissions"}
237+
>
228238
{isSyncing ? (
229239
<div className="framer-spinner" />
230-
) : isAllowedToManage ? (
240+
) : (
231241
<span>
232242
Import <span style={{ textTransform: "capitalize" }}>{dataSourceName}</span>
233243
</span>
234-
) : (
235-
"Insufficient permissions"
236244
)}
237245
</button>
238246
</footer>

starters/cms/src/data.ts

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
type EditableManagedCollectionField,
2+
type ManagedCollectionFieldInput,
33
type FieldDataInput,
44
framer,
55
type ManagedCollection,
@@ -13,7 +13,7 @@ export const PLUGIN_KEYS = {
1313

1414
export interface DataSource {
1515
id: string
16-
fields: readonly EditableManagedCollectionField[]
16+
fields: readonly ManagedCollectionFieldInput[]
1717
items: FieldDataInput[]
1818
}
1919

@@ -44,7 +44,7 @@ export async function getDataSource(dataSourceId: string, abortSignal?: AbortSig
4444
const dataSource = await dataSourceResponse.json()
4545

4646
// Map your source fields to supported field types in Framer
47-
const fields: EditableManagedCollectionField[] = []
47+
const fields: ManagedCollectionFieldInput[] = []
4848
for (const field of dataSource.fields) {
4949
switch (field.type) {
5050
case "string":
@@ -83,9 +83,9 @@ export async function getDataSource(dataSourceId: string, abortSignal?: AbortSig
8383
}
8484

8585
export function mergeFieldsWithExistingFields(
86-
sourceFields: readonly EditableManagedCollectionField[],
87-
existingFields: readonly EditableManagedCollectionField[]
88-
): EditableManagedCollectionField[] {
86+
sourceFields: readonly ManagedCollectionFieldInput[],
87+
existingFields: readonly ManagedCollectionFieldInput[]
88+
): ManagedCollectionFieldInput[] {
8989
return sourceFields.map(sourceField => {
9090
const existingField = existingFields.find(existingField => existingField.id === sourceField.id)
9191
if (existingField) {
@@ -98,8 +98,8 @@ export function mergeFieldsWithExistingFields(
9898
export async function syncCollection(
9999
collection: ManagedCollection,
100100
dataSource: DataSource,
101-
fields: readonly EditableManagedCollectionField[],
102-
slugField: EditableManagedCollectionField
101+
fields: readonly ManagedCollectionFieldInput[],
102+
slugField: ManagedCollectionFieldInput
103103
) {
104104
const items: ManagedCollectionItemInput[] = []
105105
const unsyncedItems = new Set(await collection.getItemIds())
@@ -143,9 +143,8 @@ export async function syncCollection(
143143
await collection.setPluginData(PLUGIN_KEYS.SLUG_FIELD_ID, slugField.id)
144144
}
145145

146+
// TODO: ProtectedMethod[]
146147
export const syncMethods = [
147-
"ManagedCollection.getFields",
148-
"ManagedCollection.getItemIds",
149148
"ManagedCollection.removeItems",
150149
"ManagedCollection.addItems",
151150
"ManagedCollection.setPluginData",

starters/cms/src/main.tsx

+7-13
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@ import { createRoot } from "react-dom/client"
66
import { App } from "./App.tsx"
77
import { PLUGIN_KEYS, syncExistingCollection, syncMethods } from "./data"
88

9-
if (!framer.isAllowedTo("getActiveManagedCollection", "ManagedCollection.getPluginData")) {
10-
await framer.closePlugin("Insufficient permissions", { variant: "error" })
11-
throw new Error("Insufficient permissions for getActiveManagedCollection and ManagedCollection.getPluginData")
12-
}
13-
149
const activeCollection = await framer.getActiveManagedCollection()
1510
const previousDataSourceId = await activeCollection.getPluginData(PLUGIN_KEYS.DATA_SOURCE_ID)
1611
const previousSlugFieldId = await activeCollection.getPluginData(PLUGIN_KEYS.SLUG_FIELD_ID)
@@ -31,14 +26,13 @@ function launch() {
3126
}
3227

3328
if (framer.mode === "syncManagedCollection") {
34-
if (!framer.isAllowedTo(...syncMethods)) {
35-
await framer.closePlugin("Insufficient permissions for synchronization", { variant: "error" })
36-
throw new Error("Insufficient permissions for synchronization")
37-
}
38-
39-
const { didSync } = await syncExistingCollection(activeCollection, previousDataSourceId, previousSlugFieldId)
40-
if (didSync) {
41-
await framer.closePlugin("Synchronization successful", { variant: "success" })
29+
if (framer.isAllowedTo(...syncMethods)) {
30+
const { didSync } = await syncExistingCollection(activeCollection, previousDataSourceId, previousSlugFieldId)
31+
if (didSync) {
32+
await framer.closePlugin("Synchronization successful", { variant: "success" })
33+
} else {
34+
launch()
35+
}
4236
} else {
4337
launch()
4438
}

0 commit comments

Comments
 (0)