UNPKG

tinacms

Version:

[![GitHub license](https://img.shields.io/github/license/tinacms/tinacms?color=blue)](https://github.com/tinacms/tinacms/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/tinacms.svg?style=flat)](https://www.npmjs.com/package/tinacms) [![Bui

253 lines (252 loc) • 7.79 kB
import React from "react"; function useTina(props) { const stringifiedQuery = JSON.stringify({ query: props.query, variables: props.variables }); const id = React.useMemo( () => hashFromQuery(stringifiedQuery), [stringifiedQuery] ); const processedData = React.useMemo(() => { const dataCopy = JSON.parse(JSON.stringify(props.data)); return addMetadata(id, dataCopy, []); }, [props.data, id]); const [data, setData] = React.useState(processedData); const [isClient, setIsClient] = React.useState(false); const [quickEditEnabled, setQuickEditEnabled] = React.useState(false); const [isInTinaIframe, setIsInTinaIframe] = React.useState(false); React.useEffect(() => { setIsClient(true); setData(processedData); parent.postMessage({ type: "url-changed" }); }, [id, processedData]); React.useEffect(() => { if (quickEditEnabled) { let mouseDownHandler = function(e) { const attributeNames = e.target.getAttributeNames(); const tinaAttribute = attributeNames.find( (name) => name.startsWith("data-tina-field") ); let fieldName; if (tinaAttribute) { e.preventDefault(); e.stopPropagation(); fieldName = e.target.getAttribute(tinaAttribute); } else { const ancestor = e.target.closest( "[data-tina-field], [data-tina-field-overlay]" ); if (ancestor) { const attributeNames2 = ancestor.getAttributeNames(); const tinaAttribute2 = attributeNames2.find( (name) => name.startsWith("data-tina-field") ); if (tinaAttribute2) { e.preventDefault(); e.stopPropagation(); fieldName = ancestor.getAttribute(tinaAttribute2); } } } if (fieldName) { if (isInTinaIframe) { parent.postMessage( { type: "field:selected", fieldName }, window.location.origin ); } } }; const style = document.createElement("style"); style.type = "text/css"; style.textContent = ` [data-tina-field] { outline: 2px dashed rgba(34,150,254,0.5); transition: box-shadow ease-out 150ms; } [data-tina-field]:hover { box-shadow: inset 100vi 100vh rgba(34,150,254,0.3); outline: 2px solid rgba(34,150,254,1); cursor: pointer; } [data-tina-field-overlay] { outline: 2px dashed rgba(34,150,254,0.5); position: relative; } [data-tina-field-overlay]:hover { cursor: pointer; outline: 2px solid rgba(34,150,254,1); } [data-tina-field-overlay]::after { content: ''; position: absolute; inset: 0; z-index: 20; transition: opacity ease-out 150ms; background-color: rgba(34,150,254,0.3); opacity: 0; } [data-tina-field-overlay]:hover::after { opacity: 1; } `; document.head.appendChild(style); document.body.classList.add("__tina-quick-editing-enabled"); document.addEventListener("click", mouseDownHandler, true); return () => { document.removeEventListener("click", mouseDownHandler, true); document.body.classList.remove("__tina-quick-editing-enabled"); style.remove(); }; } }, [quickEditEnabled, isInTinaIframe]); React.useEffect(() => { if (props == null ? void 0 : props.experimental___selectFormByFormId) { parent.postMessage({ type: "user-select-form", formId: props.experimental___selectFormByFormId() }); } }, [id]); React.useEffect(() => { const { experimental___selectFormByFormId, ...rest } = props; parent.postMessage({ type: "open", ...rest, id }, window.location.origin); const handleMessage = (event) => { if (event.data.type === "quickEditEnabled") { setQuickEditEnabled(event.data.value); } if (event.data.id === id && event.data.type === "updateData") { const rawData = event.data.data; const newlyProcessedData = addMetadata( id, JSON.parse(JSON.stringify(rawData)), [] ); setData(newlyProcessedData); setIsInTinaIframe(true); const anyTinaField = document.querySelector("[data-tina-field]"); if (anyTinaField) { parent.postMessage( { type: "quick-edit", value: true }, window.location.origin ); } else { parent.postMessage( { type: "quick-edit", value: false }, window.location.origin ); } } }; window.addEventListener("message", handleMessage); return () => { window.removeEventListener("message", handleMessage); parent.postMessage({ type: "close", id }, window.location.origin); }; }, [id, setQuickEditEnabled]); return { data, isClient }; } function useEditState() { const [edit, setEdit] = React.useState(false); React.useEffect(() => { if (typeof window !== "undefined") { parent.postMessage({ type: "isEditMode" }, window.location.origin); window.addEventListener("message", (event) => { var _a; if (((_a = event.data) == null ? void 0 : _a.type) === "tina:editMode") { setEdit(true); } }); } }, []); return { edit }; } const tinaField = (object, property, index) => { const contentSource = object == null ? void 0 : object._content_source; if (!contentSource) { return ""; } const { queryId, path } = contentSource; if (!property) { return `${queryId}---${path.join(".")}`; } const fullPath = typeof index === "number" ? [...path, property, index] : [...path, property]; return `${queryId}---${fullPath.join(".")}`; }; const addMetadata = (id, obj, path = []) => { if (obj === null) { return obj; } if (isScalarOrUndefined(obj)) { return obj; } if (obj instanceof String) { return obj.valueOf(); } if (Array.isArray(obj)) { return obj.map( (item, index) => addMetadata(id, item, [...path, index]) ); } const transformedObj = {}; for (const [key, value] of Object.entries(obj)) { const currentPath = [...path, key]; if ([ "__typename", "_sys", "_internalSys", "_values", "_internalValues", "_content_source", "_tina_metadata" ].includes(key)) { transformedObj[key] = value; } else { transformedObj[key] = addMetadata(id, value, currentPath); } } if (transformedObj && typeof transformedObj === "object" && "type" in transformedObj && transformedObj.type === "root") { return transformedObj; } return { ...transformedObj, _content_source: { queryId: id, path } }; }; function isScalarOrUndefined(value) { const type = typeof value; if (type === "string") return true; if (type === "number") return true; if (type === "boolean") return true; if (type === "undefined") return true; if (value == null) return true; if (value instanceof String) return true; if (value instanceof Number) return true; if (value instanceof Boolean) return true; return false; } const hashFromQuery = (input) => { let hash = 0; for (let i = 0; i < input.length; i++) { const char = input.charCodeAt(i); hash = (hash << 5) - hash + char & 4294967295; } const nonNegativeHash = Math.abs(hash); const alphanumericHash = nonNegativeHash.toString(36); return alphanumericHash; }; export { addMetadata, hashFromQuery, tinaField, useEditState, useTina };