Hear every voice in your language
실시간 AI 더빙과 양언어 자막을 제공, 무료·오픈소스·프라이버시 보장
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
The SkySlope Forms Widget is a tool that lets users embed SkySlope forms into their own web applications, providing easy access to SkySlope's real estate transaction management features. It is available as a JavaScript library and can be integrated into a variety of web applications.
Internally, the widget is built using StencilJS, a web component compiler. The widget injects an iframe into the DOM of the host application, which loads the SkySlope Forms application. The widget then communicates with the iframe to provide a seamless user experience.README 전문 보기 ↗The SkySlope Forms Widget is a tool that lets users embed SkySlope forms into their own web applications, providing easy access to SkySlope's real estate transaction management features. It is available as a JavaScript library and can be integrated into a variety of web applications.
Internally, the widget is built using StencilJS, a web component compiler. The widget injects an iframe into the DOM of the host application, which loads the SkySlope Forms application. The widget then communicates with the iframe to provide a seamless user experience.
Demo Site: https://skyslope-widget-demo.netlify.app/ Github: https://github.com/skyslope/skyslope-forms-widget/
Before starting: Please contact SkySlope Engineering to whitelist your site for framing. If using SSO, please contact SkySlope engineering to configure SSO for your company.
To implement the SkySlope Forms Widget using npm, follow these steps:
Install the package using npm:
npm i @skyslope/forms-widget
Or with Yarn:
yarn add @skyslope/forms-widget
In your index.js (or a similar entry file), import or require the @skyslope/forms-widget package and initialize the widget:
// ES6 import
import { defineCustomElements } from '@skyslope/forms-widget/loader/';
// CommonJS require
const { defineCustomElements } = require('@skyslope/forms-widget/loader/');
// Define all custom elements on the custom elements registry
defineCustomElements();
// Initialize the widget
const init = () =>
window.skyslope.widget.initialize({
idp: 'myIDP', // the idp for SSO, if using
});
window.skyslope?.widget ? init() : (window.skyslope = { onLoad: () => init() });
Web components from the @skyslope/forms-widget package can now be used in your application. For example:
<ss-button-create-listing id="create-listing-btn">Write a Listing</ss-button-create-listing>
To implement the SkySlope Forms Widget without usage of npm or yarn, add the following script tags to your index.html:
<script type="module" src="https://cdn.skyslope.com/skyslope-forms-widget/latest/skyslope-forms-widget/skyslope-forms-widget.esm.js"></script>
<script nomodule src="https://cdn.skyslope.com/skyslope-forms-widget/latest/esm/skyslope-forms-widgetcomponents.js"></script>
Then, in your app or in your index.html, add the following script:
<script>
const init = () => window.skyslope.widget.initialize({
idp: 'myIDP' // the idp for SSO, if using
})
window.skyslope?.widget ? init() : window.skyslope = { onLoad: () => init() };
</script>
Web components from the @skyslope/forms-widget package can now be used in your application. For example:
<ss-button-create-listing id="create-listing-btn">Write a Listing</ss-button-create-listing>
If you would like to use a specific version of the widget, you can specify the version in the script tag. For example:
<script type="module" src="https://cdn.skyslope.com/skyslope-forms-widget/{version-from-package.json}/skyslope-forms-widget/skyslope-forms-widget.esm.js"></script>
<script nomodule src="https://cdn.skyslope.com/skyslope-forms-widget/latest/{version-from-package.json}/skyslope-forms-widgetcomponents.js"></script>
If you are not using SSO, you can initialize the widget without an IDP:
<script>
const init = () => window.skyslope.widget.initialize();
window.skyslope?.widget ? init() : window.skyslope = { onLoad: () => init() };
</script>
A pre-made modal is available for use with the widget. To open the modal, call the openModal function:
window.skyslope.widget.openModal();
or with arguments, as shown below:
window.skyslope.widget.openModal({
open: true,
roundedEdges: true,
shouldConstrainMaxWidth: true,
showHeaderButtons: true,
showOverlay: true,
styleOverrides: {
modalWrapper: {
backgroundColor: 'black',
color: 'white',
},
modalHeader: {},
modalOverlay: {},
modalContent: {},
maxWidthContainer: {},
},
});
If you do not want to open a modal in your app and would instead like to customize the placement of the widget, follow the instructions below:
First, add the openInline: true option to the initialize() function, as shown below:
const init = () =>
window.skyslope.widget.initialize({
openInline: true,
});
This parameter will instruct the widget to avoid opening a modal and instead render the widget inline. You can also optionally pass the headerVariant: 'focused' parameter, which controls hiding navigation & settings elements in the header of any file-details page in Forms and the Build UI in DigiSign:
const init = () =>
window.skyslope.widget.initialize({
openInline: true,
headerVariant: 'focused',
});
To keep users inside the screen you embed, you can hide individual Forms elements and turn off the logo's link to the Forms home screen. Each option is off by default, so an embed that passes none of them is unchanged.
const init = () =>
window.skyslope.widget.initialize({
openInline: true,
headerVariant: 'focused',
hide: ['breadcrumb', 'add-forms'],
logoClickable: false,
});
| Option | Value | Effect |
|---|---|---|
hide | Array containing 'breadcrumb' | Hides the breadcrumb (back link) in the Forms header. |
hide | Array containing 'add-forms' | Hides the Add Forms button in the fill and template screens. |
logoClickable | false | Shows the SkySlope Forms logo without its link to the Forms home screen. |
Values of hide that are not in this table are ignored. The options last for the rest of the
session in the frame, including pages the user reaches from the one you opened.
Next, you will need to add the following ss-container-inline web component to your HTML or JSX:
<ss-container-inline style="height: 1000px; /*... add any other styling here*/" />
The widget will be rendered inside the ss-container-inline web component. The ss-container-inline web component can be styled as desired.
To interact programmatically with the ss-container-inline component, see API for custom implementations.
There are pre-made button web components that you can use to interact with the Forms widget:
<ss-button-create-listing>
<ss-button-write-offer>
<ss-button-start-buyer-agreement>
<ss-button-browse-libraries>
<ss-button-view-all-files>
<ss-button-create-listing> opens the "Create Listing" page when clicked.<ss-button-write-offer> opens the "Create Transaction" page when clicked.<ss-button-start-buyer-agreement> Starts a Buyer Agreement.<ss-button-browse-libraries> opens the "Browse Libraries" page when clicked.<ss-button-view-all-files> opens the "View All Files" page when clicked.To use these buttons, you can add them anywhere in your HTML like so:
<ss-button-create-listing id="create-listing-btn">Write a Listing</ss-button-create-listing>
<ss-button-write-offer id="write-offer-btn">Write an Offer</ss-button-write-offer>
<ss-button-start-buyer-agreement id="create-buyer-agreement">Buyer Agreement</ss-button-start-buyer-agreement>
<ss-button-browse-libraries id="browse-libraries-btn">Browse Libraries</ss-button-browse-libraries>
<ss-button-view-all-files id="view-all-files-btn">View All Files</ss-button-view-all-files>
You can also customize the way the modal is displayed by adding a custom function and assigning one of the three available props:
roundedEdges: falseshowOverlay: falseshowHeaderButtons: falsefunction openSquare() {
window.skyslope.widget.openModal({
roundedEdges: false,
});
}
For the fourth option, you can add all three and include an override for the modal style to position it manually:
window.skyslope.widget.openModal({
roundedEdges: false,
showHeaderButtons: false,
showOverlay: false,
styleOverrides: {
modalWrapper: {
top: '70px',
left: '300px',
right: 0,
bottom: 0,
zIndex: 9,
},
modalHeader: {},
modalOverlay: {},
modalContent: {
boxShadow: 'none',
margin: 0,
height: '100%',
width: 'calc(100%-300px)',
border: '1px solid #e1e7f1',
},
maxWidthContainer: {
maxWidth: '100%',
},
},
});
The unstyled prop is an optional prop that can be passed to the button web components. When this prop is set to true, the button will be rendered without any default styles applied. This allows developers to fully customize the appearance of the button using their own CSS styles.
For example, to create a custom styled ss-button-create-listing component, you could define the following CSS rules:
.custom-button {
background-color: blue;
color: white;
border-radius: 5px;
padding: 10px;
font-size: 16px;
}
.custom-button:hover {
background-color: darkblue;
}
Then, in your HTML, you can use the ss-button-create-listing component with the unstyled prop set to true and the class attribute set to custom-button:
<ss-button-create-listing id="create-listing-btn" unstyled="true" class="custom-button"> Write a Listing </ss-button-create-listing>
This will render a blue button with white text and rounded corners, with the custom styles applied. When the button is hovered over, it will turn dark blue.
Additionally, if you would like to skip the custom styling and would just like a quick all-in-one solution, we have a pre-made widget available to use.
To use the widget, you can add it anywhere in your HTML like so:
<ss-contained-widget class="contained-widget" />
This will result in the contained widget being rendered:
Some browsers block the cookies the embedded Forms app normally uses to sign the user in. Safari does this by default, as do Chrome incognito and Firefox strict mode. In these browsers the user sees a "third-party cookies are disabled" message instead of Forms.
To support them, pass a getToken callback that returns a SkySlope access token for the signed-in
user. It may return the token or a promise of it, and should return null when there is no
signed-in user. The widget calls it only when it needs a token, so browsers where the normal
sign-in works never call it.
window.skyslope.widget.initialize({
// Return a SkySlope access token for the currently signed-in user, or null.
getToken: async () => await myApp.getSkySlopeAccessToken(),
});
What getToken should do:
When you get a new token for the same user before the old one expires, you can call
await window.skyslope.widget.refreshToken() to hand it over right away. To sign out or switch
users, call clearToken() (see below).
No new Content-Security-Policy or Referrer-Policy settings are needed on your page.
The Forms session can outlast your own sign-out, even across a reload of your page. On sign-out or
a user switch, call await window.skyslope.widget.clearToken() while the widget is still mounted.
It ends the Forms session and reloads Forms without the old user.
Order matters: sign the user out of your app (or switch to the new user) first, so that
getToken returns null (or the new user's token) by the time you call clearToken(). If it
still returns the old user's token, that user is signed straight back in. After a sign-out the
widget raises an authError (iframe-auth-failed), which you can ignore.
async function signOut() {
await myApp.signOut(); // from here getToken returns null
await window.skyslope.widget.clearToken(); // the widget must still be mounted
myApp.showSignedOutPage();
}
initialize() before each openWhen the widget's container leaves the page (including when closeModal() closes the modal),
every initialize() setting, getToken included, is dropped. Call initialize() again before
each openModal() and each time you add ss-container-inline to the page, and always go through
window.skyslope.widget rather than a saved reference. Without this, users in browsers with
third-party cookies disabled cannot sign in on the second open while other browsers work, so test
a second open in Safari.
function openForms() {
window.skyslope.widget.initialize({ getToken: myApp.getSkySlopeAccessToken });
window.skyslope.widget.openModal();
}
authError eventWhen sign-in cannot be established or kept, ss-container-inline emits an authError event. It
bubbles out of ss-container-modal, so you can listen on either element (in typed JSX,
onAuthError is declared on ss-container-inline). event.detail.reason is one of:
token-callback-failed: getToken threw or did not answer within 30 seconds. The error is in
event.detail.error. If this happened while signing in, iframe-auth-failed follows.iframe-auth-failed: Forms or DigiSign could not sign the user in, for example because
getToken returned null or is not configured.token-renewal-failed: the widget could not get a new token to keep the session signed in. The
current session keeps working until it expires.signed-out: the user signed out inside Forms or DigiSign. The widget has stopped renewing; sign
the user out of your app or close the widget.document.querySelector('ss-container-modal')
.addEventListener('authError', event => {
console.warn('Forms widget auth failed:', event.detail.reason);
});
The SkySlope Forms Widget provides the following API on the window.skyslope.widget object.
All of these functions are used internally by the web-components and are exposed for use in custom implementations.
openModal opens a modal with SkySlope Forms loaded inside an iframe. This injects a modal web component into the body of the page.closeModal closes the modal.reload reloads the widgets internal iframe.refreshToken() renews a token session at once. See Support for browsers with third-party cookies disabled.clearToken() drops the token session on sign-out or a user switch. See Signing out or switching users.navigateTo(path: string) navigates to a different path within the Forms app.navigateToCreateTransaction navigates to the Create Transaction page. Returns newly created ID for data fetching. See section on Listening for Events.navigateToCreateListing navigates to the Create Listing page. Returns newly created ID for data fetching. See section on Listening for Events.navigateToStartBuyerAgreement: navigates to start buyer agreement page.navigateToBrowseLibraries navigates to the Browse Libraries page.navigateToViewAllFiles navigates to the View All Files page.navigateToEnvelope(envelopeId: integer) navigates to the Envelope Bulk Fill page.This query parameter can be applied to the following routes in each respective application:
https://forms.skyslope.com/file-details/{fileId}/{route}?headerVariant=focused
https://send.skyslope.com/envelopes/{envelopeId}?headerVariant=focused
Depending on the function selected, the SkySlope Forms Widget can post a message containing information relevant to further data querying. For example, after calling navigateToCreateTransaction:
interface SkySlopeMessage {
status?:
| 'forms-onboarding-complete'
| 'forms-user-ready'
| 'forms-edit-contacts'
| 'forms-download-document'
| 'forms-create-file'
| 'forms-bulk-fill'
| 'forms-buyer-agreement-rename'
| 'forms-prepare-signature'
| 'forms-bulk-fill-save-and-exit'
| 'digisign-prepare-for-signature'
| 'digisign-ready-to-send'
| 'digisign-envelope-sent';
metadata?: {
fileId?: number;
formsEnvelopeId?: number;
formsDocumentIds?: number[];
digisignEnvelopeId?: string;
};
}
// event.origin includes the scheme, so compare against full origins. Forms hands the user off to
// DigiSign inside the same frame, so DigiSign's messages come from the DigiSign origin.
const SKYSLOPE_ORIGINS = ['https://forms.skyslope.com', 'https://send.skyslope.com'];
window.addEventListener('message', event => {
// Ignore messages from other sites
if (!SKYSLOPE_ORIGINS.includes(event.origin)) return;
// Ignore windows that are not framed directly in this page, such as a tab or popup this page opened
const source = event.source;
if (!source || !('parent' in source) || source.parent !== window) return;
// Widget messages are JSON strings. Forms also sends some plain-text messages meant for other
// SkySlope apps; skip those.
if (typeof event.data !== 'string') return;
let data;
try {
data = JSON.parse(event.data);
} catch {
return;
}
const { status, metadata } = data ?? {};
if (status === 'forms-create-file') {
console.log(metadata?.fileId); // ID of the newly created transaction or listing, eg. 123
}
// See "Knowing when the user is done" below.
if (status === 'forms-bulk-fill-save-and-exit' || status === 'digisign-envelope-sent') {
closeWidget(); // your code
refreshFromSkySlope(); // your code, for example re-read the file from the SkySlope API
}
});
Two statuses tell you the user has finished in the embedded screens, so you can close the widget and refresh your own data:
| Status | Sent when | metadata |
|---|---|---|
forms-bulk-fill-save-and-exit | The user clicks Save & Exit in the fill screen and the save finishes. Not sent if they cancel the "Save as draft" dialog. | fileId, plus formsEnvelopeId when the draft envelope was kept |
digisign-envelope-sent | The user clicks I'm Done after sending an envelope started from Forms. | digisignEnvelopeId |
forms-bulk-fill-save-and-exit comes from the Forms origin and digisign-envelope-sent from the
DigiSign origin, which is why the listener above accepts both.
Treat the IDs in metadata as references to look up through the SkySlope API, not as proof that
the user is allowed to see them.
Ignore any status you don't handle. The widget uses some messages for its own sign-in, and new statuses may be added.
The listener above uses the production origins. For testing in other SkySlope environments, use
https://integ-forms.skyslope.com and https://integ-send.skyslope.com, or
https://staging-forms.skyslope.com and https://staging-send.skyslope.com.