npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@lpti/widget-unpam-sso

v0.0.7

Published

SSO Widget component for UNPAM SSO - React dropdown widget with iframe integration

Downloads

4

Readme

Widget SSO

Embed the UNPAM SSO login widget as an iframe. Supports both vanilla JavaScript and React (JSX/TSX) usage. No dependencies required for vanilla JS. TypeScript types included for React.

Installation

npm install @lpti/widget-unpam-sso

Or using yarn:

yarn add @lpti/widget-unpam-sso

Usage

Vanilla JavaScript (Native, No Dependencies)

import { embedWidgetSSO } from '@lpti/widget-unpam-sso';

// Basic usage
embedWidgetSSO('sso-widget-container');

// With options
embedWidgetSSO('sso-widget-container', {
  env: 'prod',     // 'dev' or 'prod'
  lang: 'en',      // 'id' or 'en'
  size: 'lg',      // 'sm', 'md', or 'lg'
  theme: 'dark',   // 'light' or 'dark'
  logoUrl: '/path/to/logo.png',
  className: 'my-sso-widget'
});

HTML Script Tag (via CDN)

<script type="module">
  import { embedWidgetSSO } from 'https://unpkg.com/@lpti/widget-unpam-sso@latest/index.js';
  embedWidgetSSO('sso-widget-container', {
    env: 'prod',
    lang: 'id',
    size: 'md',
    theme: 'light'
  });
</script>
<div id="sso-widget-container"></div>

React Component (JSX/TSX)

import { WidgetSSO } from '@lpti/widget-unpam-sso/react';

function App() {
  return (
    <div className="app">
      {/* Basic usage */}
      <WidgetSSO />
      
      {/* With options */}
      <WidgetSSO 
        env="prod"
        lang="en"
        size="lg"
        theme="dark"
        logoUrl="/path/to/logo.png"
        className="my-sso-widget"
        onLoad={() => console.log('SSO widget loaded')}
        onError={error => console.error('SSO widget error:', error)}
      />
    </div>
  );
}

React Hook Usage

import { useWidgetSSO } from '@lpti/widget-unpam-sso/react';

function CustomSSOComponent() {
  const { iframeUrl, isLoading, hasError, reload } = useWidgetSSO({
    env: 'prod',
    lang: 'id',
    size: 'md',
    theme: 'light'
  });

  if (isLoading) return <div>Loading SSO widget...</div>;
  if (hasError) return <div>Error loading SSO widget. <button onClick={reload}>Retry</button></div>;

  return (
    <iframe
      src={iframeUrl}
      width="100%"
      height="40px"
      frameBorder="0"
      scrolling="no"
    />
  );
}

Vue 3 Integration

<template>
  <div>
    <div ref="widgetContainer" class="widget-container"></div>
  </div>
</template>

<script>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { WidgetSSO } from '@lpti/widget-unpam-sso/index';

export default {
  name: 'SsoWidget',
  props: {
    theme: { type: String, default: 'light' },
    size: { type: String, default: 'md' },
    lang: { type: String, default: 'id' },
    env: { type: String, default: 'dev' },
    logoUrl: { type: String, default: null }
  },
  setup(props, { emit }) {
    const widgetContainer = ref(null);
    let ssoWidget = null;
    
    onMounted(() => {
      if (widgetContainer.value) {
        ssoWidget = new WidgetSSO({
          theme: props.theme,
          size: props.size,
          lang: props.lang,
          env: props.env,
          logoUrl: props.logoUrl
        });
        
        ssoWidget.render(widgetContainer.value);
        
        // Set up iframe load event handler
        const iframe = widgetContainer.value.querySelector('iframe');
        if (iframe) {
          iframe.onload = () => emit('load');
          iframe.onerror = (error) => emit('error', error);
        }
      }
    });
    
    onBeforeUnmount(() => {
      if (ssoWidget) {
        ssoWidget.destroy();
      }
    });
    
    return {
      widgetContainer
    };
  }
}
</script>

Vue 2 Integration

<template>
  <div>
    <div ref="widgetContainer" class="widget-container"></div>
  </div>
</template>

<script>
import { WidgetSSO } from '@lpti/widget-unpam-sso/index';

export default {
  name: 'SsoWidget',
  props: {
    theme: { type: String, default: 'light' },
    size: { type: String, default: 'md' },
    lang: { type: String, default: 'id' },
    env: { type: String, default: 'dev' },
    logoUrl: { type: String, default: null }
  },
  data() {
    return {
      ssoWidget: null
    };
  },
  mounted() {
    if (this.$refs.widgetContainer) {
      this.ssoWidget = new WidgetSSO({
        theme: this.theme,
        size: this.size,
        lang: this.lang,
        env: this.env,
        logoUrl: this.logoUrl
      });
      
      this.ssoWidget.render(this.$refs.widgetContainer);
      
      // Set up iframe load event handler
      const iframe = this.$refs.widgetContainer.querySelector('iframe');
      if (iframe) {
        iframe.onload = () => this.$emit('load');
        iframe.onerror = (error) => this.$emit('error', error);
      }
    }
  },
  beforeDestroy() {
    if (this.ssoWidget) {
      this.ssoWidget.destroy();
    }
  }
}
</script>

Vue 3 with Composition API (Alternative)

// UseWidgetSSO.js - Composable
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { WidgetSSO } from '@lpti/widget-unpam-sso/index';

export function useWidgetSSO(options = {}) {
  const containerRef = ref(null);
  const isLoading = ref(true);
  const hasError = ref(false);
  let widget = null;

  const initWidget = () => {
    if (!containerRef.value) return;
    
    isLoading.value = true;
    hasError.value = false;
    
    try {
      widget = new WidgetSSO(options);
      widget.render(containerRef.value);
      
      const iframe = containerRef.value.querySelector('iframe');
      if (iframe) {
        iframe.onload = () => {
          isLoading.value = false;
        };
        iframe.onerror = () => {
          isLoading.value = false;
          hasError.value = true;
        };
      }
    } catch (error) {
      isLoading.value = false;
      hasError.value = true;
      console.error('Failed to initialize widget:', error);
    }
  };

  onMounted(() => {
    initWidget();
  });
  
  onBeforeUnmount(() => {
    if (widget) {
      widget.destroy();
    }
  });

  return {
    containerRef,
    isLoading,
    hasError,
    reload: initWidget
  };
}

API

Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | theme | 'light' \| 'dark' | 'light' | Widget color theme | | size | 'sm' \| 'md' \| 'lg' | 'md' | Widget size | | lang | 'id' \| 'en' | 'id' | Language code | | url | string | '' | Custom widget URL (overrides env) | | env | 'dev' \| 'prod' | 'dev' | Environment to use | | logoUrl | string \| null | null | URL to logo image | | className | string | '' | Custom class for the container | | buttonClassName | string | '' | Custom class for the button | | dropdownClassName | string | '' | Custom class for the dropdown | | style | object | {} | Custom styles for the container | | buttonStyle | object | {} | Custom styles for the button | | dropdownStyle | object | {} | Custom styles for the dropdown | | width | string | '100%' | CSS width of the widget | | height | string | 'auto' | CSS height of the widget |

Vanilla JavaScript

embedWidgetSSO(elementId, options)

Embeds the SSO widget into a target element.

Parameters:

  • elementId (string): The id of the target HTML element
  • options (object, optional): Configuration options (see above)

React Component

<WidgetSSO /> Props

All the same options as the vanilla JS version, plus:

  • onLoad (function): Callback when iframe loads successfully
  • onError (function): Callback when iframe fails to load

useWidgetSSO(options) Hook

Returns an object with:

  • iframeUrl (string): The generated iframe URL
  • isLoading (boolean): Whether the URL is being validated
  • hasError (boolean): Whether there was an error loading
  • reload (function): Function to retry loading the URL

Environments

  • Development: https://devsso.unpam.ac.id/widget
  • Production: https://satu.unpam.ac.id/widget

Features

  • ✅ No dependencies for vanilla JS
  • ✅ Native JavaScript ES6 modules
  • ✅ React component (JSX/TSX) and TypeScript support
  • ✅ React hooks for advanced usage
  • ✅ Vue 2 and Vue 3 integration
  • ✅ Multiple language support (Indonesian/English)
  • ✅ Theme support (light/dark)
  • ✅ Responsive sizing options
  • ✅ URL validation before iframe creation
  • ✅ Error handling and logging
  • ✅ Loading states for React components

Browser Support

The widget supports all modern browsers (Chrome, Firefox, Safari, Edge).

License

MIT