├── .gitignore ├── LICENSE.md ├── README.md ├── composer.json ├── config └── filament-copyable.php ├── copyable-text-field.PNG ├── dist └── filament-copyable.js ├── filament-copyable-column.PNG ├── mix-manifest.json ├── package.json ├── resources ├── js │ └── filament-copyable.js └── views │ ├── columns │ └── copyable-text-column.blade.php │ └── forms │ └── components │ └── copyable-text-input.blade.php ├── src ├── FilamentCopyableProvider.php ├── Forms │ └── Components │ │ └── CopyableTextInput.php └── Tables │ └── Columns │ └── CopyableTextColumn.php └── webpack.mix.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .php_cs 3 | .php_cs.cache 4 | .phpunit.result.cache 5 | build 6 | composer.lock 7 | coverage 8 | docs 9 | package-lock.json 10 | phpunit.xml 11 | phpstan.neon 12 | testbench.yaml 13 | vendor 14 | node_modules 15 | .php-cs-fixer.cache -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) Saad Nasir Siddique 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Filament Copyable 2 | 3 | Copyable Text Column and Field for Filament PHP. 4 | 5 |  6 |  7 | 8 | ## Installation 9 | 10 | You can install the package via composer: 11 | 12 | ```bash 13 | composer require saadj55/filament-copyable 14 | ``` 15 | ## Usage 16 | ### Column 17 | In in your Table Schema: 18 | 19 | ```php 20 | 21 | \Saadj55\FilamentCopyable\Tables\Columns\CopyableTextColumn::make('name') 22 | 23 | ``` 24 | You can make the icon to only appear on hover with the `->showOnHover()` method, 25 | 26 | You can set a custom [heroicon](https://heroicons.com/) by using the `->icon('heroicon-o-duplicate')` method. 27 | 28 | ### Field 29 | 30 | ```php 31 | 32 | \Saadj55\FilamentCopyable\Forms\Components\CopyableTextInput::make('name') 33 | 34 | ``` 35 | ## License 36 | 37 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 38 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "saadj55/filament-copyable", 3 | "description": "Filament fields and columns with copyable values", 4 | "autoload": { 5 | "psr-4": { 6 | "Saadj55\\FilamentCopyable\\": "src/" 7 | } 8 | }, 9 | "version": "0.1.4", 10 | "authors": [ 11 | { 12 | "name": "Saad Nasir", 13 | "email": "saadj55@gmail.com" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.0", 18 | "filament/filament": "^2.9", 19 | "spatie/laravel-package-tools": "^1.10" 20 | }, 21 | "minimum-stability": "dev", 22 | "config": { 23 | "sort-packages": true 24 | }, 25 | "extra": { 26 | "laravel": { 27 | "providers": [ 28 | "Saadj55\\FilamentCopyable\\FilamentCopyableProvider" 29 | ] 30 | } 31 | }, 32 | "prefer-stable": true 33 | } 34 | -------------------------------------------------------------------------------- /config/filament-copyable.php: -------------------------------------------------------------------------------- 1 | { // webpackBootstrap 2 | /******/ "use strict"; 3 | /******/ var __webpack_modules__ = ({ 4 | 5 | /***/ "./node_modules/@ryangjchandler/alpine-clipboard/src/index.js": 6 | /*!********************************************************************!*\ 7 | !*** ./node_modules/@ryangjchandler/alpine-clipboard/src/index.js ***! 8 | \********************************************************************/ 9 | /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { 10 | 11 | __webpack_require__.r(__webpack_exports__); 12 | /* harmony export */ __webpack_require__.d(__webpack_exports__, { 13 | /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__) 14 | /* harmony export */ }); 15 | let onCopy = () => {} 16 | 17 | const copy = (target) => { 18 | if (typeof target === 'function') { 19 | target = target() 20 | } 21 | 22 | if (typeof target === 'object') { 23 | target = JSON.stringify(target) 24 | } 25 | 26 | return window.navigator.clipboard.writeText(target) 27 | .then(onCopy) 28 | } 29 | 30 | function Clipboard(Alpine) { 31 | Alpine.magic('clipboard', () => { 32 | return copy 33 | }) 34 | 35 | Alpine.directive('clipboard', (el, { modifiers, expression }, { evaluateLater, cleanup }) => { 36 | const getCopyContent = modifiers.includes('raw') ? c => c(expression) : evaluateLater(expression) 37 | const clickHandler = () => getCopyContent(copy) 38 | 39 | el.addEventListener('click', clickHandler) 40 | 41 | cleanup(() => { 42 | el.removeEventListener('click', clickHandler) 43 | }) 44 | }) 45 | } 46 | 47 | Clipboard.configure = (config) => { 48 | if (config.hasOwnProperty('onCopy') && typeof config.onCopy === 'function') { 49 | onCopy = config.onCopy 50 | } 51 | 52 | return Clipboard 53 | } 54 | 55 | /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Clipboard); 56 | 57 | /***/ }) 58 | 59 | /******/ }); 60 | /************************************************************************/ 61 | /******/ // The module cache 62 | /******/ var __webpack_module_cache__ = {}; 63 | /******/ 64 | /******/ // The require function 65 | /******/ function __webpack_require__(moduleId) { 66 | /******/ // Check if module is in cache 67 | /******/ var cachedModule = __webpack_module_cache__[moduleId]; 68 | /******/ if (cachedModule !== undefined) { 69 | /******/ return cachedModule.exports; 70 | /******/ } 71 | /******/ // Create a new module (and put it into the cache) 72 | /******/ var module = __webpack_module_cache__[moduleId] = { 73 | /******/ // no module.id needed 74 | /******/ // no module.loaded needed 75 | /******/ exports: {} 76 | /******/ }; 77 | /******/ 78 | /******/ // Execute the module function 79 | /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); 80 | /******/ 81 | /******/ // Return the exports of the module 82 | /******/ return module.exports; 83 | /******/ } 84 | /******/ 85 | /************************************************************************/ 86 | /******/ /* webpack/runtime/define property getters */ 87 | /******/ (() => { 88 | /******/ // define getter functions for harmony exports 89 | /******/ __webpack_require__.d = (exports, definition) => { 90 | /******/ for(var key in definition) { 91 | /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { 92 | /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); 93 | /******/ } 94 | /******/ } 95 | /******/ }; 96 | /******/ })(); 97 | /******/ 98 | /******/ /* webpack/runtime/hasOwnProperty shorthand */ 99 | /******/ (() => { 100 | /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) 101 | /******/ })(); 102 | /******/ 103 | /******/ /* webpack/runtime/make namespace object */ 104 | /******/ (() => { 105 | /******/ // define __esModule on exports 106 | /******/ __webpack_require__.r = (exports) => { 107 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 108 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 109 | /******/ } 110 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 111 | /******/ }; 112 | /******/ })(); 113 | /******/ 114 | /************************************************************************/ 115 | var __webpack_exports__ = {}; 116 | // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. 117 | (() => { 118 | /*!*******************************************!*\ 119 | !*** ./resources/js/filament-copyable.js ***! 120 | \*******************************************/ 121 | __webpack_require__.r(__webpack_exports__); 122 | /* harmony import */ var _ryangjchandler_alpine_clipboard__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @ryangjchandler/alpine-clipboard */ "./node_modules/@ryangjchandler/alpine-clipboard/src/index.js"); 123 | 124 | document.addEventListener('alpine:init', function () { 125 | Alpine.plugin(_ryangjchandler_alpine_clipboard__WEBPACK_IMPORTED_MODULE_0__["default"]); 126 | }); 127 | })(); 128 | 129 | /******/ })() 130 | ; -------------------------------------------------------------------------------- /filament-copyable-column.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saadj55/filament-copyable/33b3c4b209bf9ae70eb971585545b0b4d7e6c02d/filament-copyable-column.PNG -------------------------------------------------------------------------------- /mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/dist/filament-copyable.js": "/dist/filament-copyable.js" 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "npm run development", 4 | "development": "mix", 5 | "watch": "mix watch", 6 | "watch-poll": "mix watch -- --watch-options-poll=1000", 7 | "hot": "mix watch --hot", 8 | "prod": "npm run production", 9 | "production": "mix --production" 10 | }, 11 | "devDependencies": { 12 | "autoprefixer": "^10.4.7", 13 | "laravel-mix": "^6.0.49", 14 | "mix-tailwindcss": "^1.3.0", 15 | "postcss": "^8.4.14", 16 | "postcss-import": "^14.1.0", 17 | "tailwindcss": "^3.1.4" 18 | }, 19 | "dependencies": { 20 | "@ryangjchandler/alpine-clipboard": "^2.2.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /resources/js/filament-copyable.js: -------------------------------------------------------------------------------- 1 | import Clipboard from "@ryangjchandler/alpine-clipboard" 2 | 3 | document.addEventListener('alpine:init', () => { 4 | Alpine.plugin(Clipboard); 5 | }) 6 | -------------------------------------------------------------------------------- /resources/views/columns/copyable-text-column.blade.php: -------------------------------------------------------------------------------- 1 |